added tags + filtering

This commit is contained in:
2021-12-01 19:01:17 +01:00
parent 1da276c670
commit 979324ccc8
30 changed files with 526 additions and 84 deletions
+1 -1
View File
@@ -1 +1 @@
<ui-entries [entries]="entries" [refresh]="boundRefresh" [update]="boundUpdate"></ui-entries>
<ui-entries [entries]="entries" [refresh]="boundRefresh" [update]="boundUpdate" [gravityFilter]="gravityFilter"></ui-entries>
+29 -4
View File
@@ -12,10 +12,13 @@ export class PageEntries implements OnInit {
settings: any;
@Input() fetch: Function;
@Input() gravityFilter: boolean = false;
entries: any;
asc: boolean = false;
boundRefresh: Function;
boundUpdate: Function;
init: boolean = true;
filterOpen: boolean = false;
constructor(
private settingsService: SettingsService, private router: Router, private route: ActivatedRoute) { }
@@ -25,7 +28,7 @@ export class PageEntries implements OnInit {
this.boundUpdate = this.update.bind(this);
this.route.queryParams.subscribe(params => {
if (this.init) {
this.entries = {};
this.entries = { filter: {} };
if (params[ 'p' ]) {
this.entries.number = +params[ 'p' ] - 1;
if (this.entries.number < 0) {
@@ -37,6 +40,16 @@ export class PageEntries implements OnInit {
this.entries.size = +params[ 's' ];
}
if (params[ 'asc' ]) {
this.asc = true;
}
for (const param in params) {
if (param != 's' && param != 'p' && param != 'asc') {
this.entries.filter[ param ] = params[ param ];
}
}
this.settingsService.settings.subscribe((settings) => {
this.settings = settings;
this.refresh();
@@ -51,9 +64,11 @@ export class PageEntries implements OnInit {
this.entries = {};
}
const filter = JSON.parse(JSON.stringify(this.entries.filter || {}))
this.entries.content = null;
this.fetch(this.entries.number || 0, this.entries.size || this.settings.pageSize).subscribe((data: any) => {
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) => { })
}
@@ -70,6 +85,12 @@ export class PageEntries implements OnInit {
params.s = event.pageSize;
}
if (this.entries.filter) {
for (const param in this.entries.filter) {
params[ param ] = this.entries.filter[ param ];
}
}
this.router.navigate(
[],
{
@@ -78,9 +99,13 @@ export class PageEntries implements OnInit {
queryParamsHandling: 'merge'
});
this.fetch(event.pageIndex, event.pageSize).subscribe((data: any) => {
const filter = JSON.parse(JSON.stringify(this.entries.filter || {}))
this.fetch(event.pageIndex, event.pageSize, this.asc, this.entries.filter).subscribe((data: any) => {
this.entries = data;
}, (error) => { })
this.entries.filter = filter;
}, (error) => {
this.entries = {};
})
}
}
+8 -4
View File
@@ -1,5 +1,6 @@
<div class="container">
<form [formGroup]="form" (ngSubmit)="update()" #formDirective="ngForm">
<mat-progress-bar *ngIf="!entry" mode="indeterminate"></mat-progress-bar>
<form [formGroup]="form" (ngSubmit)="update()" #formDirective="ngForm" *ngIf="entry">
<mat-card>
<mat-card-content>
<p>{{'submission.edit' | i18n}}</p>
@@ -23,18 +24,21 @@
</mat-error>
</mat-form-field>
<mat-form-field>
<textarea [mat-autosize] [matAutosizeMinRows]="3" matInput placeholder="{{'submission.text' | i18n}}" [required]="entryType != 'LINK'"
formControlName="text"></textarea>
<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)]="entry.tags" placeholder="{{'submission.tags' | i18n}}"></ui-tagspicker>
</mat-card-content>
<mat-card-actions>
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
{{'submission.update' | i18n}}
</button>
<a *ngIf="success" mat-button color="primary">{{'submission.success' | i18n}}</a>
<a *ngIf="success" mat-button color="primary" routerLink="/e/{{entry.id}}">{{'submission.success' | i18n}}</a>
</mat-card-actions>
</mat-card>
</form>
+6 -1
View File
@@ -2,6 +2,11 @@ mat-form-field {
display: block;
}
mat-chip mat-icon.mat-icon-inline {
margin-top: -12px;
margin-right: -2px;
}
form {
margin: 5px;
@@ -17,4 +22,4 @@ form {
@media screen and (min-width: 992px) {
max-width: 50%;
}
}
}
+76 -23
View File
@@ -1,9 +1,12 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { EntriesService } from '../../../services/entries.service';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators, NgForm } from '@angular/forms';
import { distinctUntilChanged, debounceTime } from 'rxjs/operators';
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
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';
@Component({
selector: 'page-entry-edit',
@@ -20,9 +23,11 @@ export class PageEntryEdit implements OnInit {
working: boolean = false;
success: boolean = false;
form: FormGroup;
readonly tagsSeparatorKeysCodes = [ ENTER, COMMA, SPACE ] as const;
@ViewChild('formDirective') private formDirective: NgForm;
constructor(private entriesService: EntriesService,
private tagsService: TagsService,
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private snackBar: MatSnackBar) { }
@@ -73,6 +78,11 @@ export class PageEntryEdit implements OnInit {
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;
@@ -88,6 +98,25 @@ 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 {
if (this.working) {
@@ -96,31 +125,55 @@ export class PageEntryEdit implements OnInit {
this.working = true;
this.entry.url = this.form.get("url").value;
this.entry.title = this.form.get("title").value;
this.entry.text = this.form.get("text").value;
if (this.entry.metadata.edit) {
this.entry.url = this.form.get("url").value;
this.entry.title = this.form.get("title").value;
this.entry.text = this.form.get("text").value;
this.entriesService.update(this.entry).subscribe((data) => {
this.entry = data;
this.working = false;
this.success = true;
}, (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;
this.entriesService.update(this.entry).subscribe((data) => {
this.entry = data;
this.working = false;
this.success = true;
}, (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 ]);
for (let code in errors) {
this.form.get(code).setErrors(errors[ code ]);
}
}
}
})
})
} else {
this.tagsService.setTags(this.entry.id, this.entry.tags).subscribe((data) => {
this.entry = data;
this.working = false;
this.success = true;
}, (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 ]);
}
}
})
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
<div class="container">
<page-notfound *ngIf="notfound"></page-notfound>
<ng-container *ngIf="entry">
<ui-entry [entry]="entry" [change]="boundRefresh"></ui-entry>
<ui-entry [entry]="entry" [change]="boundRefresh" [linkedTag]="false"></ui-entry>
<p class="text" [innerHTML]="entry.text | urltext"></p>
+1
View File
@@ -33,6 +33,7 @@ export class PageEntry implements OnInit {
this.entry = data;
}, (error) => {
if (error.status == 404) {
this.entry = false;
this.notfound = true;
}
})
+1 -1
View File
@@ -1 +1 @@
<page-entries [fetch]="boundFetch"></page-entries>
<page-entries [fetch]="boundFetch" [gravityFilter]="true"></page-entries>
+2 -2
View File
@@ -19,8 +19,8 @@ export class PageHot implements OnInit {
this.boundFetch = this.fetch.bind(this);
}
fetch(page: number, size: number) {
return this.entriesService.getComments(page,size);
fetch(page: number, size: number, asc: boolean, filter: any) {
return this.entriesService.getComments(page, size, asc, filter);
}
}
+2 -2
View File
@@ -16,8 +16,8 @@ export class PageLast implements OnInit {
this.boundFetch = this.fetch.bind(this);
}
fetch(page: number, size: number) {
return this.entriesService.getLastComment(page,size);
fetch(page: number, size: number,asc: boolean, filter: any) {
return this.entriesService.getLastComment(page, size,asc, filter);
}
}
+1 -1
View File
@@ -53,7 +53,7 @@
<span fxFlexOffset="auto"></span>
<div class="container" *ngIf="!internalLogin">
<small>
<a href="/login?all">{{'login.local' | i18n}}</a>
<a href="javascript:" (click)="internalLogin = true;">{{'login.local' | i18n}}</a>
</small>
</div>
</div>
+2 -2
View File
@@ -18,8 +18,8 @@ export class PageNew implements OnInit {
this.boundFetch = this.fetch.bind(this);
}
fetch(page: number, size: number) {
return this.entriesService.getNew(page,size);
fetch(page: number, size: number, asc: boolean, filter: any) {
return this.entriesService.getNew(page, size, asc, filter);
}
}
@@ -31,12 +31,15 @@
</mat-error>
</mat-form-field>
<mat-form-field>
<textarea [mat-autosize] [matAutosizeMinRows]="3" matInput placeholder="{{'submission.text' | i18n}}" [required]="entryType != 'LINK'"
formControlName="text"></textarea>
<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}}"></ui-tagspicker>
</mat-card-content>
<mat-card-actions>
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
@@ -2,6 +2,11 @@ mat-form-field {
display: block;
}
ui-tagspicker {
display: block;
width: 100%;
}
form {
margin: 5px;
@@ -17,4 +22,4 @@ form {
@media screen and (min-width: 992px) {
max-width: 50%;
}
}
}
+25 -1
View File
@@ -2,7 +2,9 @@ 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 { tap, distinctUntilChanged, debounceTime } from 'rxjs/operators';
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { distinctUntilChanged, debounceTime } from 'rxjs/operators';
import { MatChipInputEvent } from '@angular/material/chips';
@Component({
selector: 'page-submission',
@@ -15,6 +17,8 @@ export class PageSubmission implements OnInit {
entryType: string = this.entryTypes[ 0 ];
working: boolean = false;
form: FormGroup;
readonly tagsSeparatorKeysCodes = [ ENTER, COMMA, SPACE ] as const;
tags: string[] = [];
@ViewChild('formDirective') private formDirective: NgForm;
constructor(private entriesService: EntriesService,
@@ -64,6 +68,25 @@ export class PageSubmission implements OnInit {
}
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) {
@@ -77,6 +100,7 @@ export class PageSubmission implements OnInit {
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('/');
+1 -1
View File
@@ -1 +1 @@
<page-entries [fetch]="boundFetch"></page-entries>
<page-entries [fetch]="boundFetch" [gravityFilter]="true"></page-entries>
+2 -2
View File
@@ -16,8 +16,8 @@ export class PageTop implements OnInit {
this.boundFetch = this.fetch.bind(this);
}
fetch(page: number, size: number) {
return this.entriesService.getRanked(page,size);
fetch(page: number, size: number, asc: boolean, filter: any) {
return this.entriesService.getRanked(page, size, asc, filter);
}
}
@@ -20,8 +20,8 @@ export class PageUserEntries implements OnInit {
this.boundFetch = this.fetch.bind(this);
}
fetch(page: number, size: number) {
return this.entriesService.getByUser(this.username, page, size);
fetch(page: number, size: number, asc: boolean, filter: any) {
return this.entriesService.getByUser(this.username, page, size, asc, filter);
}
}