From 41a2aee38cf812510010da09de9bae53590ec119 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 27 May 2016 16:23:10 +0200 Subject: Follow the angular styleguide for the directories structure --- client/app/app.component.html | 60 ++++++++++++ client/app/app.component.scss | 32 ++++++ client/app/app.component.ts | 109 +++++++++++++++++++++ client/app/friends/friend.service.ts | 27 +++++ client/app/friends/index.ts | 1 + client/app/shared/index.ts | 3 + client/app/shared/search-field.type.ts | 1 + client/app/shared/search.component.html | 17 ++++ client/app/shared/search.component.ts | 47 +++++++++ client/app/shared/search.model.ts | 6 ++ client/app/users/index.ts | 2 + client/app/users/login/index.ts | 1 + client/app/users/login/login.component.html | 14 +++ client/app/users/login/login.component.scss | 0 client/app/users/login/login.component.ts | 34 +++++++ client/app/users/shared/auth-status.model.ts | 4 + client/app/users/shared/auth.service.ts | 107 ++++++++++++++++++++ client/app/users/shared/index.ts | 4 + client/app/users/shared/token.model.ts | 31 ++++++ client/app/users/shared/user.model.ts | 20 ++++ client/app/videos/index.ts | 4 + client/app/videos/shared/index.ts | 5 + client/app/videos/shared/loader/index.ts | 1 + .../app/videos/shared/loader/loader.component.html | 3 + .../app/videos/shared/loader/loader.component.scss | 26 +++++ .../app/videos/shared/loader/loader.component.ts | 11 +++ client/app/videos/shared/pagination.model.ts | 5 + client/app/videos/shared/sort-field.type.ts | 3 + client/app/videos/shared/video.model.ts | 63 ++++++++++++ client/app/videos/shared/video.service.ts | 79 +++++++++++++++ client/app/videos/video-add/index.ts | 1 + .../app/videos/video-add/video-add.component.html | 41 ++++++++ .../app/videos/video-add/video-add.component.scss | 33 +++++++ client/app/videos/video-add/video-add.component.ts | 68 +++++++++++++ client/app/videos/video-list/index.ts | 3 + .../videos/video-list/video-list.component.html | 18 ++++ .../videos/video-list/video-list.component.scss | 37 +++++++ .../app/videos/video-list/video-list.component.ts | 101 +++++++++++++++++++ .../video-list/video-miniature.component.html | 22 +++++ .../video-list/video-miniature.component.scss | 55 +++++++++++ .../videos/video-list/video-miniature.component.ts | 46 +++++++++ .../videos/video-list/video-sort.component.html | 5 + .../app/videos/video-list/video-sort.component.ts | 36 +++++++ client/app/videos/video-watch/index.ts | 1 + .../videos/video-watch/video-watch.component.html | 10 ++ .../videos/video-watch/video-watch.component.scss | 13 +++ .../videos/video-watch/video-watch.component.ts | 75 ++++++++++++++ 47 files changed, 1285 insertions(+) create mode 100644 client/app/app.component.html create mode 100644 client/app/app.component.scss create mode 100644 client/app/app.component.ts create mode 100644 client/app/friends/friend.service.ts create mode 100644 client/app/friends/index.ts create mode 100644 client/app/shared/index.ts create mode 100644 client/app/shared/search-field.type.ts create mode 100644 client/app/shared/search.component.html create mode 100644 client/app/shared/search.component.ts create mode 100644 client/app/shared/search.model.ts create mode 100644 client/app/users/index.ts create mode 100644 client/app/users/login/index.ts create mode 100644 client/app/users/login/login.component.html create mode 100644 client/app/users/login/login.component.scss create mode 100644 client/app/users/login/login.component.ts create mode 100644 client/app/users/shared/auth-status.model.ts create mode 100644 client/app/users/shared/auth.service.ts create mode 100644 client/app/users/shared/index.ts create mode 100644 client/app/users/shared/token.model.ts create mode 100644 client/app/users/shared/user.model.ts create mode 100644 client/app/videos/index.ts create mode 100644 client/app/videos/shared/index.ts create mode 100644 client/app/videos/shared/loader/index.ts create mode 100644 client/app/videos/shared/loader/loader.component.html create mode 100644 client/app/videos/shared/loader/loader.component.scss create mode 100644 client/app/videos/shared/loader/loader.component.ts create mode 100644 client/app/videos/shared/pagination.model.ts create mode 100644 client/app/videos/shared/sort-field.type.ts create mode 100644 client/app/videos/shared/video.model.ts create mode 100644 client/app/videos/shared/video.service.ts create mode 100644 client/app/videos/video-add/index.ts create mode 100644 client/app/videos/video-add/video-add.component.html create mode 100644 client/app/videos/video-add/video-add.component.scss create mode 100644 client/app/videos/video-add/video-add.component.ts create mode 100644 client/app/videos/video-list/index.ts create mode 100644 client/app/videos/video-list/video-list.component.html create mode 100644 client/app/videos/video-list/video-list.component.scss create mode 100644 client/app/videos/video-list/video-list.component.ts create mode 100644 client/app/videos/video-list/video-miniature.component.html create mode 100644 client/app/videos/video-list/video-miniature.component.scss create mode 100644 client/app/videos/video-list/video-miniature.component.ts create mode 100644 client/app/videos/video-list/video-sort.component.html create mode 100644 client/app/videos/video-list/video-sort.component.ts create mode 100644 client/app/videos/video-watch/index.ts create mode 100644 client/app/videos/video-watch/video-watch.component.html create mode 100644 client/app/videos/video-watch/video-watch.component.scss create mode 100644 client/app/videos/video-watch/video-watch.component.ts (limited to 'client/app') diff --git a/client/app/app.component.html b/client/app/app.component.html new file mode 100644 index 000000000..48e97d523 --- /dev/null +++ b/client/app/app.component.html @@ -0,0 +1,60 @@ +
+ +
+
+

PeerTube

+
+ +
+ +
+
+ + +
+ + +
+
+ + Login + Logout +
+
+ +
+
+ + Get videos +
+ + +
+ +
+
+ + Make friends +
+ +
+ + Quit friends +
+
+
+ +
+ +
+ +
+ + +
+ PeerTube, CopyLeft 2015-2016 +
+
diff --git a/client/app/app.component.scss b/client/app/app.component.scss new file mode 100644 index 000000000..e02c2d5b0 --- /dev/null +++ b/client/app/app.component.scss @@ -0,0 +1,32 @@ +header div { + line-height: 25px; + margin-bottom: 30px; +} + +menu { + 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); +} diff --git a/client/app/app.component.ts b/client/app/app.component.ts new file mode 100644 index 000000000..c94ff79a7 --- /dev/null +++ b/client/app/app.component.ts @@ -0,0 +1,109 @@ +import { Component } from '@angular/core'; +import { HTTP_PROVIDERS } from '@angular/http'; +import { RouteConfig, Router, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated'; + +import { FriendService } from './friends/index'; +import { Search, SearchComponent } from './shared/index'; +import { + UserLoginComponent, + AuthService, + AuthStatus +} from './users/index'; +import { + VideoAddComponent, + VideoListComponent, + VideoWatchComponent, + VideoService +} from './videos/index'; + +@RouteConfig([ + { + path: '/users/login', + name: 'UserLogin', + component: UserLoginComponent + }, + { + path: '/videos/list', + name: 'VideosList', + component: VideoListComponent, + useAsDefault: true + }, + { + path: '/videos/watch/:id', + name: 'VideosWatch', + component: VideoWatchComponent + }, + { + path: '/videos/add', + name: 'VideosAdd', + component: VideoAddComponent + } +]) + +@Component({ + selector: 'my-app', + templateUrl: 'client/app/app.component.html', + styleUrls: [ 'client/app/app.component.css' ], + directives: [ ROUTER_DIRECTIVES, SearchComponent ], + providers: [ ROUTER_PROVIDERS, HTTP_PROVIDERS, VideoService, FriendService, AuthService ] +}) + +export class AppComponent { + isLoggedIn: boolean; + search_field: string = name; + choices = [ ]; + + constructor(private _friendService: FriendService, + private _authService: AuthService, + private _router: Router + + ) { + this.isLoggedIn = this._authService.isLoggedIn(); + + this._authService.loginChanged$.subscribe( + status => { + if (status === AuthStatus.LoggedIn) { + this.isLoggedIn = true; + } + } + ); + } + + onSearch(search: Search) { + if (search.value !== '') { + const params = { + search: search.value, + field: search.field + }; + this._router.navigate(['VideosList', params]); + } else { + this._router.navigate(['VideosList']); + } + } + + logout() { + // this._authService.logout(); + } + + makeFriends() { + this._friendService.makeFriends().subscribe( + status => { + if (status === 409) { + alert('Already made friends!'); + } else { + alert('Made friends!'); + } + }, + error => alert(error) + ); + } + + quitFriends() { + this._friendService.quitFriends().subscribe( + status => { + alert('Quit friends!'); + }, + error => alert(error) + ); + } +} diff --git a/client/app/friends/friend.service.ts b/client/app/friends/friend.service.ts new file mode 100644 index 000000000..d143ec40d --- /dev/null +++ b/client/app/friends/friend.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class FriendService { + private _baseFriendsUrl = '/api/v1/pods/'; + + constructor (private http: Http) {} + + makeFriends() { + return this.http.get(this._baseFriendsUrl + 'makefriends') + .map(res => res.status) + .catch(this.handleError); + } + + quitFriends() { + return this.http.get(this._baseFriendsUrl + '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/app/friends/index.ts b/client/app/friends/index.ts new file mode 100644 index 000000000..0adc256c4 --- /dev/null +++ b/client/app/friends/index.ts @@ -0,0 +1 @@ +export * from './friend.service'; diff --git a/client/app/shared/index.ts b/client/app/shared/index.ts new file mode 100644 index 000000000..a49a4f1a9 --- /dev/null +++ b/client/app/shared/index.ts @@ -0,0 +1,3 @@ +export * from './search-field.type'; +export * from './search.component'; +export * from './search.model'; diff --git a/client/app/shared/search-field.type.ts b/client/app/shared/search-field.type.ts new file mode 100644 index 000000000..846236290 --- /dev/null +++ b/client/app/shared/search-field.type.ts @@ -0,0 +1 @@ +export type SearchField = "name" | "author" | "podUrl" | "magnetUri"; diff --git a/client/app/shared/search.component.html b/client/app/shared/search.component.html new file mode 100644 index 000000000..fb13ac72e --- /dev/null +++ b/client/app/shared/search.component.html @@ -0,0 +1,17 @@ +
+
+ + +
+ + +
diff --git a/client/app/shared/search.component.ts b/client/app/shared/search.component.ts new file mode 100644 index 000000000..519810f9b --- /dev/null +++ b/client/app/shared/search.component.ts @@ -0,0 +1,47 @@ +import { Component, EventEmitter, Output } from '@angular/core'; + +import { DROPDOWN_DIRECTIVES} from 'ng2-bootstrap/components/dropdown'; + +import { Search } from './search.model'; +import { SearchField } from './search-field.type'; + +@Component({ + selector: 'my-search', + templateUrl: 'client/app/shared/search.component.html', + directives: [ DROPDOWN_DIRECTIVES ] +}) + +export class SearchComponent { + @Output() search: EventEmitter = new EventEmitter(); + + searchCriterias: Search = { + field: 'name', + value: '' + }; + fieldChoices = { + name: 'Name', + author: 'Author', + podUrl: 'Pod Url', + magnetUri: 'Magnet Uri' + }; + + get choiceKeys() { + return Object.keys(this.fieldChoices); + } + + getStringChoice(choiceKey: SearchField): string { + return this.fieldChoices[choiceKey]; + } + + choose($event:MouseEvent, choice: SearchField) { + $event.preventDefault(); + $event.stopPropagation(); + + this.searchCriterias.field = choice; + } + + doSearch(): void { + this.search.emit(this.searchCriterias); + } + +} diff --git a/client/app/shared/search.model.ts b/client/app/shared/search.model.ts new file mode 100644 index 000000000..932a6566c --- /dev/null +++ b/client/app/shared/search.model.ts @@ -0,0 +1,6 @@ +import { SearchField } from './search-field.type'; + +export interface Search { + field: SearchField; + value: string; +} diff --git a/client/app/users/index.ts b/client/app/users/index.ts new file mode 100644 index 000000000..4f08b8bc7 --- /dev/null +++ b/client/app/users/index.ts @@ -0,0 +1,2 @@ +export * from './login/index'; +export * from './shared/index'; diff --git a/client/app/users/login/index.ts b/client/app/users/login/index.ts new file mode 100644 index 000000000..69c16441f --- /dev/null +++ b/client/app/users/login/index.ts @@ -0,0 +1 @@ +export * from './login.component'; diff --git a/client/app/users/login/login.component.html b/client/app/users/login/login.component.html new file mode 100644 index 000000000..940694515 --- /dev/null +++ b/client/app/users/login/login.component.html @@ -0,0 +1,14 @@ +

Login

+
+
+ + +
+ +
+ + +
+ + +
diff --git a/client/app/users/login/login.component.scss b/client/app/users/login/login.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/app/users/login/login.component.ts b/client/app/users/login/login.component.ts new file mode 100644 index 000000000..33590ad4c --- /dev/null +++ b/client/app/users/login/login.component.ts @@ -0,0 +1,34 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router-deprecated'; + +import { AuthService, AuthStatus, User } from '../shared/index'; + +@Component({ + selector: 'my-user-login', + styleUrls: [ 'client/app/users/login/login.component.css' ], + templateUrl: 'client/app/users/login/login.component.html' +}) + +export class UserLoginComponent { + constructor(private _authService: AuthService, private _router: Router) {} + + login(username: string, password: string) { + this._authService.login(username, password).subscribe( + result => { + const user = new User(username, result); + user.save(); + + this._authService.setStatus(AuthStatus.LoggedIn); + + this._router.navigate(['VideosList']); + }, + error => { + if (error.error === 'invalid_grant') { + alert('Credentials are invalid.'); + } else { + alert(`${error.error}: ${error.error_description}`); + } + } + ); + } +} diff --git a/client/app/users/shared/auth-status.model.ts b/client/app/users/shared/auth-status.model.ts new file mode 100644 index 000000000..f646bd4cf --- /dev/null +++ b/client/app/users/shared/auth-status.model.ts @@ -0,0 +1,4 @@ +export enum AuthStatus { + LoggedIn, + LoggedOut +} diff --git a/client/app/users/shared/auth.service.ts b/client/app/users/shared/auth.service.ts new file mode 100644 index 000000000..1cb042db5 --- /dev/null +++ b/client/app/users/shared/auth.service.ts @@ -0,0 +1,107 @@ +import { Injectable } from '@angular/core'; +import { Headers, Http, RequestOptions, Response, URLSearchParams } from '@angular/http'; +import { Observable, Subject } from 'rxjs/Rx'; + +import { AuthStatus } from './auth-status.model'; +import { User } from './user.model'; + +@Injectable() +export class AuthService { + loginChanged$; + + private _loginChanged; + private _baseLoginUrl = '/api/v1/users/token'; + private _baseClientUrl = '/api/v1/users/client'; + private _clientId = ''; + private _clientSecret = ''; + + constructor (private http: Http) { + this._loginChanged = new Subject(); + this.loginChanged$ = this._loginChanged.asObservable(); + + // Fetch the client_id/client_secret + // FIXME: save in local storage? + this.http.get(this._baseClientUrl) + .map(res => res.json()) + .catch(this.handleError) + .subscribe( + result => { + this._clientId = result.client_id; + this._clientSecret = result.client_secret; + console.log('Client credentials loaded.'); + }, + error => { + alert(error); + } + ); + } + + login(username: string, password: string) { + let body = new URLSearchParams(); + body.set('client_id', this._clientId); + body.set('client_secret', this._clientSecret); + body.set('response_type', 'code'); + body.set('grant_type', 'password'); + body.set('scope', 'upload'); + body.set('username', username); + body.set('password', password); + + let headers = new Headers(); + headers.append('Content-Type', 'application/x-www-form-urlencoded'); + + let options = { + headers: headers + }; + + return this.http.post(this._baseLoginUrl, body.toString(), options) + .map(res => res.json()) + .catch(this.handleError); + } + + logout() { + // TODO make HTTP request + } + + getRequestHeader(): Headers { + return new Headers({ 'Authorization': `${this.getTokenType()} ${this.getToken()}` }); + } + + getAuthRequestOptions(): RequestOptions { + return new RequestOptions({ headers: this.getRequestHeader() }); + } + + getToken(): string { + return localStorage.getItem('access_token'); + } + + getTokenType(): string { + return localStorage.getItem('token_type'); + } + + getUser(): User { + if (this.isLoggedIn() === false) { + return null; + } + + const user = User.load(); + + return user; + } + + isLoggedIn(): boolean { + if (this.getToken()) { + return true; + } else { + return false; + } + } + + setStatus(status: AuthStatus) { + this._loginChanged.next(status); + } + + private handleError (error: Response) { + console.error(error); + return Observable.throw(error.json() || { error: 'Server error' }); + } +} diff --git a/client/app/users/shared/index.ts b/client/app/users/shared/index.ts new file mode 100644 index 000000000..c6816b3c6 --- /dev/null +++ b/client/app/users/shared/index.ts @@ -0,0 +1,4 @@ +export * from './auth-status.model'; +export * from './auth.service'; +export * from './token.model'; +export * from './user.model'; diff --git a/client/app/users/shared/token.model.ts b/client/app/users/shared/token.model.ts new file mode 100644 index 000000000..b7872e74a --- /dev/null +++ b/client/app/users/shared/token.model.ts @@ -0,0 +1,31 @@ +export class Token { + access_token: string; + refresh_token: string; + token_type: string; + + static load(): Token { + return new Token({ + access_token: localStorage.getItem('access_token'), + refresh_token: localStorage.getItem('refresh_token'), + token_type: localStorage.getItem('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():void { + 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/app/users/shared/user.model.ts b/client/app/users/shared/user.model.ts new file mode 100644 index 000000000..73fd4ddc0 --- /dev/null +++ b/client/app/users/shared/user.model.ts @@ -0,0 +1,20 @@ +import { Token } from './token.model'; + +export class User { + username: string; + token: Token; + + static load(): User { + return new User(localStorage.getItem('username'), Token.load()); + } + + constructor (username: string, hash_token: any) { + this.username = username; + this.token = new Token(hash_token); + } + + save(): void { + localStorage.setItem('username', this.username); + this.token.save(); + } +} diff --git a/client/app/videos/index.ts b/client/app/videos/index.ts new file mode 100644 index 000000000..1c80ac5e5 --- /dev/null +++ b/client/app/videos/index.ts @@ -0,0 +1,4 @@ +export * from './shared/index'; +export * from './video-add/index'; +export * from './video-list/index'; +export * from './video-watch/index'; diff --git a/client/app/videos/shared/index.ts b/client/app/videos/shared/index.ts new file mode 100644 index 000000000..c535c46fc --- /dev/null +++ b/client/app/videos/shared/index.ts @@ -0,0 +1,5 @@ +export * from './loader/index'; +export * from './pagination.model'; +export * from './sort-field.type'; +export * from './video.model'; +export * from './video.service'; diff --git a/client/app/videos/shared/loader/index.ts b/client/app/videos/shared/loader/index.ts new file mode 100644 index 000000000..ab22584e4 --- /dev/null +++ b/client/app/videos/shared/loader/index.ts @@ -0,0 +1 @@ +export * from './loader.component'; diff --git a/client/app/videos/shared/loader/loader.component.html b/client/app/videos/shared/loader/loader.component.html new file mode 100644 index 000000000..d02296a2d --- /dev/null +++ b/client/app/videos/shared/loader/loader.component.html @@ -0,0 +1,3 @@ +
+
+
diff --git a/client/app/videos/shared/loader/loader.component.scss b/client/app/videos/shared/loader/loader.component.scss new file mode 100644 index 000000000..454195811 --- /dev/null +++ b/client/app/videos/shared/loader/loader.component.scss @@ -0,0 +1,26 @@ +div { + margin-top: 150px; +} + +// Thanks https://gist.github.com/alexandrevicenzi/680147013e902a4eaa5d +.glyphicon-refresh-animate { + -animation: spin .7s infinite linear; + -ms-animation: spin .7s infinite linear; + -webkit-animation: spinw .7s infinite linear; + -moz-animation: spinm .7s infinite linear; +} + +@keyframes spin { + from { transform: scale(1) rotate(0deg);} + to { transform: scale(1) rotate(360deg);} +} + +@-webkit-keyframes spinw { + from { -webkit-transform: rotate(0deg);} + to { -webkit-transform: rotate(360deg);} +} + +@-moz-keyframes spinm { + from { -moz-transform: rotate(0deg);} + to { -moz-transform: rotate(360deg);} +} diff --git a/client/app/videos/shared/loader/loader.component.ts b/client/app/videos/shared/loader/loader.component.ts new file mode 100644 index 000000000..666d43bc3 --- /dev/null +++ b/client/app/videos/shared/loader/loader.component.ts @@ -0,0 +1,11 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'my-loader', + styleUrls: [ 'client/app/videos/shared/loader/loader.component.css' ], + templateUrl: 'client/app/videos/shared/loader/loader.component.html' +}) + +export class LoaderComponent { + @Input() loading: boolean; +} diff --git a/client/app/videos/shared/pagination.model.ts b/client/app/videos/shared/pagination.model.ts new file mode 100644 index 000000000..06f7a7875 --- /dev/null +++ b/client/app/videos/shared/pagination.model.ts @@ -0,0 +1,5 @@ +export interface Pagination { + currentPage: number; + itemsPerPage: number; + total: number; +} diff --git a/client/app/videos/shared/sort-field.type.ts b/client/app/videos/shared/sort-field.type.ts new file mode 100644 index 000000000..6e8cc7936 --- /dev/null +++ b/client/app/videos/shared/sort-field.type.ts @@ -0,0 +1,3 @@ +export type SortField = "name" | "-name" + | "duration" | "-duration" + | "createdDate" | "-createdDate"; diff --git a/client/app/videos/shared/video.model.ts b/client/app/videos/shared/video.model.ts new file mode 100644 index 000000000..eec537c9e --- /dev/null +++ b/client/app/videos/shared/video.model.ts @@ -0,0 +1,63 @@ +export class Video { + id: string; + name: string; + description: string; + magnetUri: string; + podUrl: string; + isLocal: boolean; + thumbnailPath: string; + author: string; + createdDate: Date; + by: string; + duration: string; + + private static createDurationString(duration: number): string { + const minutes = Math.floor(duration / 60); + const seconds = duration % 60; + const minutes_padding = minutes >= 10 ? '' : '0'; + const seconds_padding = seconds >= 10 ? '' : '0'; + + return minutes_padding + minutes.toString() + ':' + seconds_padding + seconds.toString(); + } + + private static createByString(author: string, podUrl: string): string { + let [ host, port ] = podUrl.replace(/^https?:\/\//, '').split(':'); + + if (port === '80' || port === '443') { + port = ''; + } else { + port = ':' + port; + } + + return author + '@' + host + port; + } + + constructor(hash: { + id: string, + name: string, + description: string, + magnetUri: string, + podUrl: string, + isLocal: boolean, + thumbnailPath: string, + author: string, + createdDate: string, + duration: number; + }) { + this.id = hash.id; + this.name = hash.name; + this.description = hash.description; + this.magnetUri = hash.magnetUri; + this.podUrl = hash.podUrl; + this.isLocal = hash.isLocal; + this.thumbnailPath = hash.thumbnailPath; + this.author = hash.author; + this.createdDate = new Date(hash.createdDate); + this.duration = Video.createDurationString(hash.duration); + this.by = Video.createByString(hash.author, hash.podUrl); + } + + isRemovableBy(user): boolean { + return this.isLocal === true && user && this.author === user.username; + } +} diff --git a/client/app/videos/shared/video.service.ts b/client/app/videos/shared/video.service.ts new file mode 100644 index 000000000..78789c3cc --- /dev/null +++ b/client/app/videos/shared/video.service.ts @@ -0,0 +1,79 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { Pagination } from './pagination.model'; +import { Search } from '../../shared/index'; +import { SortField } from './sort-field.type'; +import { AuthService } from '../../users/index'; +import { Video } from './video.model'; + +@Injectable() +export class VideoService { + private _baseVideoUrl = '/api/v1/videos/'; + + constructor (private http: Http, private _authService: AuthService) {} + + getVideos(pagination: Pagination, sort: SortField) { + const params = this.createPaginationParams(pagination); + + if (sort) params.set('sort', sort); + + return this.http.get(this._baseVideoUrl, { search: params }) + .map(res => res.json()) + .map(this.extractVideos) + .catch(this.handleError); + } + + getVideo(id: string) { + return this.http.get(this._baseVideoUrl + id) + .map(res =>