userpages + filter improvments
This commit is contained in:
@@ -1 +1,2 @@
|
||||
<ui-entries [entries]="entries" [refresh]="boundRefresh" [update]="boundUpdate" [gravityFilter]="gravityFilter"></ui-entries>
|
||||
<ui-entries [entries]="entries" [refresh]="boundRefresh" [update]="boundUpdate" [filter]="filter"
|
||||
[gravityFilter]="gravityFilter"></ui-entries>
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
|
||||
import { SettingsService } from '../../services/settings.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'page-entries',
|
||||
templateUrl: './entries.page.html'
|
||||
})
|
||||
export class PageEntries implements OnInit {
|
||||
export class PageEntries implements OnInit, OnDestroy {
|
||||
|
||||
settings: any;
|
||||
@Input() fetch: Function;
|
||||
@Input() filter: boolean = true;
|
||||
@Input() gravityFilter: boolean = false;
|
||||
entries: any;
|
||||
asc: boolean = false;
|
||||
@@ -20,6 +21,9 @@ export class PageEntries implements OnInit {
|
||||
init: boolean = true;
|
||||
filterOpen: boolean = false;
|
||||
|
||||
settings: any;
|
||||
settingsSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private settingsService: SettingsService, private router: Router, private route: ActivatedRoute) { }
|
||||
|
||||
@@ -50,7 +54,7 @@ export class PageEntries implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
this.settingsService.settings.subscribe((settings) => {
|
||||
this.settingsSubscription = this.settingsService.settings.subscribe((settings) => {
|
||||
this.settings = settings;
|
||||
this.refresh();
|
||||
this.init = false;
|
||||
@@ -59,6 +63,10 @@ export class PageEntries implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.settingsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
if (!this.entries) {
|
||||
this.entries = {};
|
||||
@@ -69,7 +77,9 @@ export class PageEntries implements OnInit {
|
||||
this.fetch(this.entries.number || 0, this.entries.size || this.settings.pageSize, this.asc, this.entries.filter).subscribe((data: any) => {
|
||||
this.entries = data;
|
||||
this.entries.filter = filter;
|
||||
}, (error) => { })
|
||||
}, (error) => {
|
||||
this.entries = { error: error };
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<div class="container">
|
||||
<mat-progress-bar *ngIf="!entry" mode="indeterminate"></mat-progress-bar>
|
||||
<form [formGroup]="form" (ngSubmit)="update()" #formDirective="ngForm" *ngIf="entry">
|
||||
<form [formGroup]="form" (ngSubmit)="entry.id ? update() : create()" #formDirective="ngForm" *ngIf="entry && settings">
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<p>{{'submission.edit' | i18n}}</p>
|
||||
<p>{{ (entry.id ? 'submission.edit' : 'submission.info') | i18n}}</p>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{'submission.entryType' | i18n}}" formControlName="entryType" disabled>
|
||||
<mat-select placeholder="{{'submission.entryType' | i18n}}" formControlName="entryType">
|
||||
<mat-select-trigger>
|
||||
<mat-icon>{{'entryType.' + entryType + '.icon' | i18n}}</mat-icon> {{'entryType.' + entryType | i18n}}
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let entryType of entryTypes" [value]="entryType">
|
||||
<mat-icon>{{'entryType.' + entryType + '.icon' | i18n}}</mat-icon> {{'entryType.' + entryType | i18n}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
@@ -36,7 +43,10 @@
|
||||
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||
<button *ngIf="!working && !entry.id" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||
{{'submission.create' | i18n}}
|
||||
</button>
|
||||
<button *ngIf="!working && entry.id" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||
{{'submission.update' | i18n}}
|
||||
</button>
|
||||
<a *ngIf="success" mat-button color="primary" routerLink="/e/{{entry.id}}">{{'submission.success' | i18n}}</a>
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { EntriesService } from '../../../services/entries.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FormBuilder, FormGroup, Validators, NgForm } from '@angular/forms';
|
||||
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { distinctUntilChanged, debounceTime } from 'rxjs/operators';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { TagsService } from 'src/app/services/tags.service';
|
||||
import { SettingsService } from 'src/app/services/settings.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'page-entry-edit',
|
||||
templateUrl: './edit.page.html',
|
||||
styleUrls: [ './edit.page.scss' ]
|
||||
})
|
||||
export class PageEntryEdit implements OnInit {
|
||||
export class PageEntryEdit implements OnInit, OnDestroy {
|
||||
|
||||
id: number;
|
||||
entry: any;
|
||||
@@ -25,13 +24,14 @@ export class PageEntryEdit implements OnInit {
|
||||
success: boolean = false;
|
||||
form: FormGroup;
|
||||
settings: any;
|
||||
readonly tagsSeparatorKeysCodes = [ ENTER, COMMA, SPACE ] as const;
|
||||
@ViewChild('formDirective') private formDirective: NgForm;
|
||||
|
||||
settingsSubscription: Subscription;
|
||||
|
||||
constructor(private entriesService: EntriesService,
|
||||
private tagsService: TagsService,
|
||||
private settingsService: SettingsService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private snackBar: MatSnackBar) { }
|
||||
|
||||
@@ -43,11 +43,11 @@ export class PageEntryEdit implements OnInit {
|
||||
text: [ '', Validators.nullValidator ],
|
||||
});
|
||||
|
||||
this.settingsService.settings.subscribe((settings) => {
|
||||
this.settingsSubscription = this.settingsService.settings.subscribe((settings) => {
|
||||
this.settings = settings;
|
||||
});
|
||||
|
||||
this.form.get('entryType').disable();
|
||||
this.form.get('entryType').setValue(this.entryType);
|
||||
|
||||
this.form.get('entryType').valueChanges.subscribe((value) => {
|
||||
this.entryType = value;
|
||||
@@ -63,6 +63,7 @@ export class PageEntryEdit implements OnInit {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.form.get('url').valueChanges.pipe(
|
||||
debounceTime(800),
|
||||
distinctUntilChanged()).subscribe((value) => {
|
||||
@@ -73,28 +74,38 @@ export class PageEntryEdit implements OnInit {
|
||||
}
|
||||
})
|
||||
|
||||
this.id = +this.route.snapshot.paramMap.get('id');
|
||||
this.id = this.route.snapshot.paramMap.get('id') && +this.route.snapshot.paramMap.get('id');
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.settingsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.entriesService.getEntry(this.id).subscribe((data) => {
|
||||
this.entry = data;
|
||||
this.entryType = this.entry.entryType;
|
||||
this.form.get("entryType").setValue(this.entry.entryType);
|
||||
this.form.get("url").setValue(this.entry.url);
|
||||
this.form.get("title").setValue(this.entry.title);
|
||||
this.form.get("text").setValue(this.entry.text);
|
||||
if (!this.entry.metadata.edit) {
|
||||
this.form.get("url").disable();
|
||||
this.form.get("title").disable();
|
||||
this.form.get("text").disable();
|
||||
}
|
||||
}, (error) => {
|
||||
if (error.status == 404) {
|
||||
this.notfound = true;
|
||||
}
|
||||
})
|
||||
if (this.id) {
|
||||
this.form.get('entryType').disable();
|
||||
this.entriesService.getEntry(this.id).subscribe((data) => {
|
||||
this.entry = data;
|
||||
this.entryType = this.entry.entryType;
|
||||
this.form.get("entryType").setValue(this.entry.entryType);
|
||||
this.form.get("url").setValue(this.entry.url);
|
||||
this.form.get("title").setValue(this.entry.title);
|
||||
this.form.get("text").setValue(this.entry.text);
|
||||
if (!this.entry.metadata.edit) {
|
||||
this.form.get("url").disable();
|
||||
this.form.get("title").disable();
|
||||
this.form.get("text").disable();
|
||||
}
|
||||
}, (error) => {
|
||||
if (error.status == 404) {
|
||||
this.notfound = true;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.entry = {};
|
||||
this.entry.entryType = this.entryType;
|
||||
}
|
||||
}
|
||||
|
||||
hasError(controlName: string): boolean {
|
||||
@@ -105,33 +116,43 @@ export class PageEntryEdit implements OnInit {
|
||||
|
||||
}
|
||||
|
||||
addTag(event: MatChipInputEvent): void {
|
||||
let value = (event.value || "").trim();
|
||||
if (value.startsWith('#')) {
|
||||
value = value.replace('#', '');
|
||||
}
|
||||
value = value.split('#').join('-');
|
||||
if (value) {
|
||||
this.entry.tags.push(value);
|
||||
}
|
||||
event.chipInput!.clear();
|
||||
}
|
||||
|
||||
removeTag(tag: string): void {
|
||||
const index = this.entry.tags.indexOf(tag);
|
||||
if (index >= 0) {
|
||||
this.entry.tags.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
update(): void {
|
||||
|
||||
create(): void {
|
||||
if (this.working) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.working = true;
|
||||
|
||||
this.entry.url = this.form.get("url").value;
|
||||
this.entry.entryType = this.entryType;
|
||||
this.entry.title = this.form.get("title").value;
|
||||
this.entry.text = this.form.get("text").value;
|
||||
|
||||
this.entriesService.create(this.entry).subscribe((data) => {
|
||||
this.router.navigateByUrl('/');
|
||||
}, (error) => {
|
||||
this.working = false;
|
||||
if (error.status == 422) {
|
||||
let errors = {};
|
||||
for (let code of error.error) {
|
||||
errors[ code.field ] = errors[ code.field ] || {};
|
||||
errors[ code.field ][ code.code ] = true;
|
||||
}
|
||||
|
||||
for (let code in errors) {
|
||||
this.form.get(code).setErrors(errors[ code ]);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
update(): void {
|
||||
if (this.working) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.working = true;
|
||||
if (this.entry.metadata.edit) {
|
||||
this.entry.url = this.form.get("url").value;
|
||||
this.entry.title = this.form.get("title").value;
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
<mat-divider></mat-divider>
|
||||
<p>{{'settings.pagesettings' | i18n}}</p>
|
||||
<mat-form-field>
|
||||
<button *ngIf="user.settings.gravity || form.get('gravity').value != settings.defaultGravity" matPrefix
|
||||
<button matTooltip="{{'settings.gravity.reset' | i18n:settings.defaultGravity}}"
|
||||
*ngIf="user.settings.gravity || form.get('gravity').value != settings.defaultGravity" matPrefix
|
||||
mat-icon-button (click)="resetGravity()">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
@@ -33,9 +34,9 @@
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<button
|
||||
*ngIf="user.settings.entryDelay || form.get('entryDelay').value != settings.defaultEntryDelay"
|
||||
matPrefix mat-icon-button (click)="resetEntryDelay()">
|
||||
<button matTooltip="{{'settings.entryDelay.reset' | i18n:settings.defaultEntryDelay}}"
|
||||
*ngIf="user.settings.entryDelay || form.get('entryDelay').value != settings.defaultEntryDelay" matPrefix
|
||||
mat-icon-button (click)="resetEntryDelay()">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
<input type="number" min="0" max="15" step="1" matInput placeholder="{{'settings.entryDelay' | i18n}}"
|
||||
@@ -48,7 +49,7 @@
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<button
|
||||
<button matTooltip="{{'settings.commentDelay.reset' | i18n:settings.defaultCommentDelay}}"
|
||||
*ngIf="user.settings.commentDelay || form.get('commentDelay').value != settings.defaultCommentDelay"
|
||||
matPrefix mat-icon-button (click)="resetCommentDelay()">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
@@ -62,6 +63,19 @@
|
||||
{{'settings.commentDelay.zero' | i18n}}
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<button matTooltip="{{'settings.pageSize.reset' | i18n:settings.defaultPageSize}}"
|
||||
*ngIf="user.settings.pageSize || form.get('pageSize').value != settings.defaultPageSize" matPrefix
|
||||
mat-icon-button (click)="resetPageSize()">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
<input type="number" min="1" max="100" step="1" matInput placeholder="{{'settings.pageSize' | i18n}}"
|
||||
formControlName="pageSize">
|
||||
<mat-hint *ngIf="form.get('pageSize').value != 0">
|
||||
{{'settings.pageSize.hint' | i18n}}
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators, NgForm } from '@angular/forms';
|
||||
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { SettingsService } from 'src/app/services/settings.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'page-settings',
|
||||
templateUrl: './settings.page.html',
|
||||
styleUrls: [ './settings.page.scss' ]
|
||||
})
|
||||
export class PageSettings implements OnInit {
|
||||
export class PageSettings implements OnInit, OnDestroy {
|
||||
|
||||
auth: any;
|
||||
user: any;
|
||||
@@ -18,6 +19,7 @@ export class PageSettings implements OnInit {
|
||||
form: FormGroup;
|
||||
settings: any;
|
||||
@ViewChild('formDirective') private formDirective: NgForm;
|
||||
settingsSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private userService: UserService,
|
||||
@@ -32,6 +34,7 @@ export class PageSettings implements OnInit {
|
||||
gravity: [ '', Validators.nullValidator ],
|
||||
entryDelay: [ '', Validators.nullValidator ],
|
||||
commentDelay: [ '', Validators.nullValidator ],
|
||||
pageSize: [ '', Validators.nullValidator ],
|
||||
});
|
||||
|
||||
this.form.get('username').disable();
|
||||
@@ -45,15 +48,18 @@ export class PageSettings implements OnInit {
|
||||
this.form.get('email').setValue(this.user.email);
|
||||
this.form.get('about').setValue(this.user.about);
|
||||
|
||||
this.settingsService.settings.subscribe((settings) => {
|
||||
this.settingsSubscription = this.settingsService.settings.subscribe((settings) => {
|
||||
this.settings = settings;
|
||||
this.form.get('gravity').setValue(this.user.settings.gravity || this.settings.defaultGravity);
|
||||
this.form.get('entryDelay').setValue(this.user.settings.entryDelay || this.settings.defaultEntryDelay);
|
||||
this.form.get('commentDelay').setValue(this.user.settings.commentDelay || this.settings.defaultCommentDelay);
|
||||
this.form.get('pageSize').setValue(this.user.settings.pageSize || this.settings.defaultPageSize);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.settingsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
hasError(controlName: string): boolean {
|
||||
@@ -75,6 +81,11 @@ export class PageSettings implements OnInit {
|
||||
this.form.get('commentDelay').setValue(this.settings.defaultCommentDelay);
|
||||
}
|
||||
|
||||
resetPageSize(): void {
|
||||
this.user.settings.pageSize = null;
|
||||
this.form.get('pageSize').setValue(this.settings.defaultPageSize);
|
||||
}
|
||||
|
||||
save(): void {
|
||||
if (this.working) {
|
||||
return;
|
||||
@@ -108,6 +119,12 @@ export class PageSettings implements OnInit {
|
||||
this.user.settings.commentDelay = this.form.get('commentDelay').value;
|
||||
}
|
||||
|
||||
if (this.form.get('pageSize').value != this.settings.defaultPageSize && !this.user.settings.pageSize) {
|
||||
this.user.settings.pageSize = this.form.get('pageSize').value;
|
||||
} else if (this.user.settings.pageSize) {
|
||||
this.user.settings.pageSize = this.form.get('pageSize').value;
|
||||
}
|
||||
|
||||
this.userService.update(this.user).subscribe((data) => {
|
||||
this.user = data;
|
||||
if (!this.user.settings) {
|
||||
@@ -115,6 +132,7 @@ export class PageSettings implements OnInit {
|
||||
}
|
||||
this.working = false;
|
||||
this.success = true;
|
||||
this.settingsService.getSettings();
|
||||
}, (error) => {
|
||||
this.working = false;
|
||||
if (error.status == 422) {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<div class="container">
|
||||
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<p>{{'submission.info' | i18n}}</p>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="{{'submission.entryType' | i18n}}" formControlName="entryType">
|
||||
<mat-select-trigger>
|
||||
<mat-icon>{{'entryType.' + entryType + '.icon' | i18n}}</mat-icon> {{'entryType.' + entryType | i18n}}
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let entryType of entryTypes" [value]="entryType">
|
||||
<mat-icon>{{'entryType.' + entryType + '.icon' | i18n}}</mat-icon> {{'entryType.' + entryType | i18n}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{'submission.url' | i18n}}" formControlName="url" type="url"
|
||||
[required]="entryType == 'LINK'" matAutofocus>
|
||||
<mat-error *ngIf="hasError('url')">
|
||||
{{'submission.url.error' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{'submission.title' | i18n}}" formControlName="title" type="text" required
|
||||
(focus)="onTitleFocus($event)">
|
||||
<mat-error>
|
||||
{{'submission.title.error' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<textarea [mat-autosize] [matAutosizeMinRows]="3" matInput placeholder="{{'submission.text' | i18n}}"
|
||||
[required]="entryType != 'LINK'" formControlName="text"></textarea>
|
||||
<mat-error>
|
||||
{{'submission.text.error' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<ui-tagspicker [(model)]="tags" placeholder="{{'submission.tags' | i18n}}" [max]="settings.maxTags">
|
||||
</ui-tagspicker>
|
||||
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||
{{'submission.create' | i18n}}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,131 +0,0 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { EntriesService } from '../../services/entries.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { FormBuilder, FormGroup, Validators, NgForm } from '@angular/forms';
|
||||
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { distinctUntilChanged, debounceTime } from 'rxjs/operators';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { SettingsService } from 'src/app/services/settings.service';
|
||||
|
||||
@Component({
|
||||
selector: 'page-submission',
|
||||
templateUrl: './submission.page.html',
|
||||
styleUrls: [ './submission.page.scss' ]
|
||||
})
|
||||
export class PageSubmission implements OnInit {
|
||||
|
||||
entryTypes: string[] = [ 'LINK', 'DISCUSSION', 'QUESTION', 'INTERN' ];
|
||||
entryType: string = this.entryTypes[ 0 ];
|
||||
working: boolean = false;
|
||||
form: FormGroup;
|
||||
readonly tagsSeparatorKeysCodes = [ ENTER, COMMA, SPACE ] as const;
|
||||
tags: string[] = [];
|
||||
settings: any;
|
||||
@ViewChild('formDirective') private formDirective: NgForm;
|
||||
|
||||
constructor(private entriesService: EntriesService,
|
||||
private settingsService: SettingsService,
|
||||
private router: Router,
|
||||
private formBuilder: FormBuilder) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.form = this.formBuilder.group({
|
||||
entryType: [ '', Validators.required ],
|
||||
url: [ '', Validators.required ],
|
||||
title: [ '', Validators.required ],
|
||||
text: [ '', Validators.nullValidator ],
|
||||
});
|
||||
|
||||
this.settingsService.settings.subscribe((settings) => {
|
||||
this.settings = settings;
|
||||
});
|
||||
|
||||
this.form.get('entryType').setValue(this.entryType);
|
||||
|
||||
this.form.get('entryType').valueChanges.subscribe((value) => {
|
||||
this.entryType = value;
|
||||
switch (value) {
|
||||
case 'LINK':
|
||||
this.form.get('url').setValidators([ Validators.required ]);
|
||||
this.form.get('text').setValidators([ Validators.nullValidator ]);
|
||||
break;
|
||||
default:
|
||||
this.form.get('url').setValidators([ Validators.nullValidator ]);
|
||||
this.form.get('text').setValidators([ Validators.required ]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
this.form.get('url').valueChanges.pipe(
|
||||
debounceTime(800),
|
||||
distinctUntilChanged()).subscribe((value) => {
|
||||
if (value && !this.form.get('title').value) {
|
||||
this.entriesService.titleHelper(value).subscribe((title: string) => {
|
||||
this.form.get('title').setValue(title);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
hasError(controlName: string): boolean {
|
||||
return this.form.controls[ controlName ].errors != null;
|
||||
}
|
||||
|
||||
onTitleFocus(event): void {
|
||||
|
||||
}
|
||||
|
||||
addTag(event: MatChipInputEvent): void {
|
||||
let value = (event.value || "").trim();
|
||||
if (value.startsWith('#')) {
|
||||
value = value.replace('#', '');
|
||||
}
|
||||
value = value.split('#').join('-');
|
||||
if (value) {
|
||||
this.tags.push(value);
|
||||
}
|
||||
event.chipInput!.clear();
|
||||
}
|
||||
|
||||
removeTag(tag: string): void {
|
||||
const index = this.tags.indexOf(tag);
|
||||
if (index >= 0) {
|
||||
this.tags.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
create(): void {
|
||||
|
||||
if (this.working) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.working = true;
|
||||
|
||||
const entry: any = {};
|
||||
entry.url = this.form.get("url").value;
|
||||
entry.entryType = this.entryType;
|
||||
entry.title = this.form.get("title").value;
|
||||
entry.text = this.form.get("text").value;
|
||||
entry.tags = this.tags;
|
||||
|
||||
this.entriesService.create(entry).subscribe((data) => {
|
||||
this.router.navigateByUrl('/');
|
||||
}, (error) => {
|
||||
this.working = false;
|
||||
if (error.status == 422) {
|
||||
let errors = {};
|
||||
for (let code of error.error) {
|
||||
errors[ code.field ] = errors[ code.field ] || {};
|
||||
errors[ code.field ][ code.code ] = true;
|
||||
}
|
||||
|
||||
for (let code in errors) {
|
||||
this.form.get(code).setErrors(errors[ code ]);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
|
||||
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { CommentService } from '../../../services/comment.service';
|
||||
import { SettingsService } from '../../../services/settings.service';
|
||||
@@ -10,19 +11,20 @@ import { SettingsService } from '../../../services/settings.service';
|
||||
templateUrl: './usercomments.page.html',
|
||||
styleUrls: [ './usercomments.page.scss' ]
|
||||
})
|
||||
export class PageUserComments implements OnInit {
|
||||
export class PageUserComments implements OnInit, OnDestroy {
|
||||
|
||||
settings: any;
|
||||
username: string;
|
||||
comments: any = {};
|
||||
init: boolean = true;
|
||||
ignore: string[] = [ "author" ];
|
||||
settingsSubscription: Subscription;
|
||||
|
||||
constructor(private settingsService: SettingsService, private commentService: CommentService, private router: Router, private route: ActivatedRoute) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.username = this.route.snapshot.paramMap.get('username');
|
||||
this.settingsService.settings.subscribe((settings) => {
|
||||
this.settingsSubscription = this.settingsService.settings.subscribe((settings) => {
|
||||
this.settings = settings;
|
||||
this.commentService.getByUser(this.username, this.comments.number || 0, this.comments.size || this.settings.pageSize, this.ignore).subscribe((data: any) => {
|
||||
this.comments = data;
|
||||
@@ -30,6 +32,10 @@ export class PageUserComments implements OnInit {
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.settingsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
showMore() {
|
||||
const oldContent: any[] = this.comments.content;
|
||||
this.commentService.getByUser(this.username, this.comments.number + 1, this.comments.size, this.ignore).subscribe((data) => {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<div class="container">
|
||||
<mat-progress-bar *ngIf="!userpage" mode="indeterminate"></mat-progress-bar>
|
||||
<form [formGroup]="form" (ngSubmit)="save()" #formDirective="ngForm" *ngIf="userpage && settings">
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<p>{{ (userpage.id ? 'userpages.edit' : 'userpages.create') | i18n}}</p>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{'userpages.name' | i18n}}" formControlName="name" matAutofocus>
|
||||
<mat-error *ngIf="hasError('name')">
|
||||
<div *ngFor="let error of form.get('name').errors | keyvalue">
|
||||
{{'userpages.name.error.' + error.key | i18n}}<br>
|
||||
</div>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="{{'userpages.sorting' | i18n}}" formControlName="sorting">
|
||||
<mat-select-trigger>
|
||||
<mat-icon>{{'sorting.' + sorting + '.icon' | i18n}}</mat-icon> {{'sorting.' + sorting | i18n}}
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let sorting of sortings" [value]="sorting">
|
||||
<mat-icon>{{'sorting.' + sorting + '.icon' | i18n}}</mat-icon> {{'sorting.' + sorting | i18n}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<ui-tagspicker [(model)]="userpage.tags" placeholder="{{'userpages.tags' | i18n}}">
|
||||
</ui-tagspicker>
|
||||
|
||||
<ui-tagspicker [(model)]="userpage.excludedTags" placeholder="{{'userpages.excludedTags' | i18n}}">
|
||||
</ui-tagspicker>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="{{'userpages.entryType' | i18n}}" formControlName="entryType">
|
||||
<mat-select-trigger>
|
||||
<span *ngIf="entryType">
|
||||
<mat-icon>{{'entryType.' + entryType + '.icon' | i18n}}</mat-icon> {{'entryType.' +
|
||||
entryType | i18n}}
|
||||
</span>
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let entryType of entryTypes" [value]="entryType">
|
||||
<span *ngIf="entryType">
|
||||
<mat-icon>{{'entryType.' + entryType + '.icon' | i18n}}</mat-icon> {{'entryType.' + entryType | i18n}}
|
||||
</span>
|
||||
<span *ngIf="!entryType">
|
||||
{{'entryType.empty' | i18n}}
|
||||
</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
</mat-card-content>
|
||||
<mat-card-actions fxLayout="row wrap" fxFlexFill>
|
||||
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||
<mat-icon>save</mat-icon> {{ (userpage.id ? 'userpages.update' : 'userpages.create') | i18n}}
|
||||
</button>
|
||||
<a mat-button color="primary" routerLink="/p/{{userpage.name}}" *ngIf="userpage.id">{{ 'userpages.goTo' |
|
||||
i18n}}</a>
|
||||
<span fxFlexOffset="auto" fxFlexOffset.xs="none"></span>
|
||||
<a mat-raised-button color="warn" *ngIf="!working && userpage.id" (click)="deleteUserPage()"><mat-icon>delete</mat-icon> {{
|
||||
'userpages.delete' |
|
||||
i18n}}</a>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</form>
|
||||
</div>
|
||||
+7
-3
@@ -2,9 +2,9 @@ mat-form-field {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ui-tagspicker {
|
||||
display: block;
|
||||
width: 100%;
|
||||
mat-chip mat-icon.mat-icon-inline {
|
||||
margin-top: -12px;
|
||||
margin-right: -2px;
|
||||
}
|
||||
|
||||
form {
|
||||
@@ -23,3 +23,7 @@ form {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
mat-card-actions > * {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FormBuilder, FormGroup, Validators, NgForm } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TagsService } from 'src/app/services/tags.service';
|
||||
import { SettingsService } from 'src/app/services/settings.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { UserPageService } from 'src/app/services/userpage.service';
|
||||
import { I18nService } from 'src/app/services/i18n.service';
|
||||
import { ConfirmDialog } from 'src/app/ui/confirm/confirm.component';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'page-userpage-edit',
|
||||
templateUrl: './edit.page.html',
|
||||
styleUrls: [ './edit.page.scss' ]
|
||||
})
|
||||
export class PageUserPageEdit implements OnInit, OnDestroy {
|
||||
|
||||
name: string;
|
||||
userpage: any;
|
||||
entryTypes: string[] = [ undefined, 'LINK', 'DISCUSSION', 'QUESTION', 'INTERN' ];
|
||||
entryType: string = this.entryTypes[ 0 ];
|
||||
sortings: string[] = [ 'NEW', 'TOP', 'HOT', 'LAST' ];
|
||||
sorting: string = this.sortings[ 0 ];
|
||||
notfound: boolean = false;
|
||||
working: boolean = false;
|
||||
form: FormGroup;
|
||||
settings: any;
|
||||
|
||||
settingsSubscription: Subscription;
|
||||
|
||||
constructor(private userPageService: UserPageService,
|
||||
private tagsService: TagsService,
|
||||
private settingsService: SettingsService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private i18n: I18nService,
|
||||
private route: ActivatedRoute,
|
||||
private snackBar: MatSnackBar,
|
||||
public dialog: MatDialog) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.form = this.formBuilder.group({
|
||||
name: [ '', Validators.required ],
|
||||
entryType: [ '', Validators.nullValidator ],
|
||||
sorting: [ '', Validators.required ],
|
||||
});
|
||||
|
||||
this.settingsSubscription = this.settingsService.settings.subscribe((settings) => {
|
||||
this.settings = settings;
|
||||
});
|
||||
|
||||
this.form.get('entryType').setValue(this.entryType);
|
||||
this.form.get('entryType').valueChanges.subscribe((value) => {
|
||||
this.entryType = value;
|
||||
});
|
||||
this.form.get('sorting').setValue(this.sorting);
|
||||
this.form.get('sorting').valueChanges.subscribe((value) => {
|
||||
this.sorting = value;
|
||||
});
|
||||
|
||||
this.name = this.route.snapshot.paramMap.get('name');
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.settingsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
if (this.name) {
|
||||
this.userPageService.getUserPage(this.name).subscribe((data) => {
|
||||
this.userpage = data;
|
||||
this.entryType = this.userpage.entryType;
|
||||
this.sorting = this.userpage.sorting;
|
||||
this.form.get("name").setValue(this.userpage.name);
|
||||
this.form.get("entryType").setValue(this.userpage.entryType);
|
||||
this.form.get("sorting").setValue(this.userpage.sorting);
|
||||
}, (error) => {
|
||||
if (error.status == 404) {
|
||||
this.notfound = true;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.userpage = {};
|
||||
this.userpage.entryType = this.entryType;
|
||||
this.userpage.sorting = this.sorting;
|
||||
this.userpage.tags = [];
|
||||
this.userpage.excludedTags = [];
|
||||
}
|
||||
}
|
||||
|
||||
hasError(controlName: string): boolean {
|
||||
return this.form.controls[ controlName ].errors != null;
|
||||
}
|
||||
|
||||
|
||||
save(): void {
|
||||
if (this.working) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.working = true;
|
||||
|
||||
this.userpage.name = this.form.get("name").value;
|
||||
this.userpage.entryType = this.entryType;
|
||||
this.userpage.sorting = this.sorting;
|
||||
|
||||
this.userPageService.createOrUpdate(this.userpage).subscribe((data) => {
|
||||
this.userpage = data;
|
||||
this.working = false;
|
||||
this.snackBar.open(this.i18n.get('userpages.success', []), this.i18n.get("close", []), {
|
||||
duration: 3000
|
||||
});
|
||||
this.router.navigateByUrl('/p/' + this.userpage.name);
|
||||
this.userPageService.getUserPages();
|
||||
}, (error) => {
|
||||
this.working = false;
|
||||
if (error.status == 403) {
|
||||
this.snackBar.open("Error");
|
||||
}
|
||||
if (error.status == 422) {
|
||||
let errors = {};
|
||||
for (let code of error.error) {
|
||||
errors[ code.field ] = errors[ code.field ] || {};
|
||||
errors[ code.field ][ code.code ] = true;
|
||||
}
|
||||
|
||||
for (let code in errors) {
|
||||
this.form.get(code).setErrors(errors[ code ]);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deleteUserPage() {
|
||||
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||
data: {
|
||||
'label': 'userpages.confirmDelete',
|
||||
'args': [ this.userpage.name ]
|
||||
}
|
||||
})
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.working = true;
|
||||
this.userPageService.deleteUserPage(this.userpage.name).subscribe(() => {
|
||||
this.working = false;
|
||||
this.router.navigateByUrl('/');
|
||||
this.userPageService.getUserPages();
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<div fxLayout="column" fxFlexFill>
|
||||
<div class="container">
|
||||
<span>{{name}} <small *ngIf="!username"><a routerLink="/p/{{name}}/edit" matTooltip="{{'userpages.edit' | i18n}}">
|
||||
<mat-icon inline="true">edit</mat-icon>
|
||||
</a></small></span>
|
||||
</div>
|
||||
<page-entries #entries [fetch]="boundFetch" [filter]="false" fxFlex="grow"></page-entries>
|
||||
</div>
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { EntriesService } from '../../services/entries.service';
|
||||
import { PageEntries } from '../entries/entries.page';
|
||||
|
||||
@Component({
|
||||
selector: 'page-userpage',
|
||||
templateUrl: './userpage.page.html'
|
||||
})
|
||||
export class PageUserPage implements OnInit, OnDestroy {
|
||||
|
||||
boundFetch: Function;
|
||||
name: string;
|
||||
username: string;
|
||||
paramsSub: Subscription;
|
||||
@ViewChild('entries') entries: PageEntries;
|
||||
|
||||
constructor(
|
||||
private entriesService: EntriesService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.paramsSub = this.route.params.subscribe((params: Params) => {
|
||||
this.name = params[ 'name' ];
|
||||
this.username = params[ 'username' ];
|
||||
if (this.entries) {
|
||||
this.entries.refresh();
|
||||
}
|
||||
});
|
||||
this.boundFetch = this.fetch.bind(this);
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.paramsSub.unsubscribe();
|
||||
}
|
||||
|
||||
fetch(page: number, size: number, asc: boolean, filter: any) {
|
||||
return this.entriesService.getByUserPage(this.name, page, size, asc, this.username);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user