Client: better notifications for a beautiful world
authorChocobozzz <florian.bigard@gmail.com>
Fri, 27 Jan 2017 15:14:11 +0000 (16:14 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Fri, 27 Jan 2017 15:14:11 +0000 (16:14 +0100)
18 files changed:
client/package.json
client/src/app/+admin/friends/friend-add/friend-add.component.ts
client/src/app/+admin/friends/friend-list/friend-list.component.ts
client/src/app/+admin/requests/request-stats/request-stats.component.ts
client/src/app/+admin/users/user-add/user-add.component.ts
client/src/app/+admin/users/user-list/user-list.component.ts
client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
client/src/app/account/account.component.html
client/src/app/account/account.component.ts
client/src/app/app.component.html
client/src/app/app.component.ts
client/src/app/core/auth/auth.service.ts
client/src/app/core/core.module.ts
client/src/app/videos/video-add/video-add.component.ts
client/src/app/videos/video-list/video-list.component.ts
client/src/app/videos/video-list/video-miniature.component.ts
client/src/app/videos/video-watch/video-report.component.ts
client/src/app/videos/video-watch/video-watch.component.ts

index 1a04be5bc9b410264ce655b7f785675bd9a5753f..bd6cb03e73eac7dd0aa5ece679ecfe3d44cd0a91 100644 (file)
@@ -36,6 +36,7 @@
     "@types/videojs": "0.0.30",
     "@types/webpack": "^2.0.0",
     "angular-pipes": "^5.0.0",
+    "angular2-notifications": "^0.4.49",
     "angular2-template-loader": "^0.6.0",
     "assets-webpack-plugin": "^3.4.0",
     "awesome-typescript-loader": "~3.0.0-beta.17",
index 014252011549b205d027d1523fcda8c93d4d2bd1..a271970ae70c7e60a20963f4023911ab924637bd 100644 (file)
@@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
 import { FormControl, FormGroup } from '@angular/forms';
 import { Router } from '@angular/router';
 
+import { NotificationsService } from 'angular2-notifications';
+
 import { validateHost } from '../../../shared';
 import { FriendService } from '../shared';
 
@@ -15,7 +17,11 @@ export class FriendAddComponent implements OnInit {
   hosts = [ ];
   error: string = null;
 
-  constructor(private router: Router, private friendService: FriendService) {}
+  constructor(
+    private router: Router,
+    private notificationsService: NotificationsService,
+    private friendService: FriendService
+  ) {}
 
   ngOnInit() {
     this.form = new FormGroup({});
@@ -83,10 +89,11 @@ export class FriendAddComponent implements OnInit {
 
     this.friendService.makeFriends(notEmptyHosts).subscribe(
       status => {
-        alert('Make friends request sent!');
+        this.notificationsService.success('Sucess', 'Make friends request sent!');
         this.router.navigate([ '/admin/friends/list' ]);
       },
-      error => alert(error.text)
+
+      err => this.notificationsService.error('Error', err.text)
     );
   }
 
index bec10162cd1e7443f5b1df30cafe2a5efcac8c13..700ea7a69fa5175d8e6c425b32630d1363192a68 100644 (file)
@@ -1,5 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 
+import { NotificationsService } from 'angular2-notifications';
+
 import { Friend, FriendService } from '../shared';
 
 @Component({
@@ -10,7 +12,10 @@ import { Friend, FriendService } from '../shared';
 export class FriendListComponent implements OnInit {
   friends: Friend[];
 
-  constructor(private friendService: FriendService) {  }
+  constructor(
+    private notificationsService: NotificationsService,
+    private friendService: FriendService
+  ) {  }
 
   ngOnInit() {
     this.getFriends();
@@ -21,10 +26,12 @@ export class FriendListComponent implements OnInit {
 
     this.friendService.quitFriends().subscribe(
       status => {
-        alert('Quit friends!');
+        this.notificationsService.success('Sucess', 'Friends left!');
+
         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(
       res => this.friends = res.friends,
 
-      err => alert(err.text)
+      err => this.notificationsService.error('Error', err.text)
     );
   }
 }
index 23b8367792329efe36696bbb38480a36c93bf622..18855a5f8e0469871a1c5db4e4a3ca38695abe79 100644 (file)
@@ -1,6 +1,7 @@
-import { setInterval } from 'timers'
 import { Component, OnInit, OnDestroy } from '@angular/core';
 
+import { NotificationsService } from 'angular2-notifications';
+
 import { RequestService, RequestStats } from '../shared';
 
 @Component({
@@ -11,9 +12,13 @@ import { RequestService, RequestStats } from '../shared';
 export class RequestStatsComponent implements OnInit, OnDestroy {
   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() {
     this.getStats();
@@ -21,8 +26,12 @@ export class RequestStatsComponent implements OnInit, OnDestroy {
   }
 
   ngOnDestroy() {
-    if (this.stats !== null && this.stats.secondsInterval !== null) {
-      clearInterval(this.interval);
+    if (this.interval !== null) {
+      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(
       stats => this.stats = stats,
 
-      err => alert(err.text)
+      err => this.notificationsService.error('Error', err.text)
     );
   }
 
   private runInterval() {
-    this.interval = setInterval(() => {
+    this.interval = window.setInterval(() => {
       this.stats.remainingMilliSeconds -= 1000;
 
       if (this.stats.remainingMilliSeconds <= 0) {
-        setTimeout(() => this.getStats(), this.stats.remainingMilliSeconds + 100);
+        this.timeout = window.setTimeout(() => this.getStats(), this.stats.remainingMilliSeconds + 100);
       }
     }, 1000);
   }
index ab96fb01dac65e3883c37e5e5d1ff11d3942a60c..a851fee44f84938f019a763fec45c107525dc7ed 100644 (file)
@@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
 import { FormBuilder, FormGroup } from '@angular/forms';
 import { Router } from '@angular/router';
 
+import { NotificationsService } from 'angular2-notifications';
+
 import { UserService } from '../shared';
 import { FormReactive, USER_USERNAME, USER_PASSWORD } from '../../../shared';
 
@@ -25,6 +27,7 @@ export class UserAddComponent extends FormReactive implements OnInit {
   constructor(
     private formBuilder: FormBuilder,
     private router: Router,
+    private notificationsService: NotificationsService,
     private userService: UserService
   ) {
     super();
@@ -49,7 +52,10 @@ export class UserAddComponent extends FormReactive implements OnInit {
     const { username, password } = this.form.value;
 
     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
     );
index 03f4e5c0aa0f31e7f50fc0268c94e7ac0a49b2b9..ca08ed3050ea41752b54111136ff584993d0441e 100644 (file)
@@ -1,5 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 
+import { NotificationsService } from 'angular2-notifications';
+
 import { User } from '../../../shared';
 import { UserService } from '../shared';
 
@@ -12,7 +14,10 @@ export class UserListComponent implements OnInit {
   totalUsers: number;
   users: User[];
 
-  constructor(private userService: UserService) {}
+  constructor(
+    private notificationsService: NotificationsService,
+    private userService: UserService
+  ) {}
 
   ngOnInit() {
     this.getUsers();
@@ -25,7 +30,7 @@ export class UserListComponent implements OnInit {
         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) {
     if (confirm('Are you sure?')) {
       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)
       );
     }
   }
index de58bba3da8a3b9401b1d26bba6219742d8d6b92..cfd9151b08050811ba4b731b5a4f4fbaa3784c90 100644 (file)
@@ -1,6 +1,7 @@
-import { setInterval } from 'timers'
 import { Component, OnInit } from '@angular/core';
 
+import { NotificationsService } from 'angular2-notifications';
+
 import { VideoAbuseService, VideoAbuse} from '../../../shared';
 
 @Component({
@@ -11,7 +12,10 @@ import { VideoAbuseService, VideoAbuse} from '../../../shared';
 export class VideoAbuseListComponent implements OnInit {
   videoAbuses: VideoAbuse[];
 
-  constructor(private videoAbuseService: VideoAbuseService) {  }
+  constructor(
+    private notificationsService: NotificationsService,
+    private videoAbuseService: VideoAbuseService
+  ) {  }
 
   ngOnInit() {
     this.getVideoAbuses();
@@ -25,7 +29,7 @@ export class VideoAbuseListComponent implements OnInit {
     this.videoAbuseService.getVideoAbuses().subscribe(
       res => this.videoAbuses = res.videoAbuses,
 
-      err => alert(err.text)
+      err => this.notificationsService.error('Error', err.text)
     );
   }
 }
index 5a8847acda812bf45b0970197ba72bf86451da70..2fbb5a90882171b5a7f825981feca32aa8b66c80 100644 (file)
@@ -1,6 +1,5 @@
 <h3>Account</h3>
 
-<div *ngIf="information" class="alert alert-success">{{ information }}</div>
 <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
 
 <form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
index 851eaf198e1ae73dc9b4d84e6d95c815a7d76e80..9b6b5fbf421123f282cc1e2e1a8d065d22d346a7 100644 (file)
@@ -1,8 +1,9 @@
-import {  } from '@angular/common';
 import { Component, OnInit } from '@angular/core';
 import { FormBuilder, FormGroup } from '@angular/forms';
 import { Router } from '@angular/router';
 
+import { NotificationsService } from 'angular2-notifications';
+
 import { AccountService } from './account.service';
 import { FormReactive, USER_PASSWORD } from '../shared';
 
@@ -12,7 +13,6 @@ import { FormReactive, USER_PASSWORD } from '../shared';
 })
 
 export class AccountComponent extends FormReactive implements OnInit {
-  information: string = null;
   error: string = null;
 
   form: FormGroup;
@@ -26,9 +26,10 @@ export class AccountComponent extends FormReactive implements OnInit {
   };
 
   constructor(
-    private accountService: AccountService,
     private formBuilder: FormBuilder,
-    private router: Router
+    private router: Router,
+    private notificationsService: NotificationsService,
+    private accountService: AccountService
   ) {
     super();
   }
@@ -50,7 +51,6 @@ export class AccountComponent extends FormReactive implements OnInit {
     const newPassword = this.form.value['new-password'];
     const newConfirmedPassword = this.form.value['new-confirmed-password'];
 
-    this.information = null;
     this.error = null;
 
     if (newPassword !== newConfirmedPassword) {
@@ -59,7 +59,7 @@ export class AccountComponent extends FormReactive implements OnInit {
     }
 
     this.accountService.changePassword(newPassword).subscribe(
-      ok => this.information = 'Password updated.',
+      () => this.notificationsService.success('Success', 'Password updated.'),
 
       err => this.error = err
     );
index 04c32f596344624014b008ccc615d24a1d3cff3e..95a02583216267a7a4569fbe222a1f8b2550224c 100644 (file)
@@ -22,6 +22,8 @@
     </div>
   </div>
 
+  <simple-notifications [options]="notificationOptions"></simple-notifications>
+
   <footer>
     PeerTube, CopyLeft 2015-2016
   </footer>
index ce4fc04fffc85c4e9adb58b5b28902fab7abba64..f487e6c482dab245824e8c7efa8f325d1f0ec715 100644 (file)
@@ -9,6 +9,19 @@ import { MetaService } from 'ng2-meta';
 })
 
 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(
     private router: Router,
     private metaService: MetaService,
index 06ffa1a42b6f3a3fc85a90ecbe8e37b7855e6185..a56adbbad913196370524f98dff33a89484db890 100644 (file)
@@ -7,6 +7,8 @@ import 'rxjs/add/operator/map';
 import 'rxjs/add/operator/mergeMap';
 import 'rxjs/add/observable/throw';
 
+import { NotificationsService } from 'angular2-notifications';
+
 // Do not use the barrel (dependency loop)
 import { AuthStatus } from '../../shared/auth/auth-status.model';
 import { AuthUser } from '../../shared/auth/auth-user.model';
@@ -27,6 +29,7 @@ export class AuthService {
 
   constructor(
     private http: Http,
+    private notificationsService: NotificationsService,
     private restExtractor: RestExtractor,
     private router: Router
    ) {
@@ -44,11 +47,14 @@ export class AuthService {
           this.clientSecret = result.client_secret;
           console.log('Client credentials loaded.');
         },
+
         error => {
-          alert(
-            `Cannot retrieve OAuth Client credentials: ${error.text}. \n` +
-            'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.'
-          );
+          let errorMessage = `Cannot retrieve OAuth Client credentials: ${error.text}. \n`;
+          errorMessage += '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 });
         }
       );
 
index 09a6f92f5542f71e6a3e1be6269a06959d95b0d4..48fec2d439a0bed607f69cc09077579f237e7d45 100644 (file)
@@ -3,6 +3,8 @@ import { CommonModule } from '@angular/common';
 import { HttpModule } from '@angular/http';
 import { RouterModule } from '@angular/router';
 
+import { SimpleNotificationsModule } from 'angular2-notifications';
+
 import { AuthService } from './auth';
 import { MenuComponent, MenuAdminComponent } from './menu';
 import { throwIfAlreadyLoaded } from './module-import-guard';
@@ -11,13 +13,17 @@ import { throwIfAlreadyLoaded } from './module-import-guard';
   imports: [
     CommonModule,
     HttpModule,
-    RouterModule
+    RouterModule,
+
+    SimpleNotificationsModule
   ],
   declarations: [
     MenuComponent,
     MenuAdminComponent
   ],
   exports: [
+    SimpleNotificationsModule,
+
     MenuComponent,
     MenuAdminComponent
   ],
index 1e700ae48df3d0d66ea717865f0bfd052a866db1..cd6bb99892ca2752cff29a3ed920131f4dc377ce 100644 (file)
@@ -3,6 +3,7 @@ import { FormBuilder, FormGroup } from '@angular/forms';
 import { Router } from '@angular/router';
 
 import { FileUploader } from 'ng2-file-upload/ng2-file-upload';
+import { NotificationsService } from 'angular2-notifications';
 
 import { AuthService } from '../../core';
 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 elementRef: ElementRef,
     private formBuilder: FormBuilder,
-    private router: Router
+    private router: Router,
+    private notificationsService: NotificationsService
   ) {
     super();
   }
@@ -151,6 +153,8 @@ export class VideoAddComponent extends FormReactive implements OnInit {
       clearInterval(interval);
 
       console.log('Video uploaded.');
+      this.notificationsService.success('Success', 'Video uploaded.');
+
 
       // Print all the videos once it's finished
       this.router.navigate(['/videos/list']);
index 6c42ba5be573db10c429c244b9cd3e1550228e7f..b3780f8b66ead38076bc5ad13aa9ecf004891ba7 100644 (file)
@@ -2,6 +2,8 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
 
+import { NotificationsService } from 'angular2-notifications';
+
 import {
   SortField,
   Video,
@@ -33,6 +35,7 @@ export class VideoListComponent implements OnInit, OnDestroy {
   private subSearch: any;
 
   constructor(
+    private notificationsService: NotificationsService,
     private authService: AuthService,
     private changeDetector: ChangeDetectorRef,
     private router: Router,
@@ -91,7 +94,7 @@ export class VideoListComponent implements OnInit, OnDestroy {
 
         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) {
+    this.notificationsService.success('Success', `Video ${video.name} deleted.`);
     this.getVideos();
   }
 
index 398d2db7507a2d4f4f2f6df78e9e99ca5b0488e8..ca4afc451bb7c2fc858b8c82a546b57fc718f4c4 100644 (file)
@@ -1,5 +1,7 @@
 import { Component, Input, Output, EventEmitter } from '@angular/core';
 
+import { NotificationsService } from 'angular2-notifications';
+
 import { SortField, Video, VideoService } from '../shared';
 import { User } from '../../shared';
 
@@ -18,7 +20,10 @@ export class VideoMiniatureComponent {
 
   hovering = false;
 
-  constructor(private videoService: VideoService) {}
+  constructor(
+    private notificationsService: NotificationsService,
+    private videoService: VideoService
+  ) {}
 
   displayRemoveIcon() {
     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?')) {
       this.videoService.removeVideo(id).subscribe(
         status => this.removed.emit(true),
-        error => alert(error.text)
+
+        error => this.notificationsService.error('Error', error.text)
       );
     }
   }
index 7a125f53e573dd548c7d9fca9a79ecd7d038f119..19a7af1482c3c3ff9f429450004c36c13bf7b756 100644 (file)
@@ -2,6 +2,7 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core';
 import { FormBuilder, FormGroup } from '@angular/forms';
 
 import { ModalDirective } from 'ng2-bootstrap/modal';
+import { NotificationsService } from 'angular2-notifications';
 
 import { FormReactive, VideoAbuseService, VIDEO_ABUSE_REASON } from '../../shared';
 import { Video, VideoService } from '../shared';
@@ -26,7 +27,8 @@ export class VideoReportComponent extends FormReactive implements OnInit {
 
   constructor(
     private formBuilder: FormBuilder,
-    private videoAbuseService: VideoAbuseService
+    private videoAbuseService: VideoAbuseService,
+    private notificationsService: NotificationsService
    ) {
     super();
   }
@@ -56,13 +58,12 @@ export class VideoReportComponent extends FormReactive implements OnInit {
 
     this.videoAbuseService.reportVideo(this.video.id, reason)
                      .subscribe(
-                       // TODO: move alert to beautiful notifications
-                       ok => {
-                         alert('Video reported.');
+                       () => {
+                         this.notificationsService.success('Success', 'Video reported.');
                          this.hide();
                        },
 
-                       err => alert(err.text)
+                       err => this.notificationsService.error('Error', err.text);
                       )
   }
 }
index 5cd7550be6b0333b82a9a2e32d540ac139cb3786..c27133f74ffd15484363f0d76564e7cfbb28f875 100644 (file)
@@ -1,8 +1,9 @@
 import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 
-import { MetaService } from 'ng2-meta';
 import * as videojs from 'video.js';
+import { MetaService } from 'ng2-meta';
+import { NotificationsService } from 'angular2-notifications';
 
 import { AuthService } from '../../core';
 import { VideoMagnetComponent } from './video-magnet.component';
@@ -45,7 +46,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     private videoService: VideoService,
     private metaService: MetaService,
     private webTorrentService: WebTorrentService,
-    private authService: AuthService
+    private authService: AuthService,
+    private notificationsService: NotificationsService
   ) {}
 
   ngOnInit() {
@@ -117,7 +119,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
       console.log('Added ' + this.video.magnetUri + '.');
       torrent.files[0].renderTo(this.playerElement, { autoplay: true }, (err) => {
         if (err) {
-          alert('Cannot append the file.');
+          this.notificationsService.error('Error', 'Cannot append the file in the video element.');
           console.error(err);
         }
       });