userpages + filter improvments
This commit is contained in:
parent
c1ea8948fc
commit
fb6631576e
@ -14,12 +14,14 @@ import { PageModerationEntries } from './pages/moderation/entries/moderation.ent
|
||||
import { PageNew } from './pages/new/new.page';
|
||||
import { PageNotFound } from './pages/notfound/notfound.page';
|
||||
import { PageSettings } from './pages/settings/settings.page';
|
||||
import { PageSubmission } from './pages/submission/submission.page';
|
||||
import { PageTop } from './pages/top/top.page';
|
||||
import { PageUnavailable } from './pages/unavailable/unavailable.page';
|
||||
import { PageUser } from './pages/user/user.page';
|
||||
|
||||
import { PageUserComments } from './pages/user/usercomments/usercomments.page';
|
||||
import { PageUserEntries } from './pages/user/userentries/userentries.page';
|
||||
import { PageUserPageEdit } from './pages/userpage/edit/edit.page';
|
||||
import { PageUserPage } from './pages/userpage/userpage.page';
|
||||
import { UiMain } from './ui/main/main.ui';
|
||||
|
||||
|
||||
@ -31,12 +33,16 @@ const routes: Routes = [
|
||||
{ path: 'top', component: PageTop, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'hot', component: PageHot, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'last', component: PageLast, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'p', component: PageUserPageEdit, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'p/:name', component: PageUserPage, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'p/:name/edit', component: PageUserPageEdit, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'p/:name/:username', component: PageUserPage, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'moderation/comments', component: PageModerationComments, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'moderation/entries', component: PageModerationEntries, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'new', component: PageNew, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'bookmarks', component: PageBookmarks, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'settings', component: PageSettings, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'submit', component: PageSubmission, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'submit', component: PageEntryEdit, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'e/:id', component: PageEntry, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'e/:id/edit', component: PageEntryEdit, canActivate: [ AuthenticatedGuard ] },
|
||||
{ path: 'c/:id', component: PageComment, canActivate: [ AuthenticatedGuard ] },
|
||||
|
@ -30,7 +30,6 @@ import { PageModerationEntries } from './pages/moderation/entries/moderation.ent
|
||||
import { PageNew } from './pages/new/new.page';
|
||||
import { PageNotFound } from './pages/notfound/notfound.page'
|
||||
import { PageSettings } from './pages/settings/settings.page';
|
||||
import { PageSubmission } from './pages/submission/submission.page';
|
||||
import { PageTop } from './pages/top/top.page';
|
||||
import { PageUnavailable } from './pages/unavailable/unavailable.page'
|
||||
import { PageUser } from './pages/user/user.page';
|
||||
@ -50,6 +49,9 @@ import { I18nService, I18nPaginatorIntl } from './services/i18n.service';
|
||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
import { environment } from '../environments/environment';
|
||||
import { UiTagsPicker } from './ui/tags/tagspicker.ui';
|
||||
import { UiUserPageMenu } from './ui/userpagemenu/userpagemenu.ui';
|
||||
import { PageUserPage } from './pages/userpage/userpage.page';
|
||||
import { PageUserPageEdit } from './pages/userpage/edit/edit.page';
|
||||
|
||||
|
||||
export function fetchI18n(i18n: I18nService) {
|
||||
@ -100,10 +102,11 @@ export class XhrInterceptor implements HttpInterceptor {
|
||||
PageNew,
|
||||
PageNotFound,
|
||||
PageSettings,
|
||||
PageSubmission,
|
||||
PageTop,
|
||||
PageUnavailable,
|
||||
PageUser, PageUserComments, PageUserEntries,
|
||||
PageUserPage,
|
||||
PageUserPageEdit,
|
||||
UiComment,
|
||||
UiCommentCount,
|
||||
UiCommentForm,
|
||||
@ -112,8 +115,9 @@ export class XhrInterceptor implements HttpInterceptor {
|
||||
UiEntry,
|
||||
UiMain,
|
||||
UiPoints,
|
||||
ConfirmDialog,
|
||||
UiTagsPicker
|
||||
UiTagsPicker,
|
||||
UiUserPageMenu,
|
||||
ConfirmDialog
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -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) => {
|
||||
|
67
src/app/pages/userpage/edit/edit.page.html
Normal file
67
src/app/pages/userpage/edit/edit.page.html
Normal file
@ -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>
|
@ -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;
|
||||
}
|
156
src/app/pages/userpage/edit/edit.page.ts
Normal file
156
src/app/pages/userpage/edit/edit.page.ts
Normal file
@ -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();
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
8
src/app/pages/userpage/userpage.page.html
Normal file
8
src/app/pages/userpage/userpage.page.html
Normal file
@ -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>
|
46
src/app/pages/userpage/userpage.page.ts
Normal file
46
src/app/pages/userpage/userpage.page.ts
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -50,6 +50,10 @@ export class EntriesService {
|
||||
return this.fetch("/last", page, size, asc, filter);
|
||||
}
|
||||
|
||||
getByUserPage(name: string, page: number, size: number, asc: boolean, username: string) {
|
||||
return this.fetch("/userpage/" + name, page, size, asc, username ? { username: username } : undefined);
|
||||
}
|
||||
|
||||
getByUser(username: string, page: number, size: number, asc: boolean, filter: any) {
|
||||
return this.fetch("/byuser/" + username, page, size, asc, filter);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import { environment } from '../../environments/environment';
|
||||
})
|
||||
export class SettingsService {
|
||||
|
||||
settings: ReplaySubject<any> = new ReplaySubject(undefined);
|
||||
settings: ReplaySubject<any> = new ReplaySubject(1);
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
53
src/app/services/userpage.service.ts
Normal file
53
src/app/services/userpage.service.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { RequestError } from './requesterror';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserPageService {
|
||||
|
||||
userPages: ReplaySubject<any> = new ReplaySubject(1);
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
getUserPages() {
|
||||
return this.http.get(environment.apiUrl + "/userpages").toPromise().then((data: any) => {
|
||||
this.userPages.next(data);
|
||||
return data;
|
||||
}, error => {
|
||||
throw new RequestError(error);
|
||||
});;
|
||||
}
|
||||
|
||||
getPublicUserPages(page: number, size: number, desc: boolean) {
|
||||
let httpParams = new HttpParams();
|
||||
if (page != undefined) {
|
||||
httpParams = httpParams.set("page", "" + page);
|
||||
}
|
||||
if (size != undefined) {
|
||||
httpParams = httpParams.set("size", "" + size);
|
||||
}
|
||||
if (desc) {
|
||||
httpParams = httpParams.set("desc", "" + desc);
|
||||
}
|
||||
|
||||
return this.http.get(environment.apiUrl + "/userpages/public", { params: httpParams });
|
||||
}
|
||||
|
||||
getUserPage(name: string) {
|
||||
return this.http.get(environment.apiUrl + "/userpages/userpage/" + name);
|
||||
}
|
||||
|
||||
createOrUpdate(userPage: any) {
|
||||
return this.http.post(environment.apiUrl + "/userpages/userpage", userPage);
|
||||
}
|
||||
|
||||
deleteUserPage(name: string) {
|
||||
return this.http.delete(environment.apiUrl + "/userpages/userpage/" + name);
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { CommentService } from '../../services/comment.service';
|
||||
import { SettingsService } from '../../services/settings.service';
|
||||
@ -9,7 +10,7 @@ import { SettingsService } from '../../services/settings.service';
|
||||
templateUrl: './comments.ui.html',
|
||||
styleUrls: [ './comments.ui.scss' ]
|
||||
})
|
||||
export class UiComments implements OnInit {
|
||||
export class UiComments implements OnInit, OnDestroy {
|
||||
|
||||
settings: any;
|
||||
comments: any;
|
||||
@ -19,17 +20,22 @@ export class UiComments implements OnInit {
|
||||
@Input() subcomments: boolean = false;
|
||||
@Input() parentLink: boolean = false;
|
||||
boundRefresh: Function;
|
||||
settingsSubscription: Subscription;
|
||||
|
||||
constructor(private settingsService: SettingsService, private commentService: CommentService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.boundRefresh = this.refresh.bind(this);
|
||||
this.settingsService.settings.subscribe((settings) => {
|
||||
this.settingsSubscription = this.settingsService.settings.subscribe((settings) => {
|
||||
this.settings = settings;
|
||||
this.refresh();
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.settingsSubscription.unsubscribe;
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
if (this.parent) {
|
||||
this.commentService.getNewByParent(this.target, this.parent, 0, this.settings.pageSize, this.ignore).subscribe((data) => {
|
||||
|
@ -1,4 +1,19 @@
|
||||
<mat-progress-bar *ngIf="!entries || !entries.content" mode="indeterminate"></mat-progress-bar>
|
||||
<mat-progress-bar *ngIf="!entries || !entries.content && !entries.error" mode="indeterminate"></mat-progress-bar>
|
||||
|
||||
|
||||
<div *ngIf="entries && entries.error" fxLayout="column" fxFlexFill>
|
||||
<mat-card class="accent box">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ 'entries.error.' + entries.error.status | i18n}}</mat-card-title>
|
||||
<mat-card-subtitle>{{'entries.error' | i18n}}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<p>
|
||||
{{ 'entries.error.' + entries.error.status + '.text' | i18n}}
|
||||
</p>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
<div *ngIf="entries" fxLayout="column" fxFlexFill>
|
||||
<mat-list *ngIf="entries.content">
|
||||
@ -20,12 +35,13 @@
|
||||
<span fxFlexOffset="auto"></span>
|
||||
|
||||
<div class="mat-paginator" fxLayout="row" fxLayout.xs="column" fxLayoutAlign.xs="start start">
|
||||
<div class="filter-container">
|
||||
<a mat-icon-button mat-button (click)="filterOpen=!filterOpen" title="{{'entries.filter' | i18n}}">
|
||||
<div *ngIf="filter" class="filter-container">
|
||||
<a mat-icon-button mat-button (click)="filterOpen=!filterOpen" title="{{'entries.filter' | i18n}}" [color]="filterOpen ? 'accent' : 'primary'">
|
||||
<mat-icon>filter_alt</mat-icon>
|
||||
</a>
|
||||
<div *ngIf="filterOpen">
|
||||
<ui-tagspicker [(model)]="entries.filter.tag" max="1" placeholder="{{'entries.filter.tag' | i18n}}" [change]="boundTagspickerChange"></ui-tagspicker>
|
||||
<div *ngIf="filterOpen" fxLayout="row wrap">
|
||||
<ui-tagspicker [(model)]="tags" placeholder="{{'entries.filter.tags' | i18n}}" [change]="boundTagsPickerChange">
|
||||
</ui-tagspicker>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput [matDatepicker]="picker" [value]="entries && entries.filter && entries.filter.date"
|
||||
@ -35,8 +51,33 @@
|
||||
<mat-datepicker #picker touchUi></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="{{'entries.filter.entryType' | i18n}}"
|
||||
[value]="entries && entries.filter && entries.filter.type"
|
||||
(selectionChange)="setFilter('type', $event.value || undefined)">
|
||||
<mat-select-trigger>
|
||||
<span *ngIf="entries.filter.type">
|
||||
<mat-icon>{{'entryType.' + entries.filter.type + '.icon' | i18n}}</mat-icon> {{'entryType.' +
|
||||
entries.filter.type | 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>
|
||||
|
||||
|
||||
<ui-tagspicker [(model)]="excludedTags" placeholder="{{'entries.filter.excludedTags' | i18n}}" [change]="boundExcludedTagsPickerChange">
|
||||
</ui-tagspicker>
|
||||
|
||||
<mat-form-field *ngIf="gravityFilter">
|
||||
<input matInput type="number" step="0.01" min="0"
|
||||
<input matInput type="number" step="0.01" min="0" max="2.0"
|
||||
[value]="entries && entries.filter && entries.filter.gravity"
|
||||
(change)="setFilter('gravity', $event.target && $event.target.value || undefined)"
|
||||
placeholder="{{'entries.filter.gravity' | i18n}}">
|
||||
|
@ -31,4 +31,22 @@ mat-chip mat-icon.mat-icon-inline {
|
||||
|
||||
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%;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { Component, OnInit, Input, ViewChild } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { Observable } from 'rxjs';
|
||||
import { debounceTime, switchMap } from 'rxjs/operators';
|
||||
import { TagsService } from 'src/app/services/tags.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-entries',
|
||||
@ -17,19 +13,23 @@ export class UiEntries implements OnInit {
|
||||
@Input() update: Function;
|
||||
@Input() refresh: Function;
|
||||
@Input() gravityFilter: boolean = false;
|
||||
@Input() filter: boolean = true;
|
||||
@ViewChild(MatPaginator) matPaginator;
|
||||
pageSizeOptions: number[] = [ 1, 2, 3, 4, 5, 10, 30, 50, 100 ];
|
||||
pageSizeOptions: number[] = [ 1, 2, 3, 4, 5, 10, 15, 30, 50, 100 ];
|
||||
filterOpen: boolean = false;
|
||||
searchTags: Observable<Object>;
|
||||
boundTagspickerChange: Function;
|
||||
boundTagsPickerChange: Function;
|
||||
boundExcludedTagsPickerChange: Function;
|
||||
entryTypes: string[] = [ undefined, 'LINK', 'DISCUSSION', 'QUESTION', 'INTERN' ];
|
||||
tags: string[] = [];
|
||||
excludedTags: string[] = [];
|
||||
|
||||
|
||||
|
||||
constructor(private tagsService: TagsService) { }
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.checkFilterOpen();
|
||||
this.boundTagspickerChange = this.tagspickerChange.bind(this);
|
||||
this.boundTagsPickerChange = this.tagsPickerChange.bind(this);
|
||||
this.boundExcludedTagsPickerChange = this.excludedTagsPickerChange.bind(this);
|
||||
}
|
||||
|
||||
checkFilterOpen() {
|
||||
@ -38,15 +38,32 @@ export class UiEntries implements OnInit {
|
||||
for (const param in this.entries.filter) {
|
||||
if (this.entries.filter[ param ]) {
|
||||
this.filterOpen = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (param == 'tags' && this.entries.filter[ param ]) {
|
||||
this.tags = this.entries.filter[ param ].split(',');
|
||||
}
|
||||
if (param == 'excludedTags' && this.entries.filter[ param ]) {
|
||||
this.excludedTags = this.entries.filter[ param ].split(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tagspickerChange(value: any) {
|
||||
console.log("change", value);
|
||||
this.setFilter('tag', value[0]);
|
||||
tagsPickerChange(value: string[]) {
|
||||
if (value && value.length > 0) {
|
||||
this.setFilter('tags', value.join(','));
|
||||
} else {
|
||||
this.setFilter('tags', undefined);
|
||||
}
|
||||
}
|
||||
|
||||
excludedTagsPickerChange(value: string[]) {
|
||||
if (value && value.length > 0) {
|
||||
this.setFilter('excludedTags', value.join(','));
|
||||
} else {
|
||||
this.setFilter('excludedTags', undefined);
|
||||
}
|
||||
}
|
||||
|
||||
setFilter(key: string, value) {
|
||||
|
@ -45,7 +45,7 @@ export class UiEntry implements OnInit {
|
||||
[],
|
||||
{
|
||||
relativeTo: this.route,
|
||||
queryParams: { 'tag': tag },
|
||||
queryParams: { 'tags': tag },
|
||||
queryParamsHandling: 'merge'
|
||||
});
|
||||
}
|
||||
|
@ -43,18 +43,19 @@
|
||||
(click)="!isBiggerScreen() && opened=false">
|
||||
<mat-nav-list>
|
||||
<a *ngIf="authenticated" routerLink="/top" routerLinkActive="active" mat-list-item>
|
||||
<mat-icon>trending_up</mat-icon> {{'page.top' | i18n}}
|
||||
<mat-icon>{{'sorting.TOP.icon' | i18n}}</mat-icon> {{'sorting.TOP' | i18n}}
|
||||
</a>
|
||||
<a *ngIf="authenticated" routerLink="/new" routerLinkActive="active" mat-list-item>
|
||||
<mat-icon>today</mat-icon> {{'page.new' | i18n}}
|
||||
<mat-icon>{{'sorting.NEW.icon' | i18n}}</mat-icon> {{'sorting.NEW' | i18n}}
|
||||
</a>
|
||||
<mat-divider *ngIf="authenticated"></mat-divider>
|
||||
<a *ngIf="authenticated" routerLink="/hot" routerLinkActive="active" mat-list-item>
|
||||
<mat-icon>whatshot</mat-icon> {{'page.hot' | i18n}}
|
||||
<mat-icon>{{'sorting.HOT.icon' | i18n}}</mat-icon> {{'sorting.HOT' | i18n}}
|
||||
</a>
|
||||
<a *ngIf="authenticated" routerLink="/last" routerLinkActive="active" mat-list-item>
|
||||
<mat-icon>history</mat-icon> {{'page.last' | i18n}}
|
||||
<mat-icon>{{'sorting.LAST.icon' | i18n}}</mat-icon> {{'sorting.LAST' | i18n}}
|
||||
</a>
|
||||
<ui-userpagemenu *ngIf="authenticated"></ui-userpagemenu>
|
||||
<mat-divider *ngIf="moderator"></mat-divider>
|
||||
<a *ngIf="moderator" routerLink="/moderation/entries" routerLinkActive="active" mat-list-item>
|
||||
<mat-icon>report</mat-icon> {{'moderation.entries' | i18n}}
|
||||
|
10
src/app/ui/userpagemenu/userpagemenu.ui.html
Normal file
10
src/app/ui/userpagemenu/userpagemenu.ui.html
Normal file
@ -0,0 +1,10 @@
|
||||
<ng-container *ngIf="settings">
|
||||
<mat-divider></mat-divider>
|
||||
<a *ngFor="let userpage of userpages" routerLink="/p/{{userpage.name}}" routerLinkActive="active" mat-list-item>
|
||||
<mat-icon>star</mat-icon> {{userpage.name}}
|
||||
</a>
|
||||
<a *ngIf="userpages.length < settings.maxUserPages" routerLink="/p" routerLinkActive="active" mat-list-item
|
||||
title="{{'userpages.add' | i18n}}">
|
||||
<mat-icon style="margin: 0 auto;">add</mat-icon>
|
||||
</a>
|
||||
</ng-container>
|
3
src/app/ui/userpagemenu/userpagemenu.ui.scss
Normal file
3
src/app/ui/userpagemenu/userpagemenu.ui.scss
Normal file
@ -0,0 +1,3 @@
|
||||
mat-form-field {
|
||||
display: block;
|
||||
}
|
32
src/app/ui/userpagemenu/userpagemenu.ui.ts
Normal file
32
src/app/ui/userpagemenu/userpagemenu.ui.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { SettingsService } from 'src/app/services/settings.service';
|
||||
import { UserPageService } from 'src/app/services/userpage.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-userpagemenu',
|
||||
templateUrl: 'userpagemenu.ui.html',
|
||||
styleUrls: [ './userpagemenu.ui.scss' ]
|
||||
})
|
||||
export class UiUserPageMenu implements OnInit {
|
||||
|
||||
|
||||
userpages: any[] = [];
|
||||
settings: any;
|
||||
settingsSubscription: Subscription;
|
||||
|
||||
constructor(private userPageService: UserPageService, private settingsService: SettingsService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.settingsSubscription = this.settingsService.settings.subscribe((settings) => {
|
||||
this.settings = settings;
|
||||
this.userPageService.userPages.subscribe((data: any) => {
|
||||
this.userpages = data.content;
|
||||
})
|
||||
this.userPageService.getUserPages();
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user