From e62f6ef741c8d14817e321c554796ad64ea7ae1b Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 27 Jul 2016 21:15:13 +0200 Subject: Client: fix login state when logout --- client/src/app/shared/auth/auth.service.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'client/src/app') diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts index 667fbeb1f..4c08e24c0 100644 --- a/client/src/app/shared/auth/auth.service.ts +++ b/client/src/app/shared/auth/auth.service.ts @@ -107,6 +107,8 @@ export class AuthService { // TODO: make an HTTP request to revoke the tokens this.user = null; User.flush(); + + this.setStatus(AuthStatus.LoggedOut); } refreshAccessToken() { -- cgit v1.2.3 From 6606150c49f587bc7eb0ecec4263ce7fbb18bf15 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 5 Aug 2016 16:09:39 +0200 Subject: Server: move clients in its own file --- client/src/app/shared/auth/auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'client/src/app') diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts index 4c08e24c0..6a5b19ffe 100644 --- a/client/src/app/shared/auth/auth.service.ts +++ b/client/src/app/shared/auth/auth.service.ts @@ -8,7 +8,7 @@ import { User } from './user.model'; @Injectable() export class AuthService { - private static BASE_CLIENT_URL = '/api/v1/users/client'; + private static BASE_CLIENT_URL = '/api/v1/clients/local'; private static BASE_TOKEN_URL = '/api/v1/users/token'; loginChangedSource: Observable; -- cgit v1.2.3 From 629d8d6f70cf83b55011dff53bfe1c4a95ac3433 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 5 Aug 2016 18:04:08 +0200 Subject: Client: implement password change --- client/src/app/account/account.component.html | 27 +++++++++++++++ client/src/app/account/account.component.ts | 45 +++++++++++++++++++++++++ client/src/app/account/account.routes.ts | 5 +++ client/src/app/account/account.service.ts | 19 +++++++++++ client/src/app/account/index.ts | 2 ++ client/src/app/app.component.html | 15 +++++++-- client/src/app/app.routes.ts | 2 ++ client/src/app/shared/auth/auth-http.service.ts | 6 ++-- client/src/app/shared/auth/auth.service.ts | 37 +++++++++++++++----- client/src/app/shared/auth/user.model.ts | 23 ++++++++++--- 10 files changed, 165 insertions(+), 16 deletions(-) create mode 100644 client/src/app/account/account.component.html create mode 100644 client/src/app/account/account.component.ts create mode 100644 client/src/app/account/account.routes.ts create mode 100644 client/src/app/account/account.service.ts create mode 100644 client/src/app/account/index.ts (limited to 'client/src/app') diff --git a/client/src/app/account/account.component.html b/client/src/app/account/account.component.html new file mode 100644 index 000000000..ad8f690bd --- /dev/null +++ b/client/src/app/account/account.component.html @@ -0,0 +1,27 @@ +

Account

+ +
{{ information }}
+
{{ error }}
+ +
+
+ + +
+ The password should have more than 5 characters +
+
+ +
+ + +
+ + +
diff --git a/client/src/app/account/account.component.ts b/client/src/app/account/account.component.ts new file mode 100644 index 000000000..5c42103f8 --- /dev/null +++ b/client/src/app/account/account.component.ts @@ -0,0 +1,45 @@ +import { Control, ControlGroup, Validators } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { AccountService } from './account.service'; + +@Component({ + selector: 'my-account', + template: require('./account.component.html'), + providers: [ AccountService ] +}) + +export class AccountComponent implements OnInit { + changePasswordForm: ControlGroup; + information: string = null; + error: string = null; + + constructor( + private accountService: AccountService, + private router: Router + ) {} + + ngOnInit() { + this.changePasswordForm = new ControlGroup({ + newPassword: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])), + newConfirmedPassword: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])), + }); + } + + changePassword(newPassword: string, newConfirmedPassword: string) { + this.information = null; + this.error = null; + + if (newPassword !== newConfirmedPassword) { + this.error = 'The new password and the confirmed password do not correspond.'; + return; + } + + this.accountService.changePassword(newPassword).subscribe( + ok => this.information = 'Password updated.', + + err => this.error = err + ); + } +} diff --git a/client/src/app/account/account.routes.ts b/client/src/app/account/account.routes.ts new file mode 100644 index 000000000..e348c6ebe --- /dev/null +++ b/client/src/app/account/account.routes.ts @@ -0,0 +1,5 @@ +import { AccountComponent } from './account.component'; + +export const AccountRoutes = [ + { path: 'account', component: AccountComponent } +]; diff --git a/client/src/app/account/account.service.ts b/client/src/app/account/account.service.ts new file mode 100644 index 000000000..19b4e0624 --- /dev/null +++ b/client/src/app/account/account.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; + +import { AuthHttp, AuthService } from '../shared'; + +@Injectable() +export class AccountService { + private static BASE_USERS_URL = '/api/v1/users/'; + + constructor(private authHttp: AuthHttp, private authService: AuthService) { } + + changePassword(newPassword: string) { + const url = AccountService.BASE_USERS_URL + this.authService.getUser().id; + const body = { + password: newPassword + }; + + return this.authHttp.put(url, body); + } +} diff --git a/client/src/app/account/index.ts b/client/src/app/account/index.ts new file mode 100644 index 000000000..7445003fd --- /dev/null +++ b/client/src/app/account/index.ts @@ -0,0 +1,2 @@ +export * from './account.component'; +export * from './account.routes'; diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index f2acffea4..ea4b31421 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -18,9 +18,20 @@
+ + + Login + + + + + Logout + +
+ +
diff --git a/client/src/app/app.routes.ts b/client/src/app/app.routes.ts index 59ef4ce55..1c414038d 100644 --- a/client/src/app/app.routes.ts +++ b/client/src/app/app.routes.ts @@ -1,5 +1,6 @@ import { RouterConfig } from '@angular/router'; +import { AccountRoutes } from './account'; import { LoginRoutes } from './login'; import { VideosRoutes } from './videos'; @@ -10,6 +11,7 @@ export const routes: RouterConfig = [ pathMatch: 'full' }, + ...AccountRoutes, ...LoginRoutes, ...VideosRoutes ]; diff --git a/client/src/app/shared/auth/auth-http.service.ts b/client/src/app/shared/auth/auth-http.service.ts index 9c7ef4389..55bb501e6 100644 --- a/client/src/app/shared/auth/auth-http.service.ts +++ b/client/src/app/shared/auth/auth-http.service.ts @@ -49,16 +49,18 @@ export class AuthHttp extends Http { return this.request(url, options); } - post(url: string, options?: RequestOptionsArgs): Observable { + post(url: string, body: any, options?: RequestOptionsArgs): Observable { if (!options) options = {}; options.method = RequestMethod.Post; + options.body = body; return this.request(url, options); } - put(url: string, options?: RequestOptionsArgs): Observable { + put(url: string, body: any, options?: RequestOptionsArgs): Observable { if (!options) options = {}; options.method = RequestMethod.Put; + options.body = body; return this.request(url, options); } diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts index 6a5b19ffe..24d1a4fa2 100644 --- a/client/src/app/shared/auth/auth.service.ts +++ b/client/src/app/shared/auth/auth.service.ts @@ -10,6 +10,7 @@ import { User } from './user.model'; export class AuthService { private static BASE_CLIENT_URL = '/api/v1/clients/local'; private static BASE_TOKEN_URL = '/api/v1/users/token'; + private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me'; loginChangedSource: Observable; @@ -99,6 +100,7 @@ export class AuthService { res.username = username; return res; }) + .flatMap(res => this.fetchUserInformations(res)) .map(res => this.handleLogin(res)) .catch(this.handleError); } @@ -136,31 +138,50 @@ export class AuthService { .catch(this.handleError); } - private setStatus(status: AuthStatus) { - this.loginChanged.next(status); + private fetchUserInformations (obj: any) { + // Do not call authHttp here to avoid circular dependencies headaches + + const headers = new Headers(); + headers.set('Authorization', `Bearer ${obj.access_token}`); + + return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers }) + .map(res => res.json()) + .map(res => { + obj.id = res.id; + obj.role = res.role; + return obj; + } + ); + } + + private handleError (error: Response) { + console.error(error); + return Observable.throw(error.json() || { error: 'Server error' }); } private handleLogin (obj: any) { + const id = obj.id; const username = obj.username; + const role = obj.role; const hash_tokens = { access_token: obj.access_token, token_type: obj.token_type, refresh_token: obj.refresh_token }; - this.user = new User(username, hash_tokens); + this.user = new User(id, username, role, hash_tokens); this.user.save(); this.setStatus(AuthStatus.LoggedIn); } - private handleError (error: Response) { - console.error(error); - return Observable.throw(error.json() || { error: 'Server error' }); - } - private handleRefreshToken (obj: any) { this.user.refreshTokens(obj.access_token, obj.refresh_token); this.user.save(); } + + private setStatus(status: AuthStatus) { + this.loginChanged.next(status); + } + } diff --git a/client/src/app/shared/auth/user.model.ts b/client/src/app/shared/auth/user.model.ts index 98852f835..e486873ab 100644 --- a/client/src/app/shared/auth/user.model.ts +++ b/client/src/app/shared/auth/user.model.ts @@ -1,15 +1,24 @@ export class User { private static KEYS = { + ID: 'id', + ROLE: 'role', USERNAME: 'username' }; + id: string; + role: string; username: string; tokens: Tokens; static load() { const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME); if (usernameLocalStorage) { - return new User(localStorage.getItem(this.KEYS.USERNAME), Tokens.load()); + return new User( + localStorage.getItem(this.KEYS.ID), + localStorage.getItem(this.KEYS.USERNAME), + localStorage.getItem(this.KEYS.ROLE), + Tokens.load() + ); } return null; @@ -17,11 +26,15 @@ export class User { static flush() { localStorage.removeItem(this.KEYS.USERNAME); + localStorage.removeItem(this.KEYS.ID); + localStorage.removeItem(this.KEYS.ROLE); Tokens.flush(); } - constructor(username: string, hash_tokens: any) { + constructor(id: string, username: string, role: string, hash_tokens: any) { + this.id = id; this.username = username; + this.role = role; this.tokens = new Tokens(hash_tokens); } @@ -43,12 +56,14 @@ export class User { } save() { - localStorage.setItem('username', this.username); + localStorage.setItem(User.KEYS.ID, this.id); + localStorage.setItem(User.KEYS.USERNAME, this.username); + localStorage.setItem(User.KEYS.ROLE, this.role); this.tokens.save(); } } -// Private class used only by User +// Private class only used by User class Tokens { private static KEYS = { ACCESS_TOKEN: 'access_token', -- cgit v1.2.3 From 66af9ee16d5b61bb707f359b750b25f2faff306c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 5 Aug 2016 18:05:27 +0200 Subject: Client: Redirect user to home page after logout --- client/src/app/app.component.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'client/src/app') diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index b7a3d7c58..5764f24ca 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -47,6 +47,8 @@ export class AppComponent { logout() { this.authService.logout(); + // Redirect to home page + this.router.navigate(['/videos/list']); } makeFriends() { -- cgit v1.2.3 From 7da18e4420c4b71a8ecfda07f39324fbfec081c3 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 9 Aug 2016 21:45:21 +0200 Subject: Client: add user management --- client/src/app/admin/admin.component.ts | 10 ++ client/src/app/admin/admin.routes.ts | 14 +++ client/src/app/admin/index.ts | 3 + client/src/app/admin/users/index.ts | 5 + client/src/app/admin/users/shared/index.ts | 1 + client/src/app/admin/users/shared/user.service.ts | 49 +++++++++ client/src/app/admin/users/user-add/index.ts | 1 + .../admin/users/user-add/user-add.component.html | 29 +++++ .../app/admin/users/user-add/user-add.component.ts | 33 ++++++ client/src/app/admin/users/user-list/index.ts | 1 + .../admin/users/user-list/user-list.component.html | 24 +++++ .../admin/users/user-list/user-list.component.scss | 7 ++ .../admin/users/user-list/user-list.component.ts | 44 ++++++++ client/src/app/admin/users/users.component.ts | 13 +++ client/src/app/admin/users/users.routes.ts | 27 +++++ client/src/app/app.component.html | 7 +- client/src/app/app.component.ts | 4 + client/src/app/app.routes.ts | 3 +- client/src/app/shared/auth/auth-user.model.ts | 120 +++++++++++++++++++++ client/src/app/shared/auth/auth.service.ts | 20 ++-- client/src/app/shared/auth/index.ts | 2 +- client/src/app/shared/auth/user.model.ts | 118 -------------------- client/src/app/shared/index.ts | 1 + client/src/app/shared/users/index.ts | 1 + client/src/app/shared/users/user.model.ts | 15 +++ .../app/videos/video-list/video-list.component.ts | 6 +- 26 files changed, 427 insertions(+), 131 deletions(-) create mode 100644 client/src/app/admin/admin.component.ts create mode 100644 client/src/app/admin/admin.routes.ts create mode 100644 client/src/app/admin/index.ts create mode 100644 client/src/app/admin/users/index.ts create mode 100644 client/src/app/admin/users/shared/index.ts create mode 100644 client/src/app/admin/users/shared/user.service.ts create mode 100644 client/src/app/admin/users/user-add/index.ts create mode 100644 client/src/app/admin/users/user-add/user-add.component.html create mode 100644 client/src/app/admin/users/user-add/user-add.component.ts create mode 100644 client/src/app/admin/users/user-list/index.ts create mode 100644 client/src/app/admin/users/user-list/user-list.component.html create mode 100644 client/src/app/admin/users/user-list/user-list.component.scss create mode 100644 client/src/app/admin/users/user-list/user-list.component.ts create mode 100644 client/src/app/admin/users/users.component.ts create mode 100644 client/src/app/admin/users/users.routes.ts create mode 100644 client/src/app/shared/auth/auth-user.model.ts delete mode 100644 client/src/app/shared/auth/user.model.ts create mode 100644 client/src/app/shared/users/index.ts create mode 100644 client/src/app/shared/users/user.model.ts (limited to 'client/src/app') diff --git a/client/src/app/admin/admin.component.ts b/client/src/app/admin/admin.component.ts new file mode 100644 index 000000000..82f2529ec --- /dev/null +++ b/client/src/app/admin/admin.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; +import { ROUTER_DIRECTIVES } from '@angular/router'; + +@Component({ + template: '', + directives: [ ROUTER_DIRECTIVES ] +}) + +export class AdminComponent { +} diff --git a/client/src/app/admin/admin.routes.ts b/client/src/app/admin/admin.routes.ts new file mode 100644 index 000000000..d375a86af --- /dev/null +++ b/client/src/app/admin/admin.routes.ts @@ -0,0 +1,14 @@ +import { RouterConfig } from '@angular/router'; + +import { AdminComponent } from './admin.component'; +import { UsersRoutes } from './users'; + +export const AdminRoutes: RouterConfig = [ + { + path: 'admin', + component: AdminComponent, + children: [ + ...UsersRoutes + ] + } +]; diff --git a/client/src/app/admin/index.ts b/client/src/app/admin/index.ts new file mode 100644 index 000000000..3b0540818 --- /dev/null +++ b/client/src/app/admin/index.ts @@ -0,0 +1,3 @@ +export * from './users'; +export * from './admin.component'; +export * from './admin.routes'; diff --git a/client/src/app/admin/users/index.ts b/client/src/app/admin/users/index.ts new file mode 100644 index 000000000..e98a81f62 --- /dev/null +++ b/client/src/app/admin/users/index.ts @@ -0,0 +1,5 @@ +export * from './shared'; +export * from './user-add'; +export * from './user-list'; +export * from './users.component'; +export * from './users.routes'; diff --git a/client/src/app/admin/users/shared/index.ts b/client/src/app/admin/users/shared/index.ts new file mode 100644 index 000000000..e17ee5c7a --- /dev/null +++ b/client/src/app/admin/users/shared/index.ts @@ -0,0 +1 @@ +export * from './user.service'; diff --git a/client/src/app/admin/users/shared/user.service.ts b/client/src/app/admin/users/shared/user.service.ts new file mode 100644 index 000000000..be433f0a1 --- /dev/null +++ b/client/src/app/admin/users/shared/user.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@angular/core'; +import { Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; + +import { AuthHttp, User } from '../../../shared'; + +@Injectable() +export class UserService { + // TODO: merge this constant with account + private static BASE_USERS_URL = '/api/v1/users/'; + + constructor(private authHttp: AuthHttp) {} + + addUser(username: string, password: string) { + const body = { + username, + password + }; + + return this.authHttp.post(UserService.BASE_USERS_URL, body); + } + + getUsers() { + return this.authHttp.get(UserService.BASE_USERS_URL) + .map(res => res.json()) + .map(this.extractUsers) + .catch(this.handleError); + } + + removeUser(user: User) { + return this.authHttp.delete(UserService.BASE_USERS_URL + user.id); + } + + private extractUsers(body: any) { + const usersJson = body.data; + const totalUsers = body.total; + const users = []; + for (const userJson of usersJson) { + users.push(new User(userJson)); + } + + return { users, totalUsers }; + } + + private handleError(error: Response) { + console.error(error); + return Observable.throw(error.json().error || 'Server error'); + } +} diff --git a/client/src/app/admin/users/user-add/index.ts b/client/src/app/admin/users/user-add/index.ts new file mode 100644 index 000000000..66d5ca04f --- /dev/null +++ b/client/src/app/admin/users/user-add/index.ts @@ -0,0 +1 @@ +export * from './user-add.component'; diff --git a/client/src/app/admin/users/user-add/user-add.component.html b/client/src/app/admin/users/user-add/user-add.component.html new file mode 100644 index 000000000..aa102358a --- /dev/null +++ b/client/src/app/admin/users/user-add/user-add.component.html @@ -0,0 +1,29 @@ +

Add user

+ +
{{ error }}
+ +
+
+ + +
+ Username is required with a length >= 3 and <= 20 +
+
+ +
+ + +
+ Password is required with a length >= 6 +
+
+ + +
diff --git a/client/src/app/admin/users/user-add/user-add.component.ts b/client/src/app/admin/users/user-add/user-add.component.ts new file mode 100644 index 000000000..30ca947a0 --- /dev/null +++ b/client/src/app/admin/users/user-add/user-add.component.ts @@ -0,0 +1,33 @@ +import { Control, ControlGroup, Validators } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { UserService } from '../shared'; + +@Component({ + selector: 'my-user-add', + template: require('./user-add.component.html'), +}) +export class UserAddComponent implements OnInit { + userAddForm: ControlGroup; + error: string = null; + + constructor(private router: Router, private userService: UserService) {} + + ngOnInit() { + this.userAddForm = new ControlGroup({ + username: new Control('', Validators.compose([ Validators.required, Validators.minLength(3), Validators.maxLength(20) ])), + password: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])), + }); + } + + addUser(username: string, password: string) { + this.error = null; + + this.userService.addUser(username, password).subscribe( + ok => this.router.navigate([ '/admin/users/list' ]), + + err => this.error = err + ); + } +} diff --git a/client/src/app/admin/users/user-list/index.ts b/client/src/app/admin/users/user-list/index.ts new file mode 100644 index 000000000..51fbefa80 --- /dev/null +++ b/client/src/app/admin/users/user-list/index.ts @@ -0,0 +1 @@ +export * from './user-list.component'; diff --git a/client/src/app/admin/users/user-list/user-list.component.html b/client/src/app/admin/users/user-list/user-list.component.html new file mode 100644 index 000000000..2aca05f2b --- /dev/null +++ b/client/src/app/admin/users/user-list/user-list.component.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + +
IdUsernameRemove
{{ user.id }}{{ user.username }} + +
+ + + + Add user + diff --git a/client/src/app/admin/users/user-list/user-list.component.scss b/client/src/app/admin/users/user-list/user-list.component.scss new file mode 100644 index 000000000..e9f61e900 --- /dev/null +++ b/client/src/app/admin/users/user-list/user-list.component.scss @@ -0,0 +1,7 @@ +.glyphicon-remove { + cursor: pointer; +} + +.add-user { + margin-top: 10px; +} diff --git a/client/src/app/admin/users/user-list/user-list.component.ts b/client/src/app/admin/users/user-list/user-list.component.ts new file mode 100644 index 000000000..598daa42a --- /dev/null +++ b/client/src/app/admin/users/user-list/user-list.component.ts @@ -0,0 +1,44 @@ +import { Component, OnInit } from '@angular/core'; +import { ROUTER_DIRECTIVES } from '@angular/router'; + +import { User } from '../../../shared'; +import { UserService } from '../shared'; + +@Component({ + selector: 'my-user-list', + template: require('./user-list.component.html'), + styles: [ require('./user-list.component.scss') ], + directives: [ ROUTER_DIRECTIVES ] +}) +export class UserListComponent implements OnInit { + totalUsers: number; + users: User[]; + + constructor(private userService: UserService) {} + + ngOnInit() { + this.getUsers(); + } + + getUsers() { + this.userService.getUsers().subscribe( + ({ users, totalUsers }) => { + this.users = users; + this.totalUsers = totalUsers; + }, + + err => alert(err) + ); + } + + + removeUser(user: User) { + if (confirm('Are you sure?')) { + this.userService.removeUser(user).subscribe( + () => this.getUsers(), + + err => alert(err) + ); + } + } +} diff --git a/client/src/app/admin/users/users.component.ts b/client/src/app/admin/users/users.component.ts new file mode 100644 index 000000000..46aa0862f --- /dev/null +++ b/client/src/app/admin/users/users.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { ROUTER_DIRECTIVES } from '@angular/router'; + +import { UserService } from './shared'; + +@Component({ + template: '', + directives: [ ROUTER_DIRECTIVES ], + providers: [ UserService ] +}) + +export class UsersComponent { +} diff --git a/client/src/app/admin/users/users.routes.ts b/client/src/app/admin/users/users.routes.ts new file mode 100644 index 000000000..0457c3843 --- /dev/null +++ b/client/src/app/admin/users/users.routes.ts @@ -0,0 +1,27 @@ +import { RouterConfig } from '@angular/router'; + +import { UsersComponent } from './users.component'; +import { UserAddComponent } from './user-add'; +import { UserListComponent } from './user-list'; + +export const UsersRoutes: RouterConfig = [ + { + path: 'users', + component: UsersComponent, + children: [ + { + path: '', + redirectTo: 'list', + pathMatch: 'full' + }, + { + path: 'list', + component: UserListComponent + }, + { + path: 'add', + component: UserAddComponent + } + ] + } +]; diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index ea4b31421..58967abca 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -47,7 +47,12 @@ -
+
+
+ + List users +
+
Make friends diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 5764f24ca..444b6b3b4 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -45,6 +45,10 @@ export class AppComponent { ); } + isUserAdmin() { + return this.authService.isAdmin(); + } + logout() { this.authService.logout(); // Redirect to home page diff --git a/client/src/app/app.routes.ts b/client/src/app/app.routes.ts index 1c414038d..d7194cb4f 100644 --- a/client/src/app/app.routes.ts +++ b/client/src/app/app.routes.ts @@ -2,6 +2,7 @@ import { RouterConfig } from '@angular/router'; import { AccountRoutes } from './account'; import { LoginRoutes } from './login'; +import { AdminRoutes } from './admin'; import { VideosRoutes } from './videos'; export const routes: RouterConfig = [ @@ -10,7 +11,7 @@ export const routes: RouterConfig = [ redirectTo: '/videos/list', pathMatch: 'full' }, - + ...AdminRoutes, ...AccountRoutes, ...LoginRoutes, ...VideosRoutes diff --git a/client/src/app/shared/auth/auth-user.model.ts b/client/src/app/shared/auth/auth-user.model.ts new file mode 100644 index 000000000..bdd5ea5a9 --- /dev/null +++ b/client/src/app/shared/auth/auth-user.model.ts @@ -0,0 +1,120 @@ +import { User } from '../users'; + +export class AuthUser extends User { + private static KEYS = { + ID: 'id', + ROLE: 'role', + USERNAME: 'username' + }; + + id: string; + role: string; + username: string; + tokens: Tokens; + + static load() { + const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME); + if (usernameLocalStorage) { + return new AuthUser( + { + id: localStorage.getItem(this.KEYS.ID), + username: localStorage.getItem(this.KEYS.USERNAME), + role: localStorage.getItem(this.KEYS.ROLE) + }, + Tokens.load() + ); + } + + return null; + } + + static flush() { + localStorage.removeItem(this.KEYS.USERNAME); + localStorage.removeItem(this.KEYS.ID); + localStorage.removeItem(this.KEYS.ROLE); + Tokens.flush(); + } + + constructor(userHash: { id: string, username: string, role: string }, hashTokens: any) { + super(userHash); + this.tokens = new Tokens(hashTokens); + } + + getAccessToken() { + return this.tokens.access_token; + } + + getRefreshToken() { + return this.tokens.refresh_token; + } + + getTokenType() { + return this.tokens.token_type; + } + + refreshTokens(access_token: string, refresh_token: string) { + this.tokens.access_token = access_token; + this.tokens.refresh_token = refresh_token; + } + + save() { + localStorage.setItem(AuthUser.KEYS.ID, this.id); + localStorage.setItem(AuthUser.KEYS.USERNAME, this.username); + localStorage.setItem(AuthUser.KEYS.ROLE, this.role); + this.tokens.save(); + } +} + +// Private class only used by User +class Tokens { + private static KEYS = { + ACCESS_TOKEN: 'access_token', + REFRESH_TOKEN: 'refresh_token', + TOKEN_TYPE: 'token_type', + }; + + access_token: string; + refresh_token: string; + token_type: string; + + static load() { + const accessTokenLocalStorage = localStorage.getItem(this.KEYS.ACCESS_TOKEN); + const refreshTokenLocalStorage = localStorage.getItem(this.KEYS.REFRESH_TOKEN); + const tokenTypeLocalStorage = localStorage.getItem(this.KEYS.TOKEN_TYPE); + + if (accessTokenLocalStorage && refreshTokenLocalStorage && tokenTypeLocalStorage) { + return new Tokens({ + access_token: accessTokenLocalStorage, + refresh_token: refreshTokenLocalStorage, + token_type: tokenTypeLocalStorage + }); + } + + return null; + } + + static flush() { + localStorage.removeItem(this.KEYS.ACCESS_TOKEN); + localStorage.removeItem(this.KEYS.REFRESH_TOKEN); + localStorage.removeItem(this.KEYS.TOKEN_TYPE); + } + + constructor(hash?: any) { + if (hash) { + this.access_token = hash.access_token; + this.refresh_token = hash.refresh_token; + + if (hash.token_type === 'bearer') { + this.token_type = 'Bearer'; + } else { + this.token_type = hash.token_type; + } + } + } + + save() { + localStorage.setItem('access_token', this.access_token); + localStorage.setItem('refresh_token', this.refresh_token); + localStorage.setItem('token_type', this.token_type); + } +} diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts index 24d1a4fa2..8eea0c4bf 100644 --- a/client/src/app/shared/auth/auth.service.ts +++ b/client/src/app/shared/auth/auth.service.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; import { AuthStatus } from './auth-status.model'; -import { User } from './user.model'; +import { AuthUser } from './auth-user.model'; @Injectable() export class AuthService { @@ -17,7 +17,7 @@ export class AuthService { private clientId: string; private clientSecret: string; private loginChanged: Subject; - private user: User = null; + private user: AuthUser = null; constructor(private http: Http) { this.loginChanged = new Subject(); @@ -40,7 +40,7 @@ export class AuthService { ); // Return null if there is nothing to load - this.user = User.load(); + this.user = AuthUser.load(); } getRefreshToken() { @@ -65,10 +65,16 @@ export class AuthService { return this.user.getTokenType(); } - getUser(): User { + getUser(): AuthUser { return this.user; } + isAdmin() { + if (this.user === null) return false; + + return this.user.isAdmin(); + } + isLoggedIn() { if (this.getAccessToken()) { return true; @@ -108,7 +114,7 @@ export class AuthService { logout() { // TODO: make an HTTP request to revoke the tokens this.user = null; - User.flush(); + AuthUser.flush(); this.setStatus(AuthStatus.LoggedOut); } @@ -163,13 +169,13 @@ export class AuthService { const id = obj.id; const username = obj.username; const role = obj.role; - const hash_tokens = { + const hashTokens = { access_token: obj.access_token, token_type: obj.token_type, refresh_token: obj.refresh_token }; - this.user = new User(id, username, role, hash_tokens); + this.user = new AuthUser({ id, username, role }, hashTokens); this.user.save(); this.setStatus(AuthStatus.LoggedIn); diff --git a/client/src/app/shared/auth/index.ts b/client/src/app/shared/auth/index.ts index aafaacbf1..ebd9e14cd 100644 --- a/client/src/app/shared/auth/index.ts +++ b/client/src/app/shared/auth/index.ts @@ -1,4 +1,4 @@ export * from './auth-http.service'; export * from './auth-status.model'; export * from './auth.service'; -export * from './user.model'; +export * from './auth-user.model'; diff --git a/client/src/app/shared/auth/user.model.ts b/client/src/app/shared/auth/user.model.ts deleted file mode 100644 index e486873ab..000000000 --- a/client/src/app/shared/auth/user.model.ts +++ /dev/null @@ -1,118 +0,0 @@ -export class User { - private static KEYS = { - ID: 'id', - ROLE: 'role', - USERNAME: 'username' - }; - - id: string; - role: string; - username: string; - tokens: Tokens; - - static load() { - const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME); - if (usernameLocalStorage) { - return new User( - localStorage.getItem(this.KEYS.ID), - localStorage.getItem(this.KEYS.USERNAME), - localStorage.getItem(this.KEYS.ROLE), - Tokens.load() - ); - } - - return null; - } - - static flush() { - localStorage.removeItem(this.KEYS.USERNAME); - localStorage.removeItem(this.KEYS.ID); - localStorage.removeItem(this.KEYS.ROLE); - Tokens.flush(); - } - - constructor(id: string, username: string, role: string, hash_tokens: any) { - this.id = id; - this.username = username; - this.role = role; - this.tokens = new Tokens(hash_tokens); - } - - getAccessToken() { - return this.tokens.access_token; - } - - getRefreshToken() { - return this.tokens.refresh_token; - } - - getTokenType() { - return this.tokens.token_type; - } - - refreshTokens(access_token: string, refresh_token: string) { - this.tokens.access_token = access_token; - this.tokens.refresh_token = refresh_token; - } - - save() { - localStorage.setItem(User.KEYS.ID, this.id); - localStorage.setItem(User.KEYS.USERNAME, this.username); - localStorage.setItem(User.KEYS.ROLE, this.role); - this.tokens.save(); - } -} - -// Private class only used by User -class Tokens { - private static KEYS = { - ACCESS_TOKEN: 'access_token', - REFRESH_TOKEN: 'refresh_token', - TOKEN_TYPE: 'token_type', - }; - - access_token: string; - refresh_token: string; - token_type: string; - - static load() { - const accessTokenLocalStorage = localStorage.getItem(this.KEYS.ACCESS_TOKEN); - const refreshTokenLocalStorage = localStorage.getItem(this.KEYS.REFRESH_TOKEN); - const tokenTypeLocalStorage = localStorage.getItem(this.KEYS.TOKEN_TYPE); - - if (accessTokenLocalStorage && refreshTokenLocalStorage && tokenTypeLocalStorage) { - return new Tokens({ - access_token: accessTokenLocalStorage, - refresh_token: refreshTokenLocalStorage, - token_type: tokenTypeLocalStorage - }); - } - - return null; - } - - static flush() { - localStorage.removeItem(this.KEYS.ACCESS_TOKEN); - localStorage.removeItem(this.KEYS.REFRESH_TOKEN); - localStorage.removeItem(this.KEYS.TOKEN_TYPE); - } - - constructor(hash?: any) { - if (hash) { - this.access_token = hash.access_token; - this.refresh_token = hash.refresh_token; - - if (hash.token_type === 'bearer') { - this.token_type = 'Bearer'; - } else { - this.token_type = hash.token_type; - } - } - } - - save() { - localStorage.setItem('access_token', this.access_token); - localStorage.setItem('refresh_token', this.refresh_token); - localStorage.setItem('token_type', this.token_type); - } -} diff --git a/client/src/app/shared/index.ts b/client/src/app/shared/index.ts index dfea4c67c..c05e8d253 100644 --- a/client/src/app/shared/index.ts +++ b/client/src/app/shared/index.ts @@ -1,2 +1,3 @@ export * from './auth'; export * from './search'; +export * from './users'; diff --git a/client/src/app/shared/users/index.ts b/client/src/app/shared/users/index.ts new file mode 100644 index 000000000..5a670ce8f --- /dev/null +++ b/client/src/app/shared/users/index.ts @@ -0,0 +1 @@ +export * from './user.model'; diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts new file mode 100644 index 000000000..0f34d4480 --- /dev/null +++ b/client/src/app/shared/users/user.model.ts @@ -0,0 +1,15 @@ +export class User { + id: string; + username: string; + role: string; + + constructor(hash: { id: string, username: string, role: string }) { + this.id = hash.id; + this.username = hash.username; + this.role = hash.role; + } + + isAdmin() { + return this.role === 'admin'; + } +} diff --git a/client/src/app/videos/video-list/video-list.component.ts b/client/src/app/videos/video-list/video-list.component.ts index 5691d684e..062340ec5 100644 --- a/client/src/app/videos/video-list/video-list.component.ts +++ b/client/src/app/videos/video-list/video-list.component.ts @@ -12,7 +12,7 @@ import { Video, VideoService } from '../shared'; -import { AuthService, Search, SearchField, User } from '../../shared'; +import { AuthService, AuthUser, Search, SearchField } from '../../shared'; import { VideoMiniatureComponent } from './video-miniature.component'; import { VideoSortComponent } from './video-sort.component'; import { SearchService } from '../../shared'; @@ -33,7 +33,7 @@ export class VideoListComponent implements OnInit, OnDestroy { totalItems: null }; sort: SortField; - user: User = null; + user: AuthUser = null; videos: Video[] = []; private search: Search; @@ -51,7 +51,7 @@ export class VideoListComponent implements OnInit, OnDestroy { ngOnInit() { if (this.authService.isLoggedIn()) { - this.user = User.load(); + this.user = AuthUser.load(); } // Subscribe to route changes -- cgit v1.2.3 From 602eb142bebb62f1774d6e17c211eef99ace584b Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 12 Aug 2016 16:52:10 +0200 Subject: Client: make an admin menu and a classic menu component --- client/src/app/admin/friends/friend.service.ts | 29 ++++++++++ client/src/app/admin/friends/index.ts | 1 + client/src/app/admin/index.ts | 1 + client/src/app/admin/menu-admin.component.html | 26 +++++++++ client/src/app/admin/menu-admin.component.ts | 42 +++++++++++++++ client/src/app/app.component.html | 53 +----------------- client/src/app/app.component.scss | 34 ------------ client/src/app/app.component.ts | 74 ++++---------------------- client/src/app/friends/friend.service.ts | 29 ---------- client/src/app/friends/index.ts | 1 - client/src/app/menu.component.html | 39 ++++++++++++++ client/src/app/menu.component.ts | 51 ++++++++++++++++++ 12 files changed, 202 insertions(+), 178 deletions(-) create mode 100644 client/src/app/admin/friends/friend.service.ts create mode 100644 client/src/app/admin/friends/index.ts create mode 100644 client/src/app/admin/menu-admin.component.html create mode 100644 client/src/app/admin/menu-admin.component.ts delete mode 100644 client/src/app/friends/friend.service.ts delete mode 100644 client/src/app/friends/index.ts create mode 100644 client/src/app/menu.component.html create mode 100644 client/src/app/menu.component.ts (limited to 'client/src/app') diff --git a/client/src/app/admin/friends/friend.service.ts b/client/src/app/admin/friends/friend.service.ts new file mode 100644 index 000000000..d4ab5e60f --- /dev/null +++ b/client/src/app/admin/friends/friend.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; +import { Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; + +import { AuthHttp, AuthService } from '../../shared'; + +@Injectable() +export class FriendService { + private static BASE_FRIEND_URL: string = '/api/v1/pods/'; + + constructor (private authHttp: AuthHttp, private authService: AuthService) {} + + makeFriends() { + return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'makefriends') + .map(res => res.status) + .catch(this.handleError); + } + + quitFriends() { + return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'quitfriends') + .map(res => res.status) + .catch(this.handleError); + } + + private handleError (error: Response): Observable { + console.error(error); + return Observable.throw(error.json().error || 'Server error'); + } +} diff --git a/client/src/app/admin/friends/index.ts b/client/src/app/admin/friends/index.ts new file mode 100644 index 000000000..0adc256c4 --- /dev/null +++ b/client/src/app/admin/friends/index.ts @@ -0,0 +1 @@ +export * from './friend.service'; diff --git a/client/src/app/admin/index.ts b/client/src/app/admin/index.ts index 3b0540818..292973681 100644 --- a/client/src/app/admin/index.ts +++ b/client/src/app/admin/index.ts @@ -1,3 +1,4 @@ export * from './users'; export * from './admin.component'; export * from './admin.routes'; +export * from './menu-admin.component'; diff --git a/client/src/app/admin/menu-admin.component.html b/client/src/app/admin/menu-admin.component.html new file mode 100644 index 000000000..15a3c764e --- /dev/null +++ b/client/src/app/admin/menu-admin.component.html @@ -0,0 +1,26 @@ + + +
+
+ + List users +
+ +
+ + Make friends +
+ +
+ + Quit friends +
+
+ +
+
+ + Quit admin. +
+
+
diff --git a/client/src/app/admin/menu-admin.component.ts b/client/src/app/admin/menu-admin.component.ts new file mode 100644 index 000000000..eb27c1e58 --- /dev/null +++ b/client/src/app/admin/menu-admin.component.ts @@ -0,0 +1,42 @@ +import { Component, Output, EventEmitter } from '@angular/core'; +import { ROUTER_DIRECTIVES } from '@angular/router'; + +import { FriendService } from './friends'; + +@Component({ + selector: 'my-menu-admin', + template: require('./menu-admin.component.html'), + directives: [ ROUTER_DIRECTIVES ], + providers: [ FriendService ] +}) +export class MenuAdminComponent { + @Output() quittedAdmin = new EventEmitter(); + + constructor(private friendService: FriendService) {} + + makeFriends() { + this.friendService.makeFriends().subscribe( + status => { + if (status === 409) { + alert('Already made friends!'); + } else { + alert('Made friends!'); + } + }, + error => alert(error) + ); + } + + quitAdmin() { + this.quittedAdmin.emit(true); + } + + quitFriends() { + this.friendService.quitFriends().subscribe( + status => { + alert('Quit friends!'); + }, + error => alert(error) + ); + } +} diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index 58967abca..a7538ee7a 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -14,61 +14,12 @@
- - -
-
- - - Login - - - - - Logout - -
- -
- - My account -
-
- -
-
- - Get videos -
- - -
- -
-
- - List users -
- -
- - Make friends -
- -
- - Quit friends -
-
-
+ +
-
diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss index 1b02b2f57..95f306d75 100644 --- a/client/src/app/app.component.scss +++ b/client/src/app/app.component.scss @@ -12,40 +12,6 @@ header div { margin-bottom: 30px; } -menu { - @media screen and (max-width: 600px) { - margin-right: 3px !important; - padding: 3px !important; - min-height: 400px !important; - } - - min-height: 600px; - margin-right: 20px; - border-right: 1px solid rgba(0, 0, 0, 0.2); - - .panel-button { - margin: 8px; - cursor: pointer; - transition: margin 0.2s; - - &:hover { - margin-left: 15px; - } - - a { - color: #333333; - } - } - - .glyphicon { - margin: 5px; - } -} - -.panel-block:not(:last-child) { - border-bottom: 1px solid rgba(0, 0, 0, 0.1); -} - .router-outlet-container { @media screen and (max-width: 400px) { padding: 0 3px 0 3px; diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 444b6b3b4..d9549ad5b 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -1,79 +1,27 @@ import { Component } from '@angular/core'; -import { ActivatedRoute, Router, ROUTER_DIRECTIVES } from '@angular/router'; +import { ROUTER_DIRECTIVES } from '@angular/router'; -import { FriendService } from './friends'; -import { - AuthService, - AuthStatus, - SearchComponent, - SearchService -} from './shared'; +import { MenuAdminComponent } from './admin'; +import { MenuComponent } from './menu.component'; +import { SearchComponent, SearchService } from './shared'; import { VideoService } from './videos'; @Component({ selector: 'my-app', template: require('./app.component.html'), styles: [ require('./app.component.scss') ], - directives: [ ROUTER_DIRECTIVES, SearchComponent ], - providers: [ FriendService, VideoService, SearchService ] + directives: [ MenuAdminComponent, MenuComponent, ROUTER_DIRECTIVES, SearchComponent ], + providers: [ VideoService, SearchService ] }) export class AppComponent { - choices = []; - isLoggedIn: boolean; + isInAdmin = false; - constructor( - private authService: AuthService, - private friendService: FriendService, - private route: ActivatedRoute, - private router: Router - ) { - this.isLoggedIn = this.authService.isLoggedIn(); - - this.authService.loginChangedSource.subscribe( - status => { - if (status === AuthStatus.LoggedIn) { - this.isLoggedIn = true; - console.log('Logged in.'); - } else if (status === AuthStatus.LoggedOut) { - this.isLoggedIn = false; - console.log('Logged out.'); - } else { - console.error('Unknown auth status: ' + status); - } - } - ); - } - - isUserAdmin() { - return this.authService.isAdmin(); - } - - logout() { - this.authService.logout(); - // Redirect to home page - this.router.navigate(['/videos/list']); - } - - makeFriends() { - this.friendService.makeFriends().subscribe( - status => { - if (status === 409) { - alert('Already made friends!'); - } else { - alert('Made friends!'); - } - }, - error => alert(error) - ); + onEnteredInAdmin() { + this.isInAdmin = true; } - quitFriends() { - this.friendService.quitFriends().subscribe( - status => { - alert('Quit friends!'); - }, - error => alert(error) - ); + onQuittedAdmin() { + this.isInAdmin = false; } } diff --git a/client/src/app/friends/friend.service.ts b/client/src/app/friends/friend.service.ts deleted file mode 100644 index 771046484..000000000 --- a/client/src/app/friends/friend.service.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Response } from '@angular/http'; -import { Observable } from 'rxjs/Observable'; - -import { AuthHttp, AuthService } from '../shared'; - -@Injectable() -export class FriendService { - private static BASE_FRIEND_URL: string = '/api/v1/pods/'; - - constructor (private authHttp: AuthHttp, private authService: AuthService) {} - - makeFriends() { - return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'makefriends') - .map(res => res.status) - .catch(this.handleError); - } - - quitFriends() { - return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'quitfriends') - .map(res => res.status) - .catch(this.handleError); - } - - private handleError (error: Response): Observable { - console.error(error); - return Observable.throw(error.json().error || 'Server error'); - } -} diff --git a/client/src/app/friends/index.ts b/client/src/app/friends/index.ts deleted file mode 100644 index 0adc256c4..000000000 --- a/client/src/app/friends/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './friend.service'; diff --git a/client/src/app/menu.component.html b/client/src/app/menu.component.html new file mode 100644 index 000000000..922375395 --- /dev/null +++ b/client/src/app/menu.component.html @@ -0,0 +1,39 @@ + +
+
+ + + Login + + + + + Logout + +
+ +
+ + My account +
+
+ +
+
+ + Get videos +
+ + +
+ +
+ +
+
diff --git a/client/src/app/menu.component.ts b/client/src/app/menu.component.ts new file mode 100644 index 000000000..594cd996e --- /dev/null +++ b/client/src/app/menu.component.ts @@ -0,0 +1,51 @@ +import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Router, ROUTER_DIRECTIVES } from '@angular/router'; + +import { AuthService, AuthStatus } from './shared'; + +@Component({ + selector: 'my-menu', + template: require('./menu.component.html'), + directives: [ ROUTER_DIRECTIVES ] +}) +export class MenuComponent implements OnInit { + @Output() enteredInAdmin = new EventEmitter(); + isLoggedIn: boolean; + + constructor ( + private authService: AuthService, + private router: Router + ) {} + + ngOnInit() { + this.isLoggedIn = this.authService.isLoggedIn(); + + this.authService.loginChangedSource.subscribe( + status => { + if (status === AuthStatus.LoggedIn) { + this.isLoggedIn = true; + console.log('Logged in.'); + } else if (status === AuthStatus.LoggedOut) { + this.isLoggedIn = false; + console.log('Logged out.'); + } else { + console.error('Unknown auth status: ' + status); + } + } + ); + } + + enterInAdmin() { + this.enteredInAdmin.emit(true); + } + + isUserAdmin() { + return this.authService.isAdmin(); + } + + logout() { + this.authService.logout(); + // Redirect to home page + this.router.navigate(['/videos/list']); + } +} -- cgit v1.2.3 From c323efb9cdc6a605242d112ac0c9db9f67eabaad Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 12 Aug 2016 17:35:00 +0200 Subject: Update webtorrent -> 0.96 --- client/src/app/shared/search/search.component.ts | 2 +- .../app/videos/video-watch/video-watch.component.ts | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'client/src/app') diff --git a/client/src/app/shared/search/search.component.ts b/client/src/app/shared/search/search.component.ts index e864fbc17..219997e85 100644 --- a/client/src/app/shared/search/search.component.ts +++ b/client/src/app/shared/search/search.component.ts @@ -28,7 +28,7 @@ export class SearchComponent implements OnInit { constructor(private searchService: SearchService) {} ngOnInit() { - // Subscribe is the search changed + // Subscribe if the search changed // Usually changed by videos list component this.searchService.updateSearch.subscribe( newSearchCriterias => { diff --git a/client/src/app/videos/video-watch/video-watch.component.ts b/client/src/app/videos/video-watch/video-watch.component.ts index 09255de5d..bc0e3157d 100644 --- a/client/src/app/videos/video-watch/video-watch.component.ts +++ b/client/src/app/videos/video-watch/video-watch.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, OnDestroy, OnInit } from '@angular/core'; +import { Component, ElementRef, NgZone, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'; @@ -31,6 +31,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { constructor( private elementRef: ElementRef, + private ngZone: NgZone, private route: ActivatedRoute, private videoService: VideoService, private webTorrentService: WebTorrentService @@ -65,12 +66,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { } }); - // Refresh each second - this.torrentInfosInterval = setInterval(() => { - this.downloadSpeed = torrent.downloadSpeed; - this.numPeers = torrent.numPeers; - this.uploadSpeed = torrent.uploadSpeed; - }, 1000); + this.runInProgress(torrent); }); } @@ -99,4 +95,15 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.error = true; console.error('The video load seems to be abnormally long.'); } + + private runInProgress(torrent: any) { + // Refresh each second + this.torrentInfosInterval = setInterval(() => { + this.ngZone.run(() => { + this.downloadSpeed = torrent.downloadSpeed; + this.numPeers = torrent.numPeers; + this.uploadSpeed = torrent.uploadSpeed; + }); + }, 1000); + } } -- cgit v1.2.3 From e2f555cab7563cd74fa790cea5fc65f2e31b0dc0 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 12 Aug 2016 18:22:58 +0200 Subject: Client: add friends page --- client/src/app/admin/admin.routes.ts | 2 + .../friends/friend-list/friend-list.component.html | 21 ++++++++++ .../friends/friend-list/friend-list.component.scss | 3 ++ .../friends/friend-list/friend-list.component.ts | 46 ++++++++++++++++++++++ client/src/app/admin/friends/friend-list/index.ts | 1 + client/src/app/admin/friends/friend.service.ts | 29 -------------- client/src/app/admin/friends/friends.component.ts | 13 ++++++ client/src/app/admin/friends/friends.routes.ts | 22 +++++++++++ client/src/app/admin/friends/index.ts | 4 +- .../src/app/admin/friends/shared/friend.model.ts | 3 ++ .../src/app/admin/friends/shared/friend.service.ts | 39 ++++++++++++++++++ client/src/app/admin/friends/shared/index.ts | 2 + client/src/app/admin/menu-admin.component.html | 9 +---- client/src/app/admin/menu-admin.component.ts | 29 +------------- 14 files changed, 158 insertions(+), 65 deletions(-) create mode 100644 client/src/app/admin/friends/friend-list/friend-list.component.html create mode 100644 client/src/app/admin/friends/friend-list/friend-list.component.scss create mode 100644 client/src/app/admin/friends/friend-list/friend-list.component.ts create mode 100644 client/src/app/admin/friends/friend-list/index.ts delete mode 100644 client/src/app/admin/friends/friend.service.ts create mode 100644 client/src/app/admin/friends/friends.component.ts create mode 100644 client/src/app/admin/friends/friends.routes.ts create mode 100644 client/src/app/admin/friends/shared/friend.model.ts create mode 100644 client/src/app/admin/friends/shared/friend.service.ts create mode 100644 client/src/app/admin/friends/shared/index.ts (limited to 'client/src/app') diff --git a/client/src/app/admin/admin.routes.ts b/client/src/app/admin/admin.routes.ts index d375a86af..f57deef62 100644 --- a/client/src/app/admin/admin.routes.ts +++ b/client/src/app/admin/admin.routes.ts @@ -1,6 +1,7 @@ import { RouterConfig } from '@angular/router'; import { AdminComponent } from './admin.component'; +import { FriendsRoutes } from './friends'; import { UsersRoutes } from './users'; export const AdminRoutes: RouterConfig = [ @@ -8,6 +9,7 @@ export const AdminRoutes: RouterConfig = [ path: 'admin', component: AdminComponent, children: [ + ...FriendsRoutes, ...UsersRoutes ] } diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.html b/client/src/app/admin/friends/friend-list/friend-list.component.html new file mode 100644 index 000000000..860bd2c07 --- /dev/null +++ b/client/src/app/admin/friends/friend-list/friend-list.component.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + +
Url
{{ friend.url }}
+ + + Quit friends + + + + Make friends + diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.scss b/client/src/app/admin/friends/friend-list/friend-list.component.scss new file mode 100644 index 000000000..cb597e12b --- /dev/null +++ b/client/src/app/admin/friends/friend-list/friend-list.component.scss @@ -0,0 +1,3 @@ +table { + margin-bottom: 40px; +} diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.ts b/client/src/app/admin/friends/friend-list/friend-list.component.ts new file mode 100644 index 000000000..bf66d3ff1 --- /dev/null +++ b/client/src/app/admin/friends/friend-list/friend-list.component.ts @@ -0,0 +1,46 @@ +import { Component, OnInit } from '@angular/core'; + +import { Friend, FriendService } from '../shared'; + +@Component({ + selector: 'my-friend-list', + template: require('./friend-list.component.html'), + styles: [ require('./friend-list.component.scss') ] +}) +export class FriendListComponent implements OnInit { + friends: Friend[]; + + constructor(private friendService: FriendService) { } + + ngOnInit() { + this.friendService.getFriends().subscribe( + friends => this.friends = friends, + + err => alert(err) + ); + } + + makeFriends() { + this.friendService.makeFriends().subscribe( + status => { + if (status === 409) { + alert('Already made friends!'); + } else { + alert('Made friends!'); + } + }, + error => alert(error) + ); + } + + quitFriends() { + if (!confirm('Are you sure?')) return; + + this.friendService.quitFriends().subscribe( + status => { + alert('Quit friends!'); + }, + error => alert(error) + ); + } +} diff --git a/client/src/app/admin/friends/friend-list/index.ts b/client/src/app/admin/friends/friend-list/index.ts new file mode 100644 index 000000000..354c978a4 --- /dev/null +++ b/client/src/app/admin/friends/friend-list/index.ts @@ -0,0 +1 @@ +export * from './friend-list.component'; diff --git a/client/src/app/admin/friends/friend.service.ts b/client/src/app/admin/friends/friend.service.ts deleted file mode 100644 index d4ab5e60f..000000000 --- a/client/src/app/admin/friends/friend.service.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Response } from '@angular/http'; -import { Observable } from 'rxjs/Observable'; - -import { AuthHttp, AuthService } from '../../shared'; - -@Injectable() -export class FriendService { - private static BASE_FRIEND_URL: string = '/api/v1/pods/'; - - constructor (private authHttp: AuthHttp, private authService: AuthService) {} - - makeFriends() { - return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'makefriends') - .map(res => res.status) - .catch(this.handleError); - } - - quitFriends() { - return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'quitfriends') - .map(res => res.status) - .catch(this.handleError); - } - - private handleError (error: Response): Observable { - console.error(error); - return Observable.throw(error.json().error || 'Server error'); - } -} diff --git a/client/src/app/admin/friends/friends.component.ts b/client/src/app/admin/friends/friends.component.ts new file mode 100644 index 000000000..e66280f01 --- /dev/null +++ b/client/src/app/admin/friends/friends.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { ROUTER_DIRECTIVES } from '@angular/router'; + +import { FriendService } from './shared'; + +@Component({ + template: '', + directives: [ ROUTER_DIRECTIVES ], + providers: [ FriendService ] +}) + +export class FriendsComponent { +} diff --git a/client/src/app/admin/friends/friends.routes.ts b/client/src/app/admin/friends/friends.routes.ts new file mode 100644 index 000000000..1e3646395 --- /dev/null +++ b/client/src/app/admin/friends/friends.routes.ts @@ -0,0 +1,22 @@ +import { RouterConfig } from '@angular/router'; + +import { FriendsComponent } from './friends.component'; +import { FriendListComponent } from './friend-list'; + +export const FriendsRoutes: RouterConfig = [ + { + path: 'friends', + component: FriendsComponent, + children: [ + { + path: '', + redirectTo: 'list', + pathMatch: 'full' + }, + { + path: 'list', + component: FriendListComponent + } + ] + } +]; diff --git a/client/src/app/admin/friends/index.ts b/client/src/app/admin/friends/index.ts index 0adc256c4..01aeedeee 100644 --- a/client/src/app/admin/friends/index.ts +++ b/client/src/app/admin/friends/index.ts @@ -1 +1,3 @@ -export * from './friend.service'; +export * from './shared'; +export * from './friend-list'; +export * from './friends.routes'; diff --git a/client/src/app/admin/friends/shared/friend.model.ts b/client/src/app/admin/friends/shared/friend.model.ts new file mode 100644 index 000000000..847eb9c9c --- /dev/null +++ b/client/src/app/admin/friends/shared/friend.model.ts @@ -0,0 +1,3 @@ +export interface Friend { + url: string; +} diff --git a/client/src/app/admin/friends/shared/friend.service.ts b/client/src/app/admin/friends/shared/friend.service.ts new file mode 100644 index 000000000..da4d64611 --- /dev/null +++ b/client/src/app/admin/friends/shared/friend.service.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@angular/core'; +import { Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; + +import { Friend } from './friend.model'; +import { AuthHttp, AuthService } from '../../../shared'; + +@Injectable() +export class FriendService { + private static BASE_FRIEND_URL: string = '/api/v1/pods/'; + + constructor ( + private authHttp: AuthHttp, + private authService: AuthService + ) {} + + getFriends(): Observable { + return this.authHttp.get(FriendService.BASE_FRIEND_URL) + .map(res => res.json()) + .catch(this.handleError); + } + + makeFriends() { + return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'makefriends') + .map(res => res.status) + .catch(this.handleError); + } + + quitFriends() { + return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'quitfriends') + .map(res => res.status) + .catch(this.handleError); + } + + private handleError (error: Response) { + console.error(error); + return Observable.throw(error.json().error || 'Server error'); + } +} diff --git a/client/src/app/admin/friends/shared/index.ts b/client/src/app/admin/friends/shared/index.ts new file mode 100644 index 000000000..0d671637d --- /dev/null +++ b/client/src/app/admin/friends/shared/index.ts @@ -0,0 +1,2 @@ +export * from './friend.model'; +export * from './friend.service'; diff --git a/client/src/app/admin/menu-admin.component.html b/client/src/app/admin/menu-admin.component.html index 15a3c764e..092ab6081 100644 --- a/client/src/app/admin/menu-admin.component.html +++ b/client/src/app/admin/menu-admin.component.html @@ -6,14 +6,9 @@ List users
- diff --git a/client/src/app/admin/menu-admin.component.ts b/client/src/app/admin/menu-admin.component.ts index eb27c1e58..b23f7409e 100644 --- a/client/src/app/admin/menu-admin.component.ts +++ b/client/src/app/admin/menu-admin.component.ts @@ -1,42 +1,15 @@ import { Component, Output, EventEmitter } from '@angular/core'; import { ROUTER_DIRECTIVES } from '@angular/router'; -import { FriendService } from './friends'; - @Component({ selector: 'my-menu-admin', template: require('./menu-admin.component.html'), - directives: [ ROUTER_DIRECTIVES ], - providers: [ FriendService ] + directives: [ ROUTER_DIRECTIVES ] }) export class MenuAdminComponent { @Output() quittedAdmin = new EventEmitter(); - constructor(private friendService: FriendService) {} - - makeFriends() { - this.friendService.makeFriends().subscribe( - status => { - if (status === 409) { - alert('Already made friends!'); - } else { - alert('Made friends!'); - } - }, - error => alert(error) - ); - } - quitAdmin() { this.quittedAdmin.emit(true); } - - quitFriends() { - this.friendService.quitFriends().subscribe( - status => { - alert('Quit friends!'); - }, - error => alert(error) - ); - } } -- cgit v1.2.3 From dfe3ec6bf66dfc5e2878f1df24f6a55b66812f46 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 12 Aug 2016 18:24:24 +0200 Subject: Client: add users list/friends list titles --- client/src/app/admin/friends/friend-list/friend-list.component.html | 2 ++ client/src/app/admin/users/user-list/user-list.component.html | 2 ++ 2 files changed, 4 insertions(+) (limited to 'client/src/app') diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.html b/client/src/app/admin/friends/friend-list/friend-list.component.html index 860bd2c07..f4d14293e 100644 --- a/client/src/app/admin/friends/friend-list/friend-list.component.html +++ b/client/src/app/admin/friends/friend-list/friend-list.component.html @@ -1,3 +1,5 @@ +

Friends list

+ diff --git a/client/src/app/admin/users/user-list/user-list.component.html b/client/src/app/admin/users/user-list/user-list.component.html index 2aca05f2b..fa7f71864 100644 --- a/client/src/app/admin/users/user-list/user-list.component.html +++ b/client/src/app/admin/users/user-list/user-list.component.html @@ -1,3 +1,5 @@ +

Users list

+
-- cgit v1.2.3 From 7eef95353f9202e1f3285606282fc8fd904c90ef Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 15 Aug 2016 19:05:52 +0200 Subject: Client: reset pagination when we search something --- client/src/app/videos/video-list/video-list.component.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'client/src/app') diff --git a/client/src/app/videos/video-list/video-list.component.ts b/client/src/app/videos/video-list/video-list.component.ts index 062340ec5..7c6d4b992 100644 --- a/client/src/app/videos/video-list/video-list.component.ts +++ b/client/src/app/videos/video-list/video-list.component.ts @@ -66,6 +66,8 @@ export class VideoListComponent implements OnInit, OnDestroy { // Subscribe to search changes this.subSearch = this.searchService.searchUpdated.subscribe(search => { this.search = search; + // Reset pagination + this.pagination.currentPage = 1; this.navigateToNewParams(); }); @@ -76,7 +78,7 @@ export class VideoListComponent implements OnInit, OnDestroy { this.subSearch.unsubscribe(); } - getVideos(detectChanges = true) { + getVideos() { this.loading.next(true); this.videos = []; @@ -153,7 +155,11 @@ export class VideoListComponent implements OnInit, OnDestroy { this.sort = routeParams['sort'] || '-createdDate'; - this.pagination.currentPage = parseInt(routeParams['page']) || 1; + if (routeParams['page'] !== undefined) { + this.pagination.currentPage = parseInt(routeParams['page']); + } else { + this.pagination.currentPage = 1; + } this.changeDetector.detectChanges(); } -- cgit v1.2.3 From e105c19c8ebe56b6828ba82948895ad0ca71d8c2 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Sun, 21 Aug 2016 10:41:21 +0200 Subject: Client: support the new make friends method --- client/src/app/admin/admin.routes.ts | 5 ++ .../friends/friend-add/friend-add.component.html | 18 ++++ .../friends/friend-add/friend-add.component.scss | 3 + .../friends/friend-add/friend-add.component.ts | 99 ++++++++++++++++++++++ client/src/app/admin/friends/friend-add/index.ts | 1 + .../friends/friend-list/friend-list.component.html | 2 +- .../friends/friend-list/friend-list.component.ts | 4 +- client/src/app/admin/friends/friends.routes.ts | 5 ++ client/src/app/admin/friends/index.ts | 3 +- .../src/app/admin/friends/shared/friend.service.ts | 8 +- client/src/app/admin/menu-admin.component.html | 4 +- client/src/app/menu.component.html | 2 +- 12 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 client/src/app/admin/friends/friend-add/friend-add.component.html create mode 100644 client/src/app/admin/friends/friend-add/friend-add.component.scss create mode 100644 client/src/app/admin/friends/friend-add/friend-add.component.ts create mode 100644 client/src/app/admin/friends/friend-add/index.ts (limited to 'client/src/app') diff --git a/client/src/app/admin/admin.routes.ts b/client/src/app/admin/admin.routes.ts index f57deef62..80b3ecbc1 100644 --- a/client/src/app/admin/admin.routes.ts +++ b/client/src/app/admin/admin.routes.ts @@ -9,6 +9,11 @@ export const AdminRoutes: RouterConfig = [ path: 'admin', component: AdminComponent, children: [ + { + path: '', + redirectTo: 'users', + pathMatch: 'full' + }, ...FriendsRoutes, ...UsersRoutes ] diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.html b/client/src/app/admin/friends/friend-add/friend-add.component.html new file mode 100644 index 000000000..a52965e8f --- /dev/null +++ b/client/src/app/admin/friends/friend-add/friend-add.component.html @@ -0,0 +1,18 @@ +

Make friends

+ +
{{ error }}
+ +
+
+ +
+ + + + + +
+
+ + + diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.scss b/client/src/app/admin/friends/friend-add/friend-add.component.scss new file mode 100644 index 000000000..cb597e12b --- /dev/null +++ b/client/src/app/admin/friends/friend-add/friend-add.component.scss @@ -0,0 +1,3 @@ +table { + margin-bottom: 40px; +} diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.ts b/client/src/app/admin/friends/friend-add/friend-add.component.ts new file mode 100644 index 000000000..30dbf4d36 --- /dev/null +++ b/client/src/app/admin/friends/friend-add/friend-add.component.ts @@ -0,0 +1,99 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +import { FriendService } from '../shared'; + +@Component({ + selector: 'my-friend-add', + template: require('./friend-add.component.html'), + styles: [ require('./friend-add.component.scss') ] +}) +export class FriendAddComponent { + urls = [ '' ]; + error: string = null; + + constructor(private router: Router, private friendService: FriendService) {} + + addField() { + this.urls.push(''); + } + + customTrackBy(index: number, obj: any): any { + return index; + } + + displayAddField(index: number) { + return index === (this.urls.length - 1); + } + + displayRemoveField(index: number) { + return (index !== 0 || this.urls.length > 1) && index !== (this.urls.length - 1); + } + + removeField(index: number) { + this.urls.splice(index, 1); + } + + makeFriends() { + this.error = ''; + + const notEmptyUrls = this.getNotEmptyUrls(); + if (notEmptyUrls.length === 0) { + this.error = 'You need to specify at less 1 url.'; + return; + } + + if (!this.isUrlsRegexValid(notEmptyUrls)) { + this.error = 'Some url(s) are not valid.'; + return; + } + + if (!this.isUrlsUnique(notEmptyUrls)) { + this.error = 'Urls need to be unique.'; + return; + } + + const confirmMessage = 'Are you sure to make friends with:\n - ' + this.urls.join('\n - '); + if (!confirm(confirmMessage)) return; + + this.friendService.makeFriends(notEmptyUrls).subscribe( + status => { + if (status === 409) { + alert('Already made friends!'); + } else { + alert('Made friends!'); + } + }, + error => alert(error) + ); + } + + private getNotEmptyUrls() { + const notEmptyUrls = []; + + this.urls.forEach((url) => { + if (url !== '') notEmptyUrls.push(url); + }); + + return notEmptyUrls; + } + + // Temporary + // Use HTML validators + private isUrlsRegexValid(urls: string[]) { + let res = true; + + const urlRegex = new RegExp('^https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$'); + urls.forEach((url) => { + if (urlRegex.test(url) === false) { + res = false; + } + }); + + return res; + } + + private isUrlsUnique(urls: string[]) { + return urls.every(url => urls.indexOf(url) === urls.lastIndexOf(url)); + } +} diff --git a/client/src/app/admin/friends/friend-add/index.ts b/client/src/app/admin/friends/friend-add/index.ts new file mode 100644 index 000000000..a101b3be5 --- /dev/null +++ b/client/src/app/admin/friends/friend-add/index.ts @@ -0,0 +1 @@ +export * from './friend-add.component'; diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.html b/client/src/app/admin/friends/friend-list/friend-list.component.html index f4d14293e..4be3d364f 100644 --- a/client/src/app/admin/friends/friend-list/friend-list.component.html +++ b/client/src/app/admin/friends/friend-list/friend-list.component.html @@ -18,6 +18,6 @@ Quit friends - + Make friends diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.ts b/client/src/app/admin/friends/friend-list/friend-list.component.ts index bf66d3ff1..aa92c1b1e 100644 --- a/client/src/app/admin/friends/friend-list/friend-list.component.ts +++ b/client/src/app/admin/friends/friend-list/friend-list.component.ts @@ -1,11 +1,13 @@ import { Component, OnInit } from '@angular/core'; +import { ROUTER_DIRECTIVES } from '@angular/router'; import { Friend, FriendService } from '../shared'; @Component({ selector: 'my-friend-list', template: require('./friend-list.component.html'), - styles: [ require('./friend-list.component.scss') ] + styles: [ require('./friend-list.component.scss') ], + directives: [ ROUTER_DIRECTIVES ] }) export class FriendListComponent implements OnInit { friends: Friend[]; diff --git a/client/src/app/admin/friends/friends.routes.ts b/client/src/app/admin/friends/friends.routes.ts index 1e3646395..42b4a6c14 100644 --- a/client/src/app/admin/friends/friends.routes.ts +++ b/client/src/app/admin/friends/friends.routes.ts @@ -1,6 +1,7 @@ import { RouterConfig } from '@angular/router'; import { FriendsComponent } from './friends.component'; +import { FriendAddComponent } from './friend-add'; import { FriendListComponent } from './friend-list'; export const FriendsRoutes: RouterConfig = [ @@ -16,6 +17,10 @@ export const FriendsRoutes: RouterConfig = [ { path: 'list', component: FriendListComponent + }, + { + path: 'add', + component: FriendAddComponent } ] } diff --git a/client/src/app/admin/friends/index.ts b/client/src/app/admin/friends/index.ts index 01aeedeee..f3110e31d 100644 --- a/client/src/app/admin/friends/index.ts +++ b/client/src/app/admin/friends/index.ts @@ -1,3 +1,4 @@ -export * from './shared'; +export * from './friend-add'; export * from './friend-list'; +export * from './shared'; export * from './friends.routes'; diff --git a/client/src/app/admin/friends/shared/friend.service.ts b/client/src/app/admin/friends/shared/friend.service.ts index da4d64611..e4e680c29 100644 --- a/client/src/app/admin/friends/shared/friend.service.ts +++ b/client/src/app/admin/friends/shared/friend.service.ts @@ -20,8 +20,12 @@ export class FriendService { .catch(this.handleError); } - makeFriends() { - return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'makefriends') + makeFriends(notEmptyUrls) { + const body = { + urls: notEmptyUrls + }; + + return this.authHttp.post(FriendService.BASE_FRIEND_URL + 'makefriends', body) .map(res => res.status) .catch(this.handleError); } diff --git a/client/src/app/admin/menu-admin.component.html b/client/src/app/admin/menu-admin.component.html index 092ab6081..26a3f3492 100644 --- a/client/src/app/admin/menu-admin.component.html +++ b/client/src/app/admin/menu-admin.component.html @@ -8,14 +8,14 @@ diff --git a/client/src/app/menu.component.html b/client/src/app/menu.component.html index 922375395..8ea99138d 100644 --- a/client/src/app/menu.component.html +++ b/client/src/app/menu.component.html @@ -33,7 +33,7 @@ -- cgit v1.2.3 From 9aa46b0c7bade696a477626ad7590ffdd281e03c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Sun, 21 Aug 2016 11:21:45 +0200 Subject: Client: navigate to /videos/list when do search on another page --- client/src/app/shared/search/search.component.ts | 7 ++++++- client/src/app/shared/search/search.service.ts | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'client/src/app') diff --git a/client/src/app/shared/search/search.component.ts b/client/src/app/shared/search/search.component.ts index 219997e85..853f5dc7c 100644 --- a/client/src/app/shared/search/search.component.ts +++ b/client/src/app/shared/search/search.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; import { DROPDOWN_DIRECTIVES} from 'ng2-bootstrap/components/dropdown'; @@ -25,7 +26,7 @@ export class SearchComponent implements OnInit { value: '' }; - constructor(private searchService: SearchService) {} + constructor(private searchService: SearchService, private router: Router) {} ngOnInit() { // Subscribe if the search changed @@ -58,6 +59,10 @@ export class SearchComponent implements OnInit { } doSearch() { + if (this.router.url.indexOf('/videos/list') === -1) { + this.router.navigate([ '/videos/list' ]); + } + this.searchService.searchUpdated.next(this.searchCriterias); } diff --git a/client/src/app/shared/search/search.service.ts b/client/src/app/shared/search/search.service.ts index c7993db3d..717a7fa50 100644 --- a/client/src/app/shared/search/search.service.ts +++ b/client/src/app/shared/search/search.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs/Subject'; +import { ReplaySubject } from 'rxjs/ReplaySubject'; import { Search } from './search.model'; @@ -12,6 +13,6 @@ export class SearchService { constructor() { this.updateSearch = new Subject(); - this.searchUpdated = new Subject(); + this.searchUpdated = new ReplaySubject(1); } } -- cgit v1.2.3 From beacf6993c93f93bf5ea86665827154eb291d1fd Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Sun, 21 Aug 2016 11:27:24 +0200 Subject: Client: simplify simple menu/admin menu displaying logic --- client/src/app/admin/menu-admin.component.html | 2 +- client/src/app/admin/menu-admin.component.ts | 10 ++-------- client/src/app/app.component.html | 7 ++----- client/src/app/app.component.ts | 12 ++++-------- client/src/app/menu.component.html | 2 +- client/src/app/menu.component.ts | 7 +------ 6 files changed, 11 insertions(+), 29 deletions(-) (limited to 'client/src/app') diff --git a/client/src/app/admin/menu-admin.component.html b/client/src/app/admin/menu-admin.component.html index 26a3f3492..f821974bd 100644 --- a/client/src/app/admin/menu-admin.component.html +++ b/client/src/app/admin/menu-admin.component.html @@ -15,7 +15,7 @@ diff --git a/client/src/app/admin/menu-admin.component.ts b/client/src/app/admin/menu-admin.component.ts index b23f7409e..788592872 100644 --- a/client/src/app/admin/menu-admin.component.ts +++ b/client/src/app/admin/menu-admin.component.ts @@ -1,4 +1,4 @@ -import { Component, Output, EventEmitter } from '@angular/core'; +import { Component } from '@angular/core'; import { ROUTER_DIRECTIVES } from '@angular/router'; @Component({ @@ -6,10 +6,4 @@ import { ROUTER_DIRECTIVES } from '@angular/router'; template: require('./menu-admin.component.html'), directives: [ ROUTER_DIRECTIVES ] }) -export class MenuAdminComponent { - @Output() quittedAdmin = new EventEmitter(); - - quitAdmin() { - this.quittedAdmin.emit(true); - } -} +export class MenuAdminComponent { } diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index a7538ee7a..ead491968 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -14,14 +14,11 @@
- - + +
-
-
-
PeerTube, CopyLeft 2015-2016 diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index d9549ad5b..2e0fd13f1 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; +import { Router, ROUTER_DIRECTIVES } from '@angular/router'; import { MenuAdminComponent } from './admin'; import { MenuComponent } from './menu.component'; @@ -15,13 +15,9 @@ import { VideoService } from './videos'; }) export class AppComponent { - isInAdmin = false; + constructor(private router: Router) {} - onEnteredInAdmin() { - this.isInAdmin = true; - } - - onQuittedAdmin() { - this.isInAdmin = false; + isInAdmin() { + return this.router.url.indexOf('/admin/') !== -1; } } diff --git a/client/src/app/menu.component.html b/client/src/app/menu.component.html index 8ea99138d..29ef7f9cf 100644 --- a/client/src/app/menu.component.html +++ b/client/src/app/menu.component.html @@ -33,7 +33,7 @@ diff --git a/client/src/app/menu.component.ts b/client/src/app/menu.component.ts index 594cd996e..6b08301df 100644 --- a/client/src/app/menu.component.ts +++ b/client/src/app/menu.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { Router, ROUTER_DIRECTIVES } from '@angular/router'; import { AuthService, AuthStatus } from './shared'; @@ -9,7 +9,6 @@ import { AuthService, AuthStatus } from './shared'; directives: [ ROUTER_DIRECTIVES ] }) export class MenuComponent implements OnInit { - @Output() enteredInAdmin = new EventEmitter(); isLoggedIn: boolean; constructor ( @@ -35,10 +34,6 @@ export class MenuComponent implements OnInit { ); } - enterInAdmin() { - this.enteredInAdmin.emit(true); - } - isUserAdmin() { return this.authService.isAdmin(); } -- cgit v1.2.3 From 96b0c2bf708a20c7e0fe1995223b0fdefbdd9f41 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Sun, 21 Aug 2016 11:36:43 +0200 Subject: Client: remove makeFriend from friend-list (in friend-add now) --- .../app/admin/friends/friend-list/friend-list.component.ts | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'client/src/app') diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.ts b/client/src/app/admin/friends/friend-list/friend-list.component.ts index aa92c1b1e..379d44c97 100644 --- a/client/src/app/admin/friends/friend-list/friend-list.component.ts +++ b/client/src/app/admin/friends/friend-list/friend-list.component.ts @@ -22,19 +22,6 @@ export class FriendListComponent implements OnInit { ); } - makeFriends() { - this.friendService.makeFriends().subscribe( - status => { - if (status === 409) { - alert('Already made friends!'); - } else { - alert('Made friends!'); - } - }, - error => alert(error) - ); - } - quitFriends() { if (!confirm('Are you sure?')) return; -- cgit v1.2.3 From 52672600223d840ab123cd48c4c4d0457ac2e1a1 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Aug 2016 11:45:28 +0200 Subject: Client: fix malformed div --- client/src/app/app.component.html | 2 ++ 1 file changed, 2 insertions(+) (limited to 'client/src/app') diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index ead491968..04c32f596 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -19,6 +19,8 @@
+
+
PeerTube, CopyLeft 2015-2016 -- cgit v1.2.3 From 0f6da32b148c0f4146b2ae9ad1add9a9f00cc339 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Aug 2016 14:37:49 +0200 Subject: Client: update to new form api --- client/src/app/account/account.component.html | 8 ++++---- client/src/app/account/account.component.ts | 22 ++++++++++++--------- .../friends/friend-add/friend-add.component.html | 4 ++-- .../friends/friend-add/friend-add.component.ts | 2 +- .../admin/users/user-add/user-add.component.html | 16 +++++++-------- .../app/admin/users/user-add/user-add.component.ts | 18 ++++++++++------- client/src/app/login/login.component.html | 17 ++++++++-------- client/src/app/login/login.component.ts | 23 +++++++++++++++++----- .../app/videos/video-add/video-add.component.html | 14 ++++++------- .../app/videos/video-add/video-add.component.ts | 15 +++++++------- 10 files changed, 80 insertions(+), 59 deletions(-) (limited to 'client/src/app') diff --git a/client/src/app/account/account.component.html b/client/src/app/account/account.component.html index ad8f690bd..4797fa914 100644 --- a/client/src/app/account/account.component.html +++ b/client/src/app/account/account.component.html @@ -3,14 +3,14 @@
{{ information }}
{{ error }}
-
+
-
+
The password should have more than 5 characters
@@ -19,7 +19,7 @@
diff --git a/client/src/app/account/account.component.ts b/client/src/app/account/account.component.ts index 5c42103f8..54939f43b 100644 --- a/client/src/app/account/account.component.ts +++ b/client/src/app/account/account.component.ts @@ -1,5 +1,6 @@ -import { Control, ControlGroup, Validators } from '@angular/common'; +import { Validators } from '@angular/common'; import { Component, OnInit } from '@angular/core'; +import { FormControl, FormGroup, REACTIVE_FORM_DIRECTIVES } from '@angular/forms'; import { Router } from '@angular/router'; import { AccountService } from './account.service'; @@ -7,11 +8,14 @@ import { AccountService } from './account.service'; @Component({ selector: 'my-account', template: require('./account.component.html'), - providers: [ AccountService ] + providers: [ AccountService ], + directives: [ REACTIVE_FORM_DIRECTIVES ] }) export class AccountComponent implements OnInit { - changePasswordForm: ControlGroup; + newPassword = ''; + newConfirmedPassword = ''; + changePasswordForm: FormGroup; information: string = null; error: string = null; @@ -21,22 +25,22 @@ export class AccountComponent implements OnInit { ) {} ngOnInit() { - this.changePasswordForm = new ControlGroup({ - newPassword: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])), - newConfirmedPassword: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])), + this.changePasswordForm = new FormGroup({ + 'new-password': new FormControl('', [ Validators.required, Validators.minLength(6) ]), + 'new-confirmed-password': new FormControl('', [ Validators.required, Validators.minLength(6) ]), }); } - changePassword(newPassword: string, newConfirmedPassword: string) { + changePassword() { this.information = null; this.error = null; - if (newPassword !== newConfirmedPassword) { + if (this.newPassword !== this.newConfirmedPassword) { this.error = 'The new password and the confirmed password do not correspond.'; return; } - this.accountService.changePassword(newPassword).subscribe( + this.accountService.changePassword(this.newPassword).subscribe( ok => this.information = 'Password updated.', err => this.error = err diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.html b/client/src/app/admin/friends/friend-add/friend-add.component.html index a52965e8f..d8bb740b4 100644 --- a/client/src/app/admin/friends/friend-add/friend-add.component.html +++ b/client/src/app/admin/friends/friend-add/friend-add.component.html @@ -2,11 +2,11 @@
{{ error }}
- +
- + diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.ts b/client/src/app/admin/friends/friend-add/friend-add.component.ts index 30dbf4d36..07888a781 100644 --- a/client/src/app/admin/friends/friend-add/friend-add.component.ts +++ b/client/src/app/admin/friends/friend-add/friend-add.component.ts @@ -53,7 +53,7 @@ export class FriendAddComponent { return; } - const confirmMessage = 'Are you sure to make friends with:\n - ' + this.urls.join('\n - '); + const confirmMessage = 'Are you sure to make friends with:\n - ' + notEmptyUrls.join('\n - '); if (!confirm(confirmMessage)) return; this.friendService.makeFriends(notEmptyUrls).subscribe( diff --git a/client/src/app/admin/users/user-add/user-add.component.html b/client/src/app/admin/users/user-add/user-add.component.html index aa102358a..09219893b 100644 --- a/client/src/app/admin/users/user-add/user-add.component.html +++ b/client/src/app/admin/users/user-add/user-add.component.html @@ -2,14 +2,14 @@
{{ error }}
- +
-
+
Username is required with a length >= 3 and <= 20
@@ -17,13 +17,13 @@
-
+
Password is required with a length >= 6
- + diff --git a/client/src/app/admin/users/user-add/user-add.component.ts b/client/src/app/admin/users/user-add/user-add.component.ts index 30ca947a0..b7efd3a80 100644 --- a/client/src/app/admin/users/user-add/user-add.component.ts +++ b/client/src/app/admin/users/user-add/user-add.component.ts @@ -1,5 +1,6 @@ -import { Control, ControlGroup, Validators } from '@angular/common'; +import { Validators } from '@angular/common'; import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormControl, REACTIVE_FORM_DIRECTIVES } from '@angular/forms'; import { Router } from '@angular/router'; import { UserService } from '../shared'; @@ -7,24 +8,27 @@ import { UserService } from '../shared'; @Component({ selector: 'my-user-add', template: require('./user-add.component.html'), + directives: [ REACTIVE_FORM_DIRECTIVES ] }) export class UserAddComponent implements OnInit { - userAddForm: ControlGroup; + userAddForm: FormGroup; error: string = null; + username = ''; + password = ''; constructor(private router: Router, private userService: UserService) {} ngOnInit() { - this.userAddForm = new ControlGroup({ - username: new Control('', Validators.compose([ Validators.required, Validators.minLength(3), Validators.maxLength(20) ])), - password: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])), + this.userAddForm = new FormGroup({ + username: new FormControl('', [ Validators.required, Validators.minLength(3), Validators.maxLength(20) ]), + password: new FormControl('', [ Validators.required, Validators.minLength(6) ]), }); } - addUser(username: string, password: string) { + addUser() { this.error = null; - this.userService.addUser(username, password).subscribe( + this.userService.addUser(this.username, this.password).subscribe( ok => this.router.navigate([ '/admin/users/list' ]), err => this.error = err diff --git a/client/src/app/login/login.component.html b/client/src/app/login/login.component.html index 5848fcba3..636872942 100644 --- a/client/src/app/login/login.component.html +++ b/client/src/app/login/login.component.html @@ -1,16 +1,15 @@

Login

-
{{ error }}
-
+
-
+
Username is required
@@ -18,13 +17,13 @@
-
+
Password is required
- + diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts index ddd62462e..fe867b7b4 100644 --- a/client/src/app/login/login.component.ts +++ b/client/src/app/login/login.component.ts @@ -1,23 +1,36 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { Validators } from '@angular/common'; +import { FormControl, FormGroup, REACTIVE_FORM_DIRECTIVES } from '@angular/forms'; import { Router } from '@angular/router'; import { AuthService } from '../shared'; @Component({ selector: 'my-login', - template: require('./login.component.html') + template: require('./login.component.html'), + directives: [ REACTIVE_FORM_DIRECTIVES ] }) -export class LoginComponent { +export class LoginComponent implements OnInit { error: string = null; + username = ''; + password: ''; + loginForm: FormGroup; constructor( private authService: AuthService, private router: Router ) {} - login(username: string, password: string) { - this.authService.login(username, password).subscribe( + ngOnInit() { + this.loginForm = new FormGroup({ + username: new FormControl('', [ Validators.required ]), + password: new FormControl('', [ Validators.required ]), + }); + } + + login() { + this.authService.login(this.username, this.password).subscribe( result => { this.error = null; diff --git a/client/src/app/videos/video-add/video-add.component.html b/client/src/app/videos/video-add/video-add.component.html index bcd78c7cb..76bb61f7d 100644 --- a/client/src/app/videos/video-add/video-add.component.html +++ b/client/src/app/videos/video-add/video-add.component.html @@ -2,14 +2,14 @@
{{ error }}
-
+
-
+
A name is required and should be between 3 and 50 characters long
@@ -18,9 +18,9 @@ -
+
A tag should be between 2 and 10 characters (alphanumeric) long
@@ -54,10 +54,10 @@ -
+
A description is required and should be between 3 and 250 characters long
diff --git a/client/src/app/videos/video-add/video-add.component.ts b/client/src/app/videos/video-add/video-add.component.ts index c0f8cb9c4..900ef1da3 100644 --- a/client/src/app/videos/video-add/video-add.component.ts +++ b/client/src/app/videos/video-add/video-add.component.ts @@ -1,5 +1,6 @@ -import { Control, ControlGroup, Validators } from '@angular/common'; +import { Validators } from '@angular/common'; import { Component, ElementRef, OnInit } from '@angular/core'; +import { FormControl, FormGroup, REACTIVE_FORM_DIRECTIVES } from '@angular/forms'; import { Router } from '@angular/router'; import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'; @@ -12,14 +13,14 @@ import { AuthService } from '../../shared'; selector: 'my-videos-add', styles: [ require('./video-add.component.scss') ], template: require('./video-add.component.html'), - directives: [ FileSelectDirective, PROGRESSBAR_DIRECTIVES ], + directives: [ FileSelectDirective, PROGRESSBAR_DIRECTIVES, REACTIVE_FORM_DIRECTIVES ], pipes: [ BytesPipe ] }) export class VideoAddComponent implements OnInit { currentTag: string; // Tag the user is writing in the input error: string = null; - videoForm: ControlGroup; + videoForm: FormGroup; uploader: FileUploader; video = { name: '', @@ -70,10 +71,10 @@ export class VideoAddComponent implements OnInit { } ngOnInit() { - this.videoForm = new ControlGroup({ - name: new Control('', Validators.compose([ Validators.required, Validators.minLength(3), Validators.maxLength(50) ])), - description: new Control('', Validators.compose([ Validators.required, Validators.minLength(3), Validators.maxLength(250) ])), - tags: new Control('', Validators.pattern('^[a-zA-Z0-9]{2,10}$')) + this.videoForm = new FormGroup({ + name: new FormControl('', [ Validators.required, Validators.minLength(3), Validators.maxLength(50) ]), + description: new FormControl('', [ Validators.required, Validators.minLength(3), Validators.maxLength(250) ]), + tags: new FormControl('', Validators.pattern('^[a-zA-Z0-9]{2,10}$')) }); -- cgit v1.2.3 From 6be622478a36a20ec36605c772b20009d9d270ac Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Aug 2016 14:43:20 +0200 Subject: Client: display make/quit friends according to the situation --- client/src/app/admin/friends/friend-list/friend-list.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'client/src/app') diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.html b/client/src/app/admin/friends/friend-list/friend-list.component.html index 4be3d364f..1f3789265 100644 --- a/client/src/app/admin/friends/friend-list/friend-list.component.html +++ b/client/src/app/admin/friends/friend-list/friend-list.component.html @@ -14,10 +14,10 @@
- + Quit friends - + Make friends -- cgit v1.2.3 From 9ab1071c8d90d112f1031cf006ed621011553e84 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Aug 2016 14:48:59 +0200 Subject: Do not wait the make friends process ends to send a response to the request --- client/src/app/admin/friends/friend-add/friend-add.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'client/src/app') diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.ts b/client/src/app/admin/friends/friend-add/friend-add.component.ts index 07888a781..ffc499b92 100644 --- a/client/src/app/admin/friends/friend-add/friend-add.component.ts +++ b/client/src/app/admin/friends/friend-add/friend-add.component.ts @@ -61,7 +61,8 @@ export class FriendAddComponent { if (status === 409) { alert('Already made friends!'); } else { - alert('Made friends!'); + alert('Make friends request sent!'); + this.router.navigate([ '/admin/friends/list' ]); } }, error => alert(error) -- cgit v1.2.3 From b5d6b94c1e192cc7b6ace0e73fc8edee2dc510ef Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Aug 2016 15:28:03 +0200 Subject: Client: make friends url button (+/-) -> same width --- client/src/app/admin/friends/friend-add/friend-add.component.scss | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'client/src/app') diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.scss b/client/src/app/admin/friends/friend-add/friend-add.component.scss index cb597e12b..5fde51636 100644 --- a/client/src/app/admin/friends/friend-add/friend-add.component.scss +++ b/client/src/app/admin/friends/friend-add/friend-add.component.scss @@ -1,3 +1,7 @@ table { margin-bottom: 40px; } + +.input-group-btn button { + width: 35px; +} -- cgit v1.2.3 From 9e8aa10d94e4642ec1d20a522c41b06e9df7758b Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Aug 2016 15:49:16 +0200 Subject: Client: change url validation for friend add --- .../friends/friend-add/friend-add.component.html | 16 +++++-- .../friends/friend-add/friend-add.component.ts | 53 ++++++++++++---------- client/src/app/shared/form-validators/index.ts | 1 + .../app/shared/form-validators/url.validator.ts | 11 +++++ client/src/app/shared/index.ts | 1 + 5 files changed, 54 insertions(+), 28 deletions(-) create mode 100644 client/src/app/shared/form-validators/index.ts create mode 100644 client/src/app/shared/form-validators/url.validator.ts (limited to 'client/src/app') diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.html b/client/src/app/admin/friends/friend-add/friend-add.component.html index d8bb740b4..5b8dc8d87 100644 --- a/client/src/app/admin/friends/friend-add/friend-add.component.html +++ b/client/src/app/admin/friends/friend-add/friend-add.component.html @@ -2,17 +2,25 @@
{{ error }}
- +
+
- + - +
+ +
+ It should be a valid url. +
- + diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.ts b/client/src/app/admin/friends/friend-add/friend-add.component.ts index ffc499b92..16cfd8a3a 100644 --- a/client/src/app/admin/friends/friend-add/friend-add.component.ts +++ b/client/src/app/admin/friends/friend-add/friend-add.component.ts @@ -1,20 +1,30 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { FormControl, FormGroup, REACTIVE_FORM_DIRECTIVES } from '@angular/forms'; import { Router } from '@angular/router'; +import { validateUrl } from '../../../shared'; import { FriendService } from '../shared'; @Component({ selector: 'my-friend-add', template: require('./friend-add.component.html'), - styles: [ require('./friend-add.component.scss') ] + styles: [ require('./friend-add.component.scss') ], + directives: [ REACTIVE_FORM_DIRECTIVES ] }) -export class FriendAddComponent { - urls = [ '' ]; +export class FriendAddComponent implements OnInit { + friendAddForm: FormGroup; + urls = [ ]; error: string = null; constructor(private router: Router, private friendService: FriendService) {} + ngOnInit() { + this.friendAddForm = new FormGroup({}); + this.addField(); + } + addField() { + this.friendAddForm.addControl(`url-${this.urls.length}`, new FormControl('', [ validateUrl ])); this.urls.push(''); } @@ -30,6 +40,21 @@ export class FriendAddComponent { return (index !== 0 || this.urls.length > 1) && index !== (this.urls.length - 1); } + isFormValid() { + // Do not check the last input + for (let i = 0; i < this.urls.length - 1; i++) { + if (!this.friendAddForm.controls[`url-${i}`].valid) return false; + } + + const lastIndex = this.urls.length - 1; + // If the last input (which is not the first) is empty, it's ok + if (this.urls[lastIndex] === '' && lastIndex !== 0) { + return true; + } else { + return this.friendAddForm.controls[`url-${lastIndex}`].valid; + } + } + removeField(index: number) { this.urls.splice(index, 1); } @@ -43,11 +68,6 @@ export class FriendAddComponent { return; } - if (!this.isUrlsRegexValid(notEmptyUrls)) { - this.error = 'Some url(s) are not valid.'; - return; - } - if (!this.isUrlsUnique(notEmptyUrls)) { this.error = 'Urls need to be unique.'; return; @@ -79,21 +99,6 @@ export class FriendAddComponent { return notEmptyUrls; } - // Temporary - // Use HTML validators - private isUrlsRegexValid(urls: string[]) { - let res = true; - - const urlRegex = new RegExp('^https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$'); - urls.forEach((url) => { - if (urlRegex.test(url) === false) { - res = false; - } - }); - - return res; - } - private isUrlsUnique(urls: string[]) { return urls.every(url => urls.indexOf(url) === urls.lastIndexOf(url)); } diff --git a/client/src/app/shared/form-validators/index.ts b/client/src/app/shared/form-validators/index.ts new file mode 100644 index 000000000..f9e9a6191 --- /dev/null +++ b/client/src/app/shared/form-validators/index.ts @@ -0,0 +1 @@ +export * from './url.validator'; diff --git a/client/src/app/shared/form-validators/url.validator.ts b/client/src/app/shared/form-validators/url.validator.ts new file mode 100644 index 000000000..67163b4e9 --- /dev/null +++ b/client/src/app/shared/form-validators/url.validator.ts @@ -0,0 +1,11 @@ +import { FormControl } from '@angular/forms'; + +export function validateUrl(c: FormControl) { + let URL_REGEXP = new RegExp('^https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$'); + + return URL_REGEXP.test(c.value) ? null : { + validateUrl: { + valid: false + } + }; +} diff --git a/client/src/app/shared/index.ts b/client/src/app/shared/index.ts index c05e8d253..9edf9b4a0 100644 --- a/client/src/app/shared/index.ts +++ b/client/src/app/shared/index.ts @@ -1,3 +1,4 @@ export * from './auth'; +export * from './form-validators'; export * from './search'; export * from './users'; -- cgit v1.2.3 From de59c48f5f317018e3f746bbe4a7b7efe00109f2 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Aug 2016 16:54:21 +0200 Subject: Client: centralize http res extraction in a service --- client/src/app/account/account.service.ts | 12 +++-- .../src/app/admin/friends/shared/friend.service.ts | 22 ++++---- client/src/app/admin/users/shared/user.service.ts | 28 +++++----- client/src/app/app.component.ts | 4 +- client/src/app/login/login.component.ts | 6 +-- client/src/app/shared/auth/auth.service.ts | 22 ++++---- client/src/app/shared/index.ts | 1 + client/src/app/shared/rest/index.ts | 3 ++ .../src/app/shared/rest/rest-extractor.service.ts | 46 ++++++++++++++++ client/src/app/shared/rest/rest-pagination.ts | 5 ++ client/src/app/shared/rest/rest.service.ts | 27 ++++++++++ client/src/app/videos/shared/index.ts | 1 - client/src/app/videos/shared/pagination.model.ts | 5 -- client/src/app/videos/shared/video.service.ts | 62 ++++++++-------------- .../app/videos/video-list/video-list.component.ts | 5 +- 15 files changed, 151 insertions(+), 98 deletions(-) create mode 100644 client/src/app/shared/rest/index.ts create mode 100644 client/src/app/shared/rest/rest-extractor.service.ts create mode 100644 client/src/app/shared/rest/rest-pagination.ts create mode 100644 client/src/app/shared/rest/rest.service.ts delete mode 100644 client/src/app/videos/shared/pagination.model.ts (limited to 'client/src/app') diff --git a/client/src/app/account/account.service.ts b/client/src/app/account/account.service.ts index 19b4e0624..355bcef74 100644 --- a/client/src/app/account/account.service.ts +++ b/client/src/app/account/account.service.ts @@ -1,12 +1,16 @@ import { Injectable } from '@angular/core'; -import { AuthHttp, AuthService } from '../shared'; +import { AuthHttp, AuthService, RestExtractor } from '../shared'; @Injectable() export class AccountService { private static BASE_USERS_URL = '/api/v1/users/'; - constructor(private authHttp: AuthHttp, private authService: AuthService) { } + constructor( + private authHttp: AuthHttp, + private authService: AuthService, + private restExtractor: RestExtractor + ) {} changePassword(newPassword: string) { const url = AccountService.BASE_USERS_URL + this.authService.getUser().id; @@ -14,6 +18,8 @@ export class AccountService { password: newPassword }; - return this.authHttp.put(url, body); + return this.authHttp.put(url, body) + .map(this.restExtractor.extractDataBool) + .catch((res) => this.restExtractor.handleError(res)); } } diff --git a/client/src/app/admin/friends/shared/friend.service.ts b/client/src/app/admin/friends/shared/friend.service.ts index e4e680c29..75826fc17 100644 --- a/client/src/app/admin/friends/shared/friend.service.ts +++ b/client/src/app/admin/friends/shared/friend.service.ts @@ -1,9 +1,8 @@ import { Injectable } from '@angular/core'; -import { Response } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { Friend } from './friend.model'; -import { AuthHttp, AuthService } from '../../../shared'; +import { AuthHttp, RestExtractor } from '../../../shared'; @Injectable() export class FriendService { @@ -11,13 +10,15 @@ export class FriendService { constructor ( private authHttp: AuthHttp, - private authService: AuthService + private restExtractor: RestExtractor ) {} getFriends(): Observable { return this.authHttp.get(FriendService.BASE_FRIEND_URL) - .map(res => res.json()) - .catch(this.handleError); + // Not implemented as a data list by the server yet + // .map(this.restExtractor.extractDataList) + .map((res) => res.json()) + .catch((res) => this.restExtractor.handleError(res)); } makeFriends(notEmptyUrls) { @@ -26,18 +27,13 @@ export class FriendService { }; return this.authHttp.post(FriendService.BASE_FRIEND_URL + 'makefriends', body) - .map(res => res.status) - .catch(this.handleError); + .map(this.restExtractor.extractDataBool) + .catch((res) => this.restExtractor.handleError(res)); } quitFriends() { return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'quitfriends') .map(res => res.status) - .catch(this.handleError); - } - - private handleError (error: Response) { - console.error(error); - return Observable.throw(error.json().error || 'Server error'); + .catch((res) => this.restExtractor.handleError(res)); } } diff --git a/client/src/app/admin/users/shared/user.service.ts b/client/src/app/admin/users/shared/user.service.ts index be433f0a1..d96db4575 100644 --- a/client/src/app/admin/users/shared/user.service.ts +++ b/client/src/app/admin/users/shared/user.service.ts @@ -1,15 +1,16 @@ import { Injectable } from '@angular/core'; -import { Response } from '@angular/http'; -import { Observable } from 'rxjs/Observable'; -import { AuthHttp, User } from '../../../shared'; +import { AuthHttp, RestExtractor, ResultList, User } from '../../../shared'; @Injectable() export class UserService { // TODO: merge this constant with account private static BASE_USERS_URL = '/api/v1/users/'; - constructor(private authHttp: AuthHttp) {} + constructor( + private authHttp: AuthHttp, + private restExtractor: RestExtractor + ) {} addUser(username: string, password: string) { const body = { @@ -17,23 +18,25 @@ export class UserService { password }; - return this.authHttp.post(UserService.BASE_USERS_URL, body); + return this.authHttp.post(UserService.BASE_USERS_URL, body) + .map(this.restExtractor.extractDataBool) + .catch((res) => this.restExtractor.handleError(res)); } getUsers() { return this.authHttp.get(UserService.BASE_USERS_URL) - .map(res => res.json()) + .map(this.restExtractor.extractDataList) .map(this.extractUsers) - .catch(this.handleError); + .catch((res) => this.restExtractor.handleError(res)); } removeUser(user: User) { return this.authHttp.delete(UserService.BASE_USERS_URL + user.id); } - private extractUsers(body: any) { - const usersJson = body.data; - const totalUsers = body.total; + private extractUsers(result: ResultList) { + const usersJson = result.data; + const totalUsers = result.total; const users = []; for (const userJson of usersJson) { users.push(new User(userJson)); @@ -41,9 +44,4 @@ export class UserService { return { users, totalUsers }; } - - private handleError(error: Response) { - console.error(error); - return Observable.throw(error.json().error || 'Server error'); - } } diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 2e0fd13f1..9d05c272f 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -3,7 +3,7 @@ import { Router, ROUTER_DIRECTIVES } from '@angular/router'; import { MenuAdminComponent } from './admin'; import { MenuComponent } from './menu.component'; -import { SearchComponent, SearchService } from './shared'; +import { RestExtractor, RestService, SearchComponent, SearchService } from './shared'; import { VideoService } from './videos'; @Component({ @@ -11,7 +11,7 @@ import { VideoService } from './videos'; template: require('./app.component.html'), styles: [ require('./app.component.scss') ], directives: [ MenuAdminComponent, MenuComponent, ROUTER_DIRECTIVES, SearchComponent ], - providers: [ VideoService, SearchService ] + providers: [ RestExtractor, RestService, VideoService, SearchService ] }) export class AppComponent { diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts index fe867b7b4..1e0ba0fe8 100644 --- a/client/src/app/login/login.component.ts +++ b/client/src/app/login/login.component.ts @@ -37,12 +37,12 @@ export class LoginComponent implements OnInit { this.router.navigate(['/videos/list']); }, error => { - console.error(error); + console.error(error.json); - if (error.error === 'invalid_grant') { + if (error.json.error === 'invalid_grant') { this.error = 'Credentials are invalid.'; } else { - this.error = `${error.error}: ${error.error_description}`; + this.error = `${error.json.error}: ${error.json.error_description}`; } } ); diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts index 8eea0c4bf..2273048c8 100644 --- a/client/src/app/shared/auth/auth.service.ts +++ b/client/src/app/shared/auth/auth.service.ts @@ -1,10 +1,11 @@ import { Injectable } from '@angular/core'; -import { Headers, Http, Response, URLSearchParams } from '@angular/http'; +import { Headers, Http, URLSearchParams } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; import { AuthStatus } from './auth-status.model'; import { AuthUser } from './auth-user.model'; +import { RestExtractor } from '../rest'; @Injectable() export class AuthService { @@ -19,15 +20,15 @@ export class AuthService { private loginChanged: Subject; private user: AuthUser = null; - constructor(private http: Http) { + constructor(private http: Http, private restExtractor: RestExtractor) { this.loginChanged = new Subject(); this.loginChangedSource = this.loginChanged.asObservable(); // Fetch the client_id/client_secret // FIXME: save in local storage? this.http.get(AuthService.BASE_CLIENT_URL) - .map(res => res.json()) - .catch(this.handleError) + .map(this.restExtractor.extractDataGet) + .catch((res) => this.restExtractor.handleError(res)) .subscribe( result => { this.clientId = result.client_id; @@ -101,14 +102,14 @@ export class AuthService { }; return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) - .map(res => res.json()) + .map(this.restExtractor.extractDataGet) .map(res => { res.username = username; return res; }) .flatMap(res => this.fetchUserInformations(res)) .map(res => this.handleLogin(res)) - .catch(this.handleError); + .catch((res) => this.restExtractor.handleError(res)); } logout() { @@ -139,9 +140,9 @@ export class AuthService { }; return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) - .map(res => res.json()) + .map(this.restExtractor.extractDataGet) .map(res => this.handleRefreshToken(res)) - .catch(this.handleError); + .catch((res) => this.restExtractor.handleError(res)); } private fetchUserInformations (obj: any) { @@ -160,11 +161,6 @@ export class AuthService { ); } - private handleError (error: Response) { - console.error(error); - return Observable.throw(error.json() || { error: 'Server error' }); - } - private handleLogin (obj: any) { const id = obj.id; const username = obj.username; diff --git a/client/src/app/shared/index.ts b/client/src/app/shared/index.ts index 9edf9b4a0..c362a0e4a 100644 --- a/client/src/app/shared/index.ts +++ b/client/src/app/shared/index.ts @@ -1,4 +1,5 @@ export * from './auth'; export * from './form-validators'; +export * from './rest'; export * from './search'; export * from './users'; diff --git a/client/src/app/shared/rest/index.ts b/client/src/app/shared/rest/index.ts new file mode 100644 index 000000000..3c9509dc7 --- /dev/null +++ b/client/src/app/shared/rest/index.ts @@ -0,0 +1,3 @@ +export * from './rest-extractor.service'; +export * from './rest-pagination'; +export * from './rest.service'; diff --git a/client/src/app/shared/rest/rest-extractor.service.ts b/client/src/app/shared/rest/rest-extractor.service.ts new file mode 100644 index 000000000..aa44799af --- /dev/null +++ b/client/src/app/shared/rest/rest-extractor.service.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@angular/core'; +import { Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; + +export interface ResultList { + data: any[]; + total: number; +} + +@Injectable() +export class RestExtractor { + + constructor () { ; } + + extractDataBool(res: Response) { + return true; + } + + extractDataList(res: Response) { + const body = res.json(); + + const ret: ResultList = { + data: body.data, + total: body.total + }; + + return ret; + } + + extractDataGet(res: Response) { + return res.json(); + } + + handleError(res: Response) { + let text = 'Server error: '; + text += res.text(); + let json = res.json(); + + const error = { + json, + text + }; + + return Observable.throw(error); + } +} diff --git a/client/src/app/shared/rest/rest-pagination.ts b/client/src/app/shared/rest/rest-pagination.ts new file mode 100644 index 000000000..0cfa4f468 --- /dev/null +++ b/client/src/app/shared/rest/rest-pagination.ts @@ -0,0 +1,5 @@ +export interface RestPagination { + currentPage: number; + itemsPerPage: number; + totalItems: number; +}; diff --git a/client/src/app/shared/rest/rest.service.ts b/client/src/app/shared/rest/rest.service.ts new file mode 100644 index 000000000..16b47e957 --- /dev/null +++ b/client/src/app/shared/rest/rest.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { URLSearchParams } from '@angular/http'; + +import { RestPagination } from './rest-pagination'; + +@Injectable() +export class RestService { + + buildRestGetParams(pagination?: RestPagination, sort?: string) { + const params = new URLSearchParams(); + + if (pagination) { + const start: number = (pagination.currentPage - 1) * pagination.itemsPerPage; + const count: number = pagination.itemsPerPage; + + params.set('start', start.toString()); + params.set('count', count.toString()); + } + + if (sort) { + params.set('sort', sort); + } + + return params; + } + +} diff --git a/client/src/app/videos/shared/index.ts b/client/src/app/videos/shared/index.ts index a54120f5d..67d16ead1 100644 --- a/client/src/app/videos/shared/index.ts +++ b/client/src/app/videos/shared/index.ts @@ -1,5 +1,4 @@ export * from './loader'; -export * from './pagination.model'; export * from './sort-field.type'; export * from './video.model'; export * from './video.service'; diff --git a/client/src/app/videos/shared/pagination.model.ts b/client/src/app/videos/shared/pagination.model.ts deleted file mode 100644 index eda44ebfb..000000000 --- a/client/src/app/videos/shared/pagination.model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface Pagination { - currentPage: number; - itemsPerPage: number; - totalItems: number; -} diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts index b4396f767..ad8557533 100644 --- a/client/src/app/videos/shared/video.service.ts +++ b/client/src/app/videos/shared/video.service.ts @@ -1,11 +1,10 @@ import { Injectable } from '@angular/core'; -import { Http, Response, URLSearchParams } from '@angular/http'; +import { Http } from '@angular/http'; import { Observable } from 'rxjs/Observable'; -import { Pagination } from './pagination.model'; import { Search } from '../../shared'; import { SortField } from './sort-field.type'; -import { AuthHttp, AuthService } from '../../shared'; +import { AuthHttp, AuthService, RestExtractor, RestPagination, RestService, ResultList } from '../../shared'; import { Video } from './video.model'; @Injectable() @@ -15,68 +14,51 @@ export class VideoService { constructor( private authService: AuthService, private authHttp: AuthHttp, - private http: Http + private http: Http, + private restExtractor: RestExtractor, + private restService: RestService ) {} - getVideo(id: string) { + getVideo(id: string): Observable
+
+ Maximum number of requests per interval: + {{ stats.maxRequestsInParallel }} +
+
Remaining requests: {{ stats.requests.length }} diff --git a/client/src/app/admin/requests/shared/request-stats.model.ts b/client/src/app/admin/requests/shared/request-stats.model.ts index dfa956f10..766e80836 100644 --- a/client/src/app/admin/requests/shared/request-stats.model.ts +++ b/client/src/app/admin/requests/shared/request-stats.model.ts @@ -4,15 +4,18 @@ export interface Request { } export class RequestStats { + maxRequestsInParallel: number; milliSecondsInterval: number; - remainingMilliSeconds: number; + remainingMilliSeconds: number; requests: Request[]; constructor(hash: { + maxRequestsInParallel: number, milliSecondsInterval: number, remainingMilliSeconds: number, requests: Request[]; }) { + this.maxRequestsInParallel = hash.maxRequestsInParallel; this.milliSecondsInterval = hash.milliSecondsInterval; this.remainingMilliSeconds = hash.remainingMilliSeconds; this.requests = hash.requests; -- cgit v1.2.3