search prep, refactor userpages

This commit is contained in:
2021-12-03 23:47:37 +01:00
parent c2b29317c3
commit d75fa5fe59
39 changed files with 544 additions and 235 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
<ui-entries [entries]="entries" [refresh]="boundRefresh" [update]="boundUpdate" [filter]="filter"
[gravityFilter]="gravityFilter"></ui-entries>
[gravityFilter]="gravityFilter" [text]="text"></ui-entries>
+2 -1
View File
@@ -13,6 +13,7 @@ export class PageEntries implements OnInit, OnDestroy {
@Input() fetch: Function;
@Input() filter: boolean = true;
@Input() text: boolean = false;
@Input() gravityFilter: boolean = false;
entries: any;
asc: boolean = false;
@@ -114,7 +115,7 @@ export class PageEntries implements OnInit, OnDestroy {
this.entries = data;
this.entries.filter = filter;
}, (error) => {
this.entries = {};
this.entries = { error: error };
})
}
+1 -3
View File
@@ -1,9 +1,7 @@
<div class="container">
<page-notfound *ngIf="notfound"></page-notfound>
<ng-container *ngIf="entry">
<ui-entry [entry]="entry" [change]="boundRefresh" [linkedTag]="false"></ui-entry>
<p class="text" [innerHTML]="entry.text | urltext"></p>
<ui-entry [entry]="entry" [change]="boundRefresh" [linkedTag]="false" [text]="true"></ui-entry>
<ui-commentform [target]="entry.id" (replyCommentEvent)="replyCallback($event)"></ui-commentform>
-4
View File
@@ -1,4 +0,0 @@
.text {
max-width: 100%;
white-space: break-spaces;
}
-1
View File
@@ -1 +0,0 @@
<page-entries [fetch]="boundFetch" [gravityFilter]="true"></page-entries>
-26
View File
@@ -1,26 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { EntriesService } from '../../services/entries.service';
import { PageEvent } from '@angular/material/paginator';
@Component({
selector: 'page-hot',
templateUrl: './hot.page.html'
})
export class PageHot implements OnInit {
boundFetch: Function;
constructor(private entriesService: EntriesService) { }
ngOnInit(): void {
this.boundFetch = this.fetch.bind(this);
}
fetch(page: number, size: number, asc: boolean, filter: any) {
return this.entriesService.getComments(page, size, asc, filter);
}
}
-1
View File
@@ -1 +0,0 @@
<page-entries [fetch]="boundFetch"></page-entries>
-23
View File
@@ -1,23 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { EntriesService } from '../../services/entries.service';
@Component({
selector: 'page-last',
templateUrl: './last.page.html'
})
export class PageLast implements OnInit {
boundFetch: Function;
constructor(private entriesService: EntriesService) { }
ngOnInit(): void {
this.boundFetch = this.fetch.bind(this);
}
fetch(page: number, size: number,asc: boolean, filter: any) {
return this.entriesService.getLastComment(page, size,asc, filter);
}
}
-1
View File
@@ -1 +0,0 @@
<page-entries [fetch]="boundFetch"></page-entries>
-25
View File
@@ -1,25 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { PageEvent } from '@angular/material/paginator';
import { EntriesService } from '../../services/entries.service';
@Component({
selector: 'page-new',
templateUrl: './new.page.html'
})
export class PageNew implements OnInit {
boundFetch: Function;
constructor(private entriesService: EntriesService) { }
ngOnInit(): void {
this.boundFetch = this.fetch.bind(this);
}
fetch(page: number, size: number, asc: boolean, filter: any) {
return this.entriesService.getNew(page, size, asc, filter);
}
}
+103
View File
@@ -0,0 +1,103 @@
<div fxLayout="column" fxFlexFill>
<div class="container">
<mat-form-field appearance="fill">
<mat-label>{{'search.query' | i18n}}</mat-label>
<input matInput [formControl]="searchFormControl">
</mat-form-field>
<mat-form-field>
<mat-select placeholder="{{'search.types' | i18n}}" [value]="searchType"
(selectionChange)="searchType = $event.value; refresh()">
<mat-select-trigger>
<span *ngIf="searchType">
<mat-icon>{{'search.types.' + searchType + '.icon' | i18n}}</mat-icon> {{'search.types.' +
searchType | i18n}}
</span>
</mat-select-trigger>
<mat-option *ngFor="let type of types" [value]="type">
<span *ngIf="type">
<mat-icon>{{'search.types.' + type + '.icon' | i18n}}</mat-icon> {{'search.types.' + type | i18n}}
</span>
<span *ngIf="!type">
{{'search.types.all' | i18n}}
</span>
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="{{'search.byDate' | i18n}}" [value]="byDate" (selectionChange)="byDate = $event.value; refresh()">
<mat-select-trigger>
<mat-icon>{{'search.byDate.' + byDate + '.icon' | i18n}}</mat-icon> {{'search.byDate.' +
byDate | i18n}}
</mat-select-trigger>
<mat-option [value]="false">
<mat-icon>{{'search.byDate.false.icon' | i18n}}</mat-icon>
{{'search.byDate.false' | i18n}}
</mat-option>
<mat-option [value]="true">
<mat-icon>{{'search.byDate.true.icon' | i18n}}</mat-icon>
{{'search.byDate.true' | i18n}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="{{'search.asc' | i18n}}" [value]="asc" (selectionChange)="asc = $event.value; refresh()">
<mat-select-trigger>
<mat-icon>{{'search.asc.' + asc + '.icon' | i18n}}</mat-icon> {{'search.asc.' +
asc | i18n}}
</mat-select-trigger>
<mat-option [value]="false">
<mat-icon>{{'search.asc.false.icon' | i18n}}</mat-icon>
{{'search.asc.false' | i18n}}
</mat-option>
<mat-option [value]="true">
<mat-icon>{{'search.asc.true.icon' | i18n}}</mat-icon>
{{'search.asc.true' | i18n}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<mat-progress-bar *ngIf="!results || !results.content && !results.error" mode="indeterminate"></mat-progress-bar>
<div *ngIf="results && results.error" fxLayout="column" fxFlexFill>
<mat-card class="accent box">
<mat-card-header>
<mat-card-title>{{ 'search.error.' + results.error.status | i18n}}</mat-card-title>
<mat-card-subtitle>{{'search.error' | i18n}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<p>
{{ 'search.error.' + results.error.status + '.text' | i18n}}
</p>
</mat-card-content>
</mat-card>
</div>
<div class="results" *ngIf="results" fxLayout="column">
<mat-list *ngIf="results.content">
<ng-container *ngFor="let result of results.content; let i = index">
<mat-divider *ngIf="i > 0"></mat-divider>
<mat-list-item>
<ui-entry *ngIf="result.type == 'entry'" class="entry" [entry]="result"
[index]="i+1 + results.number*results.size" [change]="boundRefresh" [text]="true" [actions]="false">
</ui-entry>
<ui-comment *ngIf="result.type == 'comment'" class="comment" [comment]="result"
[index]="i+1 + results.number*results.size" [change]="boundRefresh" [subcomments]="false"
[parentLink]="true" [actions]="false"></ui-comment>
</mat-list-item>
</ng-container>
</mat-list>
<mat-list *ngIf="results.totalElements == 0">
<mat-list-item>
<p>{{'search.nothing' | i18n}}</p>
</mat-list-item>
</mat-list>
<span fxFlexOffset="auto" fxFlexOffset.xs="none"></span>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [pageIndex]="results.number" [length]="results.totalElements"
[pageSize]="results.size" (page)="update && update($event)" showFirstLastButtons>
</mat-paginator>
</div>
</div>
+61
View File
@@ -0,0 +1,61 @@
.results {
height: 100%;
}
mat-list mat-list-item {
height: auto;
}
mat-form-field {
margin-right: 15px;
}
.container {
margin-bottom: 0;
}
mat-form-field .mat-form-field-wrapper {
padding-bottom: 0;
}
.entry {
display: inline-block;
width: 100%;
max-width: 100%;
}
.entry-item {
height: auto;
min-height: 48px;
}
mat-chip mat-icon.mat-icon-inline {
margin-top: -12px;
margin-right: -2px;
}
.mat-option .mat-icon {
margin-right: -2px;
}
ui-tagspicker {
display: inline-block;
margin-left: 15px;
}
.box {
margin: 5px;
@media screen and (min-width: 576px) {
max-width: 100%;
}
@media screen and (min-width: 768px) {
max-width: 80%;
margin: 15px;
}
@media screen and (min-width: 992px) {
max-width: 50%;
}
}
+109
View File
@@ -0,0 +1,109 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { PageEvent } from '@angular/material/paginator';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { SearchService } from 'src/app/services/search.service';
import { SettingsService } from 'src/app/services/settings.service';
@Component({
selector: 'page-search',
templateUrl: './search.page.html',
styleUrls: [ './search.page.scss' ]
})
export class PageSearch implements OnInit {
searchFormControl = new FormControl();
pageSizeOptions: number[] = [ 1, 2, 3, 4, 5, 10, 15, 30, 50, 100 ];
results: any;
asc: boolean = false;
byDate: boolean = false;
settings: any;
settingsSubscription: Subscription;
boundRefresh: Function;
types: string[] = [ 'all', 'entry', 'comment' ];
searchType: string = this.types[ 1 ];
constructor(
private searchService: SearchService,
private settingsService: SettingsService,
private router: Router,
private route: ActivatedRoute) { }
ngOnInit(): void {
this.boundRefresh = this.refresh.bind(this);
this.asc = this.route.snapshot.queryParamMap.get('asc') == 'true';
this.byDate = this.route.snapshot.queryParamMap.get('byDate') == 'true';
this.searchType = this.route.snapshot.queryParamMap.get('type') || this.types[ 1 ];
this.searchFormControl.setValue(this.route.snapshot.queryParamMap.get('q'));
this.searchFormControl.valueChanges.pipe(
debounceTime(300)).subscribe((value: string) => {
this.refresh();
})
this.settingsSubscription = this.settingsService.settings.subscribe((settings) => {
this.settings = settings;
this.refresh();
})
}
ngOnDestroy(): void {
this.settingsSubscription.unsubscribe();
}
refresh(): void {
if (!this.results) {
this.results = {};
}
const params: any = { q: null, p: null };
params.q = this.searchFormControl.value;
params.asc = this.asc ? 'true' : undefined;
params.byDate = this.byDate ? 'true' : undefined;
params.type = this.searchType != 'entry' ? this.searchType : undefined;
this.router.navigate(
[],
{
relativeTo: this.route,
queryParams: params,
queryParamsHandling: 'merge'
});
this.results.content = null;
this.searchService.search(this.searchFormControl.value || " ", this.searchType, 0, this.results.size || this.settings.pageSize, this.asc, this.byDate).subscribe((data: any) => {
this.results = data;
}, (error) => {
this.results = { error: error };
})
}
update(event: PageEvent) {
this.results.content = null;
const params: any = { p: null, s: null };
if (event.pageIndex != 0) {
params.p = event.pageIndex + 1;
}
if (event.pageSize != this.settings.pageSize) {
params.s = event.pageSize;
}
this.router.navigate(
[],
{
relativeTo: this.route,
queryParams: params,
queryParamsHandling: 'merge'
});
this.searchService.search(this.searchFormControl.value, this.searchType, event.pageIndex, event.pageSize, this.asc, this.byDate).subscribe((data: any) => {
this.results = data;
}, (error) => {
this.results = { error: error };
})
}
}
+2 -3
View File
@@ -1,5 +1,5 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators, NgForm } from '@angular/forms';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { UserService } from '../../services/user.service';
import { SettingsService } from 'src/app/services/settings.service';
@@ -18,7 +18,6 @@ export class PageSettings implements OnInit, OnDestroy {
success: boolean = false;
form: FormGroup;
settings: any;
@ViewChild('formDirective') private formDirective: NgForm;
settingsSubscription: Subscription;
constructor(
-1
View File
@@ -1 +0,0 @@
<page-entries [fetch]="boundFetch" [gravityFilter]="true"></page-entries>
-23
View File
@@ -1,23 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { EntriesService } from '../../services/entries.service';
@Component({
selector: 'page-top',
templateUrl: './top.page.html'
})
export class PageTop implements OnInit {
boundFetch: Function;
constructor(private entriesService: EntriesService) { }
ngOnInit(): void {
this.boundFetch = this.fetch.bind(this);
}
fetch(page: number, size: number, asc: boolean, filter: any) {
return this.entriesService.getRanked(page, size, asc, filter);
}
}
+13 -2
View File
@@ -50,6 +50,15 @@
</mat-select>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="{{'userpages.index' | i18n}}" formControlName="index" type="number" min="0">
<mat-error *ngIf="hasError('index')">
<div *ngFor="let error of form.get('index').errors | keyvalue">
{{'userpages.index.error.' + error.key | i18n}}<br>
</div>
</mat-error>
</mat-form-field>
</mat-card-content>
<mat-card-actions fxLayout="row wrap" fxFlexFill>
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
@@ -58,9 +67,11 @@
<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> {{
<a mat-raised-button color="warn" *ngIf="!working && userpage.id" (click)="deleteUserPage()">
<mat-icon>delete</mat-icon> {{
'userpages.delete' |
i18n}}</a>
i18n}}
</a>
</mat-card-actions>
</mat-card>
</form>
+18 -4
View File
@@ -27,6 +27,7 @@ export class PageUserPageEdit implements OnInit, OnDestroy {
working: boolean = false;
form: FormGroup;
settings: any;
index: number;
settingsSubscription: Subscription;
@@ -45,12 +46,17 @@ export class PageUserPageEdit implements OnInit, OnDestroy {
name: [ '', Validators.required ],
entryType: [ '', Validators.nullValidator ],
sorting: [ '', Validators.required ],
index: [ '', Validators.required ],
});
this.settingsSubscription = this.settingsService.settings.subscribe((settings) => {
this.settings = settings;
});
if (this.route.snapshot.queryParamMap.get('name')) {
this.form.get('name').setValue(this.route.snapshot.queryParamMap.get('name'));
}
this.form.get('entryType').setValue(this.entryType);
this.form.get('entryType').valueChanges.subscribe((value) => {
this.entryType = value;
@@ -59,6 +65,10 @@ export class PageUserPageEdit implements OnInit, OnDestroy {
this.form.get('sorting').valueChanges.subscribe((value) => {
this.sorting = value;
});
this.form.get('index').setValue(this.index);
this.form.get('index').valueChanges.subscribe((value) => {
this.index = value;
});
this.name = this.route.snapshot.paramMap.get('name');
this.refresh();
@@ -70,13 +80,10 @@ export class PageUserPageEdit implements OnInit, OnDestroy {
refresh() {
if (this.name) {
this.userPageService.getUserPage(this.name).subscribe((data) => {
this.userPageService.getUserPage(this.name, undefined).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;
@@ -88,7 +95,13 @@ export class PageUserPageEdit implements OnInit, OnDestroy {
this.userpage.sorting = this.sorting;
this.userpage.tags = [];
this.userpage.excludedTags = [];
this.userpage.index = 99;
}
this.form.get("name").setValue(this.userpage.name);
this.form.get("entryType").setValue(this.userpage.entryType);
this.form.get("sorting").setValue(this.userpage.sorting);
this.form.get("index").setValue(this.userpage.index);
}
hasError(controlName: string): boolean {
@@ -106,6 +119,7 @@ export class PageUserPageEdit implements OnInit, OnDestroy {
this.userpage.name = this.form.get("name").value;
this.userpage.entryType = this.entryType;
this.userpage.sorting = this.sorting;
this.userpage.index = this.index;
this.userPageService.createOrUpdate(this.userpage).subscribe((data) => {
this.userpage = data;
+4 -6
View File
@@ -1,8 +1,6 @@
<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>
<a mat-mini-fab routerLink="/p/{{name}}/edit" matTooltip="{{'userpages.edit' | i18n}}" color="primary">
<mat-icon inline="true">edit</mat-icon>
</a>
<page-entries #entries [fetch]="boundFetch" fxFlex="grow"></page-entries>
</div>
@@ -0,0 +1,6 @@
a.mat-mini-fab {
position: fixed;
top: 50px;
right: 15px;
z-index: 5;
}
+18 -5
View File
@@ -1,13 +1,15 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { UserPageService } from 'src/app/services/userpage.service';
import { EntriesService } from '../../services/entries.service';
import { PageEntries } from '../entries/entries.page';
@Component({
selector: 'page-userpage',
templateUrl: './userpage.page.html'
templateUrl: './userpage.page.html',
styleUrls: [ './userpage.page.scss' ]
})
export class PageUserPage implements OnInit, OnDestroy {
@@ -19,6 +21,7 @@ export class PageUserPage implements OnInit, OnDestroy {
constructor(
private entriesService: EntriesService,
private userPageService: UserPageService,
private router: Router,
private route: ActivatedRoute) {
}
@@ -27,9 +30,14 @@ export class PageUserPage implements OnInit, OnDestroy {
this.paramsSub = this.route.params.subscribe((params: Params) => {
this.name = params[ 'name' ];
this.username = params[ 'username' ];
if (this.entries) {
this.entries.refresh();
}
this.userPageService.getUserPage(this.name, this.username).subscribe((data) => {
if (this.entries) {
this.entries.refresh();
}
}, (error) => {
this.router.navigate([ '/p' ], { queryParams: { name: this.name }, queryParamsHandling: 'merge' });
})
});
this.boundFetch = this.fetch.bind(this);
}
@@ -40,7 +48,12 @@ export class PageUserPage implements OnInit, OnDestroy {
}
fetch(page: number, size: number, asc: boolean, filter: any) {
return this.entriesService.getByUserPage(this.name, page, size, asc, this.username);
if (this.username) {
filter = filter || {};
filter.username = this.username;
}
return this.entriesService.getByUserPage(this.name, page, size, asc, filter);
}
}