Client: better notifications for a beautiful world

This commit is contained in:
Chocobozzz 2017-01-27 16:14:11 +01:00
parent cddadde81f
commit 7ddd02c9b8
18 changed files with 132 additions and 47 deletions

View File

@ -36,6 +36,7 @@
"@types/videojs": "0.0.30", "@types/videojs": "0.0.30",
"@types/webpack": "^2.0.0", "@types/webpack": "^2.0.0",
"angular-pipes": "^5.0.0", "angular-pipes": "^5.0.0",
"angular2-notifications": "^0.4.49",
"angular2-template-loader": "^0.6.0", "angular2-template-loader": "^0.6.0",
"assets-webpack-plugin": "^3.4.0", "assets-webpack-plugin": "^3.4.0",
"awesome-typescript-loader": "~3.0.0-beta.17", "awesome-typescript-loader": "~3.0.0-beta.17",

View File

@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { NotificationsService } from 'angular2-notifications';
import { validateHost } from '../../../shared'; import { validateHost } from '../../../shared';
import { FriendService } from '../shared'; import { FriendService } from '../shared';
@ -15,7 +17,11 @@ export class FriendAddComponent implements OnInit {
hosts = [ ]; hosts = [ ];
error: string = null; error: string = null;
constructor(private router: Router, private friendService: FriendService) {} constructor(
private router: Router,
private notificationsService: NotificationsService,
private friendService: FriendService
) {}
ngOnInit() { ngOnInit() {
this.form = new FormGroup({}); this.form = new FormGroup({});
@ -83,10 +89,11 @@ export class FriendAddComponent implements OnInit {
this.friendService.makeFriends(notEmptyHosts).subscribe( this.friendService.makeFriends(notEmptyHosts).subscribe(
status => { status => {
alert('Make friends request sent!'); this.notificationsService.success('Sucess', 'Make friends request sent!');
this.router.navigate([ '/admin/friends/list' ]); this.router.navigate([ '/admin/friends/list' ]);
}, },
error => alert(error.text)
err => this.notificationsService.error('Error', err.text)
); );
} }

View File

@ -1,5 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { NotificationsService } from 'angular2-notifications';
import { Friend, FriendService } from '../shared'; import { Friend, FriendService } from '../shared';
@Component({ @Component({
@ -10,7 +12,10 @@ import { Friend, FriendService } from '../shared';
export class FriendListComponent implements OnInit { export class FriendListComponent implements OnInit {
friends: Friend[]; friends: Friend[];
constructor(private friendService: FriendService) { } constructor(
private notificationsService: NotificationsService,
private friendService: FriendService
) { }
ngOnInit() { ngOnInit() {
this.getFriends(); this.getFriends();
@ -21,10 +26,12 @@ export class FriendListComponent implements OnInit {
this.friendService.quitFriends().subscribe( this.friendService.quitFriends().subscribe(
status => { status => {
alert('Quit friends!'); this.notificationsService.success('Sucess', 'Friends left!');
this.getFriends(); this.getFriends();
}, },
error => alert(error.text)
err => this.notificationsService.error('Error', err.text)
); );
} }
@ -32,7 +39,7 @@ export class FriendListComponent implements OnInit {
this.friendService.getFriends().subscribe( this.friendService.getFriends().subscribe(
res => this.friends = res.friends, res => this.friends = res.friends,
err => alert(err.text) err => this.notificationsService.error('Error', err.text)
); );
} }
} }

View File

@ -1,6 +1,7 @@
import { setInterval } from 'timers'
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnInit, OnDestroy } from '@angular/core';
import { NotificationsService } from 'angular2-notifications';
import { RequestService, RequestStats } from '../shared'; import { RequestService, RequestStats } from '../shared';
@Component({ @Component({
@ -11,9 +12,13 @@ import { RequestService, RequestStats } from '../shared';
export class RequestStatsComponent implements OnInit, OnDestroy { export class RequestStatsComponent implements OnInit, OnDestroy {
stats: RequestStats = null; stats: RequestStats = null;
private interval: NodeJS.Timer = null; private interval: number = null;
private timeout: number = null;
constructor(private requestService: RequestService) { } constructor(
private notificationsService: NotificationsService,
private requestService: RequestService
) { }
ngOnInit() { ngOnInit() {
this.getStats(); this.getStats();
@ -21,8 +26,12 @@ export class RequestStatsComponent implements OnInit, OnDestroy {
} }
ngOnDestroy() { ngOnDestroy() {
if (this.stats !== null && this.stats.secondsInterval !== null) { if (this.interval !== null) {
clearInterval(this.interval); window.clearInterval(this.interval);
}
if (this.timeout !== null) {
window.clearTimeout(this.timeout);
} }
} }
@ -30,16 +39,16 @@ export class RequestStatsComponent implements OnInit, OnDestroy {
this.requestService.getStats().subscribe( this.requestService.getStats().subscribe(
stats => this.stats = stats, stats => this.stats = stats,
err => alert(err.text) err => this.notificationsService.error('Error', err.text)
); );
} }
private runInterval() { private runInterval() {
this.interval = setInterval(() => { this.interval = window.setInterval(() => {
this.stats.remainingMilliSeconds -= 1000; this.stats.remainingMilliSeconds -= 1000;
if (this.stats.remainingMilliSeconds <= 0) { if (this.stats.remainingMilliSeconds <= 0) {
setTimeout(() => this.getStats(), this.stats.remainingMilliSeconds + 100); this.timeout = window.setTimeout(() => this.getStats(), this.stats.remainingMilliSeconds + 100);
} }
}, 1000); }, 1000);
} }

View File

@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { NotificationsService } from 'angular2-notifications';
import { UserService } from '../shared'; import { UserService } from '../shared';
import { FormReactive, USER_USERNAME, USER_PASSWORD } from '../../../shared'; import { FormReactive, USER_USERNAME, USER_PASSWORD } from '../../../shared';
@ -25,6 +27,7 @@ export class UserAddComponent extends FormReactive implements OnInit {
constructor( constructor(
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private router: Router, private router: Router,
private notificationsService: NotificationsService,
private userService: UserService private userService: UserService
) { ) {
super(); super();
@ -49,7 +52,10 @@ export class UserAddComponent extends FormReactive implements OnInit {
const { username, password } = this.form.value; const { username, password } = this.form.value;
this.userService.addUser(username, password).subscribe( this.userService.addUser(username, password).subscribe(
ok => this.router.navigate([ '/admin/users/list' ]), () => {
this.notificationsService.success('Success', `User ${username} created.`);
this.router.navigate([ '/admin/users/list' ]);
},
err => this.error = err.text err => this.error = err.text
); );

View File

@ -1,5 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { NotificationsService } from 'angular2-notifications';
import { User } from '../../../shared'; import { User } from '../../../shared';
import { UserService } from '../shared'; import { UserService } from '../shared';
@ -12,7 +14,10 @@ export class UserListComponent implements OnInit {
totalUsers: number; totalUsers: number;
users: User[]; users: User[];
constructor(private userService: UserService) {} constructor(
private notificationsService: NotificationsService,
private userService: UserService
) {}
ngOnInit() { ngOnInit() {
this.getUsers(); this.getUsers();
@ -25,7 +30,7 @@ export class UserListComponent implements OnInit {
this.totalUsers = totalUsers; this.totalUsers = totalUsers;
}, },
err => alert(err.text) err => this.notificationsService.error('Error', err.text)
); );
} }
@ -33,9 +38,12 @@ export class UserListComponent implements OnInit {
removeUser(user: User) { removeUser(user: User) {
if (confirm('Are you sure?')) { if (confirm('Are you sure?')) {
this.userService.removeUser(user).subscribe( this.userService.removeUser(user).subscribe(
() => this.getUsers(), () => {
this.notificationsService.success('Success', `User ${user.username} deleted.`);
this.getUsers();
},
err => alert(err.text) err => this.notificationsService.error('Error', err.text)
); );
} }
} }

View File

@ -1,6 +1,7 @@
import { setInterval } from 'timers'
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { NotificationsService } from 'angular2-notifications';
import { VideoAbuseService, VideoAbuse} from '../../../shared'; import { VideoAbuseService, VideoAbuse} from '../../../shared';
@Component({ @Component({
@ -11,7 +12,10 @@ import { VideoAbuseService, VideoAbuse} from '../../../shared';
export class VideoAbuseListComponent implements OnInit { export class VideoAbuseListComponent implements OnInit {
videoAbuses: VideoAbuse[]; videoAbuses: VideoAbuse[];
constructor(private videoAbuseService: VideoAbuseService) { } constructor(
private notificationsService: NotificationsService,
private videoAbuseService: VideoAbuseService
) { }
ngOnInit() { ngOnInit() {
this.getVideoAbuses(); this.getVideoAbuses();
@ -25,7 +29,7 @@ export class VideoAbuseListComponent implements OnInit {
this.videoAbuseService.getVideoAbuses().subscribe( this.videoAbuseService.getVideoAbuses().subscribe(
res => this.videoAbuses = res.videoAbuses, res => this.videoAbuses = res.videoAbuses,
err => alert(err.text) err => this.notificationsService.error('Error', err.text)
); );
} }
} }

View File

@ -1,6 +1,5 @@
<h3>Account</h3> <h3>Account</h3>
<div *ngIf="information" class="alert alert-success">{{ information }}</div>
<div *ngIf="error" class="alert alert-danger">{{ error }}</div> <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
<form role="form" (ngSubmit)="changePassword()" [formGroup]="form"> <form role="form" (ngSubmit)="changePassword()" [formGroup]="form">

View File

@ -1,8 +1,9 @@
import { } from '@angular/common';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { NotificationsService } from 'angular2-notifications';
import { AccountService } from './account.service'; import { AccountService } from './account.service';
import { FormReactive, USER_PASSWORD } from '../shared'; import { FormReactive, USER_PASSWORD } from '../shared';
@ -12,7 +13,6 @@ import { FormReactive, USER_PASSWORD } from '../shared';
}) })
export class AccountComponent extends FormReactive implements OnInit { export class AccountComponent extends FormReactive implements OnInit {
information: string = null;
error: string = null; error: string = null;
form: FormGroup; form: FormGroup;
@ -26,9 +26,10 @@ export class AccountComponent extends FormReactive implements OnInit {
}; };
constructor( constructor(
private accountService: AccountService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private router: Router private router: Router,
private notificationsService: NotificationsService,
private accountService: AccountService
) { ) {
super(); super();
} }
@ -50,7 +51,6 @@ export class AccountComponent extends FormReactive implements OnInit {
const newPassword = this.form.value['new-password']; const newPassword = this.form.value['new-password'];
const newConfirmedPassword = this.form.value['new-confirmed-password']; const newConfirmedPassword = this.form.value['new-confirmed-password'];
this.information = null;
this.error = null; this.error = null;
if (newPassword !== newConfirmedPassword) { if (newPassword !== newConfirmedPassword) {
@ -59,7 +59,7 @@ export class AccountComponent extends FormReactive implements OnInit {
} }
this.accountService.changePassword(newPassword).subscribe( this.accountService.changePassword(newPassword).subscribe(
ok => this.information = 'Password updated.', () => this.notificationsService.success('Success', 'Password updated.'),
err => this.error = err err => this.error = err
); );

View File

@ -22,6 +22,8 @@
</div> </div>
</div> </div>
<simple-notifications [options]="notificationOptions"></simple-notifications>
<footer> <footer>
PeerTube, CopyLeft 2015-2016 PeerTube, CopyLeft 2015-2016
</footer> </footer>

View File

@ -9,6 +9,19 @@ import { MetaService } from 'ng2-meta';
}) })
export class AppComponent { export class AppComponent {
notificationOptions = {
timeOut: 3000,
lastOnBottom: true,
clickToClose: true,
maxLength: 0,
maxStack: 7,
showProgressBar: false,
pauseOnHover: false,
preventDuplicates: false,
preventLastDuplicates: 'visible',
rtl: false
};
constructor( constructor(
private router: Router, private router: Router,
private metaService: MetaService, private metaService: MetaService,

View File

@ -7,6 +7,8 @@ import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap'; import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/observable/throw'; import 'rxjs/add/observable/throw';
import { NotificationsService } from 'angular2-notifications';
// Do not use the barrel (dependency loop) // Do not use the barrel (dependency loop)
import { AuthStatus } from '../../shared/auth/auth-status.model'; import { AuthStatus } from '../../shared/auth/auth-status.model';
import { AuthUser } from '../../shared/auth/auth-user.model'; import { AuthUser } from '../../shared/auth/auth-user.model';
@ -27,6 +29,7 @@ export class AuthService {
constructor( constructor(
private http: Http, private http: Http,
private notificationsService: NotificationsService,
private restExtractor: RestExtractor, private restExtractor: RestExtractor,
private router: Router private router: Router
) { ) {
@ -44,11 +47,14 @@ export class AuthService {
this.clientSecret = result.client_secret; this.clientSecret = result.client_secret;
console.log('Client credentials loaded.'); console.log('Client credentials loaded.');
}, },
error => { error => {
alert( let errorMessage = `Cannot retrieve OAuth Client credentials: ${error.text}. \n`;
`Cannot retrieve OAuth Client credentials: ${error.text}. \n` + errorMessage += 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.';
'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.'
); // We put a bigger timeout
// This is an important message
this.notificationsService.error('Error', errorMessage, { timeOut: 7000 });
} }
); );

View File

@ -3,6 +3,8 @@ import { CommonModule } from '@angular/common';
import { HttpModule } from '@angular/http'; import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { SimpleNotificationsModule } from 'angular2-notifications';
import { AuthService } from './auth'; import { AuthService } from './auth';
import { MenuComponent, MenuAdminComponent } from './menu'; import { MenuComponent, MenuAdminComponent } from './menu';
import { throwIfAlreadyLoaded } from './module-import-guard'; import { throwIfAlreadyLoaded } from './module-import-guard';
@ -11,13 +13,17 @@ import { throwIfAlreadyLoaded } from './module-import-guard';
imports: [ imports: [
CommonModule, CommonModule,
HttpModule, HttpModule,
RouterModule RouterModule,
SimpleNotificationsModule
], ],
declarations: [ declarations: [
MenuComponent, MenuComponent,
MenuAdminComponent MenuAdminComponent
], ],
exports: [ exports: [
SimpleNotificationsModule,
MenuComponent, MenuComponent,
MenuAdminComponent MenuAdminComponent
], ],

View File

@ -3,6 +3,7 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { FileUploader } from 'ng2-file-upload/ng2-file-upload'; import { FileUploader } from 'ng2-file-upload/ng2-file-upload';
import { NotificationsService } from 'angular2-notifications';
import { AuthService } from '../../core'; import { AuthService } from '../../core';
import { FormReactive, VIDEO_NAME, VIDEO_DESCRIPTION, VIDEO_TAGS } from '../../shared'; import { FormReactive, VIDEO_NAME, VIDEO_DESCRIPTION, VIDEO_TAGS } from '../../shared';
@ -38,7 +39,8 @@ export class VideoAddComponent extends FormReactive implements OnInit {
private authService: AuthService, private authService: AuthService,
private elementRef: ElementRef, private elementRef: ElementRef,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private router: Router private router: Router,
private notificationsService: NotificationsService
) { ) {
super(); super();
} }
@ -151,6 +153,8 @@ export class VideoAddComponent extends FormReactive implements OnInit {
clearInterval(interval); clearInterval(interval);
console.log('Video uploaded.'); console.log('Video uploaded.');
this.notificationsService.success('Success', 'Video uploaded.');
// Print all the videos once it's finished // Print all the videos once it's finished
this.router.navigate(['/videos/list']); this.router.navigate(['/videos/list']);

View File

@ -2,6 +2,8 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { NotificationsService } from 'angular2-notifications';
import { import {
SortField, SortField,
Video, Video,
@ -33,6 +35,7 @@ export class VideoListComponent implements OnInit, OnDestroy {
private subSearch: any; private subSearch: any;
constructor( constructor(
private notificationsService: NotificationsService,
private authService: AuthService, private authService: AuthService,
private changeDetector: ChangeDetectorRef, private changeDetector: ChangeDetectorRef,
private router: Router, private router: Router,
@ -91,7 +94,7 @@ export class VideoListComponent implements OnInit, OnDestroy {
this.loading.next(false); this.loading.next(false);
}, },
error => alert(error.text) error => this.notificationsService.error('Error', error.text)
); );
} }
@ -107,6 +110,7 @@ export class VideoListComponent implements OnInit, OnDestroy {
} }
onRemoved(video: Video) { onRemoved(video: Video) {
this.notificationsService.success('Success', `Video ${video.name} deleted.`);
this.getVideos(); this.getVideos();
} }

View File

@ -1,5 +1,7 @@
import { Component, Input, Output, EventEmitter } from '@angular/core'; import { Component, Input, Output, EventEmitter } from '@angular/core';
import { NotificationsService } from 'angular2-notifications';
import { SortField, Video, VideoService } from '../shared'; import { SortField, Video, VideoService } from '../shared';
import { User } from '../../shared'; import { User } from '../../shared';
@ -18,7 +20,10 @@ export class VideoMiniatureComponent {
hovering = false; hovering = false;
constructor(private videoService: VideoService) {} constructor(
private notificationsService: NotificationsService,
private videoService: VideoService
) {}
displayRemoveIcon() { displayRemoveIcon() {
return this.hovering && this.video.isRemovableBy(this.user); return this.hovering && this.video.isRemovableBy(this.user);
@ -36,7 +41,8 @@ export class VideoMiniatureComponent {
if (confirm('Do you really want to remove this video?')) { if (confirm('Do you really want to remove this video?')) {
this.videoService.removeVideo(id).subscribe( this.videoService.removeVideo(id).subscribe(
status => this.removed.emit(true), status => this.removed.emit(true),
error => alert(error.text)
error => this.notificationsService.error('Error', error.text)
); );
} }
} }

View File

@ -2,6 +2,7 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { ModalDirective } from 'ng2-bootstrap/modal'; import { ModalDirective } from 'ng2-bootstrap/modal';
import { NotificationsService } from 'angular2-notifications';
import { FormReactive, VideoAbuseService, VIDEO_ABUSE_REASON } from '../../shared'; import { FormReactive, VideoAbuseService, VIDEO_ABUSE_REASON } from '../../shared';
import { Video, VideoService } from '../shared'; import { Video, VideoService } from '../shared';
@ -26,7 +27,8 @@ export class VideoReportComponent extends FormReactive implements OnInit {
constructor( constructor(
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private videoAbuseService: VideoAbuseService private videoAbuseService: VideoAbuseService,
private notificationsService: NotificationsService
) { ) {
super(); super();
} }
@ -56,13 +58,12 @@ export class VideoReportComponent extends FormReactive implements OnInit {
this.videoAbuseService.reportVideo(this.video.id, reason) this.videoAbuseService.reportVideo(this.video.id, reason)
.subscribe( .subscribe(
// TODO: move alert to beautiful notifications () => {
ok => { this.notificationsService.success('Success', 'Video reported.');
alert('Video reported.');
this.hide(); this.hide();
}, },
err => alert(err.text) err => this.notificationsService.error('Error', err.text);
) )
} }
} }

View File

@ -1,8 +1,9 @@
import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { MetaService } from 'ng2-meta';
import * as videojs from 'video.js'; import * as videojs from 'video.js';
import { MetaService } from 'ng2-meta';
import { NotificationsService } from 'angular2-notifications';
import { AuthService } from '../../core'; import { AuthService } from '../../core';
import { VideoMagnetComponent } from './video-magnet.component'; import { VideoMagnetComponent } from './video-magnet.component';
@ -45,7 +46,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
private videoService: VideoService, private videoService: VideoService,
private metaService: MetaService, private metaService: MetaService,
private webTorrentService: WebTorrentService, private webTorrentService: WebTorrentService,
private authService: AuthService private authService: AuthService,
private notificationsService: NotificationsService
) {} ) {}
ngOnInit() { ngOnInit() {
@ -117,7 +119,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
console.log('Added ' + this.video.magnetUri + '.'); console.log('Added ' + this.video.magnetUri + '.');
torrent.files[0].renderTo(this.playerElement, { autoplay: true }, (err) => { torrent.files[0].renderTo(this.playerElement, { autoplay: true }, (err) => {
if (err) { if (err) {
alert('Cannot append the file.'); this.notificationsService.error('Error', 'Cannot append the file in the video element.');
console.error(err); console.error(err);
} }
}); });