From b33f657c304b77938c1f68164d8e754787f5aae5 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 1 Dec 2017 09:20:19 +0100 Subject: Begin new menu design --- client/config/webpack.common.js | 2 +- client/package.json | 3 +- client/src/app/app.component.html | 18 +-- client/src/app/app.component.scss | 63 +++++----- client/src/app/app.component.ts | 17 +-- client/src/app/app.module.ts | 6 +- client/src/app/core/core.module.ts | 8 +- client/src/app/core/index.ts | 1 - client/src/app/core/menu/index.ts | 2 - client/src/app/core/menu/menu-admin.component.html | 35 ------ client/src/app/core/menu/menu-admin.component.ts | 33 ------ client/src/app/core/menu/menu.component.html | 55 --------- client/src/app/core/menu/menu.component.scss | 51 -------- client/src/app/core/menu/menu.component.ts | 92 --------------- client/src/app/menu/index.ts | 2 + client/src/app/menu/menu-admin.component.html | 35 ++++++ client/src/app/menu/menu-admin.component.ts | 33 ++++++ client/src/app/menu/menu.component.html | 48 ++++++++ client/src/app/menu/menu.component.scss | 131 +++++++++++++++++++++ client/src/app/menu/menu.component.ts | 97 +++++++++++++++ client/src/app/shared/search/search.component.html | 26 +--- client/src/app/shared/search/search.component.scss | 51 -------- client/src/assets/logo.png | Bin 838 -> 0 bytes client/src/assets/logo.svg | 118 +++++++++++++++++++ client/src/assets/menu/administration.svg | 14 +++ client/src/assets/menu/recently-added.svg | 13 ++ client/src/assets/menu/trending.svg | 16 +++ client/src/sass/_variables.scss | 15 ++- client/src/sass/application.scss | 8 ++ client/yarn.lock | 10 +- 30 files changed, 586 insertions(+), 417 deletions(-) delete mode 100644 client/src/app/core/menu/index.ts delete mode 100644 client/src/app/core/menu/menu-admin.component.html delete mode 100644 client/src/app/core/menu/menu-admin.component.ts delete mode 100644 client/src/app/core/menu/menu.component.html delete mode 100644 client/src/app/core/menu/menu.component.scss delete mode 100644 client/src/app/core/menu/menu.component.ts create mode 100644 client/src/app/menu/index.ts create mode 100644 client/src/app/menu/menu-admin.component.html create mode 100644 client/src/app/menu/menu-admin.component.ts create mode 100644 client/src/app/menu/menu.component.html create mode 100644 client/src/app/menu/menu.component.scss create mode 100644 client/src/app/menu/menu.component.ts delete mode 100644 client/src/assets/logo.png create mode 100644 client/src/assets/logo.svg create mode 100644 client/src/assets/menu/administration.svg create mode 100644 client/src/assets/menu/recently-added.svg create mode 100644 client/src/assets/menu/trending.svg (limited to 'client') diff --git a/client/config/webpack.common.js b/client/config/webpack.common.js index 9cd33d2ed..583f4ba07 100644 --- a/client/config/webpack.common.js +++ b/client/config/webpack.common.js @@ -153,7 +153,7 @@ module.exports = function (options) { ] }, { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, use: 'url-loader?limit=10000&minetype=application/font-woff' }, - { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, use: 'file-loader' }, + { test: /\.(otf|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, use: 'url-loader?limit=10000' }, /* Raw loader support for *.html * Returns file content as string diff --git a/client/package.json b/client/package.json index 39b3185cc..c551c995a 100644 --- a/client/package.json +++ b/client/package.json @@ -70,7 +70,7 @@ "markdown-it": "^8.4.0", "ng-router-loader": "^2.0.0", "ngc-webpack": "3.2.2", - "ngx-bootstrap": "1.9.3", + "ngx-bootstrap": "2.0.0-beta.9", "ngx-chips": "1.5.3", "node-sass": "^4.1.1", "normalize.css": "^7.0.0", @@ -86,6 +86,7 @@ "sass-resources-loader": "^1.2.1", "script-ext-html-webpack-plugin": "^1.3.2", "source-map-loader": "^0.2.1", + "source-sans-pro": "^2.0.10", "standard": "^10.0.0", "string-replace-loader": "^1.0.3", "style-loader": "^0.19.0", diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index 8a826e783..f4672c7ec 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -1,7 +1,7 @@
-
+
@@ -11,15 +11,13 @@
- -
- - - +
+ +
-
-
+
+
- -
-
-
diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss index a656d5c29..28e86097c 100644 --- a/client/src/app/app.component.scss +++ b/client/src/app/app.component.scss @@ -2,10 +2,23 @@ min-height: calc(100vh - #{$header-height} - #{$footer-height} - #{$footer-margin}); } +.main-col { + margin-left: $menu-width; + + &.expanded { + margin-left: 0; + } +} + +.sub-header-container { + margin-top: $header-height; +} + .title-menu-left { position: fixed; height: calc(100vh - #{$header-height}); padding: 0; + width: $menu-width; .title-menu-left-block.menu { height: 100%; @@ -14,35 +27,28 @@ .header { height: $header-height; - - .fake-title-block { - display: inline-block; - } + position: fixed; + width: 100%; + background-color: #fff; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.16); + display: flex; .top-left-block { - z-index: 100; - background-color: #fff; - border-right: 1px solid $header-border-color; + width: $menu-width; + z-index: 1001; height: $header-height; line-height: $header-height; margin-top: 0; margin-bottom: 0; display: flex; - position: fixed; padding: 0; - &.border-bottom { - border-bottom: 1px solid $header-border-color; - } - .hamburger-block { - margin-right: 15px; - margin-left: 15px; + margin-right: 10px; + margin-left: 25px; .glyphicon { cursor: pointer; - position: relative; - top: 4px; } } @@ -50,12 +56,9 @@ a { color: inherit !important; display: block; - background: url('../assets/logo.png') no-repeat; - background-size: contain; - background-position: center; - height: 100%; - margin: auto; - width: 135px; + background: url('../assets/logo.svg') no-repeat; + width: 24px; + height: 24px; &:hover { color: inherit !important; @@ -122,17 +125,11 @@ } } - my-search { - position: fixed; - z-index: 1000; - // Fix col-md-* padding - padding: 0; - } - - .search-col { - height: 100%; - margin-left: -15px; - padding: 0; + .header-right { + text-align: right; + height: $header-height; + margin-left: $menu-width; + flex-grow: 1; } } diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 9b699fafd..b1818c298 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -1,8 +1,6 @@ import { Component, OnInit } from '@angular/core' import { Router } from '@angular/router' - import { AuthService, ServerService } from './core' -import { UserService } from './shared' @Component({ selector: 'my-app', @@ -62,20 +60,9 @@ export class AppComponent implements OnInit { } getMainColClasses () { - const colSizes = { - md: 10, - sm: 9, - xs: 9 - } - // Take all width is the menu is not displayed - if (this.isMenuDisplayed === false) { - Object.keys(colSizes).forEach(col => colSizes[col] = 12) - } - - const classes = [] - Object.keys(colSizes).forEach(col => classes.push(`col-${col}-${colSizes[col]}`)) + if (this.isMenuDisplayed === false) return [ 'expanded' ] - return classes + return [] } } diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index e71641e0d..342589003 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -20,6 +20,7 @@ import { LoginModule } from './login' import { SignupModule } from './signup' import { SharedModule } from './shared' import { VideosModule } from './videos' +import { MenuComponent, MenuAdminComponent } from './menu' export function metaFactory (): MetaLoader { return new MetaStaticLoader({ @@ -47,7 +48,10 @@ const APP_PROVIDERS = [ @NgModule({ bootstrap: [ AppComponent ], declarations: [ - AppComponent + AppComponent, + + MenuComponent, + MenuAdminComponent ], imports: [ BrowserModule, diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index c4ce2b637..75262e6cf 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts @@ -26,17 +26,13 @@ import { throwIfAlreadyLoaded } from './module-import-guard' ], declarations: [ - ConfirmComponent, - MenuComponent, - MenuAdminComponent + ConfirmComponent ], exports: [ SimpleNotificationsModule, - ConfirmComponent, - MenuComponent, - MenuAdminComponent + ConfirmComponent ], providers: [ diff --git a/client/src/app/core/index.ts b/client/src/app/core/index.ts index 8358261ae..3c01e05aa 100644 --- a/client/src/app/core/index.ts +++ b/client/src/app/core/index.ts @@ -1,6 +1,5 @@ export * from './auth' export * from './server' export * from './confirm' -export * from './menu' export * from './routing' export * from './core.module' diff --git a/client/src/app/core/menu/index.ts b/client/src/app/core/menu/index.ts deleted file mode 100644 index c905ed20a..000000000 --- a/client/src/app/core/menu/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './menu.component' -export * from './menu-admin.component' diff --git a/client/src/app/core/menu/menu-admin.component.html b/client/src/app/core/menu/menu-admin.component.html deleted file mode 100644 index 9857b2e3e..000000000 --- a/client/src/app/core/menu/menu-admin.component.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - diff --git a/client/src/app/core/menu/menu-admin.component.ts b/client/src/app/core/menu/menu-admin.component.ts deleted file mode 100644 index ea8d5f57c..000000000 --- a/client/src/app/core/menu/menu-admin.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Component } from '@angular/core' - -import { AuthService } from '../auth/auth.service' -import { UserRight } from '../../../../../shared' - -@Component({ - selector: 'my-menu-admin', - templateUrl: './menu-admin.component.html', - styleUrls: [ './menu.component.scss' ] -}) -export class MenuAdminComponent { - constructor (private auth: AuthService) {} - - hasUsersRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_USERS) - } - - hasServerFollowRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_SERVER_FOLLOW) - } - - hasVideoAbusesRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) - } - - hasVideoBlacklistRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) - } - - hasJobsRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS) - } -} diff --git a/client/src/app/core/menu/menu.component.html b/client/src/app/core/menu/menu.component.html deleted file mode 100644 index fcde23fdd..000000000 --- a/client/src/app/core/menu/menu.component.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - diff --git a/client/src/app/core/menu/menu.component.scss b/client/src/app/core/menu/menu.component.scss deleted file mode 100644 index 45679c310..000000000 --- a/client/src/app/core/menu/menu.component.scss +++ /dev/null @@ -1,51 +0,0 @@ -menu { - background-color: $black-background; - padding: 15px; - margin: 0; - height: 100%; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - z-index: 1000; - - @media screen and (max-width: 550px) { - font-size: 90%; - } - - @media screen and (min-width: 1200px) { - padding: 25px; - } - - .panel-block { - margin-bottom: 15px; - } - - .block-title { - text-transform: uppercase; - font-weight: bold; - color: $menu-color-block; - margin-bottom: 10px; - } - - a { - display: block; - margin-left: 5px; - height: 30px; - color: $menu-color-link; - cursor: pointer; - transition: color 0.3s; - - &:hover, &:focus { - text-decoration: none !important; - outline: none !important; - } - - .glyphicon { - margin-right: 15px; - } - - &:hover, &.active { - color: #fff; - } - } -} diff --git a/client/src/app/core/menu/menu.component.ts b/client/src/app/core/menu/menu.component.ts deleted file mode 100644 index d2bd71534..000000000 --- a/client/src/app/core/menu/menu.component.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Component, OnInit } from '@angular/core' -import { Router } from '@angular/router' - -import { AuthService, AuthStatus } from '../auth' -import { ServerService } from '../server' -import { UserRight } from '../../../../../shared/models/users/user-right.enum' - -@Component({ - selector: 'my-menu', - templateUrl: './menu.component.html', - styleUrls: [ './menu.component.scss' ] -}) -export class MenuComponent implements OnInit { - isLoggedIn: boolean - userHasAdminAccess = false - - private routesPerRight = { - [UserRight.MANAGE_USERS]: '/admin/users', - [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends', - [UserRight.MANAGE_VIDEO_ABUSES]: '/admin/video-abuses', - [UserRight.MANAGE_VIDEO_BLACKLIST]: '/admin/video-blacklist' - } - - constructor ( - private authService: AuthService, - private serverService: ServerService, - private router: Router - ) {} - - ngOnInit () { - this.isLoggedIn = this.authService.isLoggedIn() - this.computeIsUserHasAdminAccess() - - this.authService.loginChangedSource.subscribe( - status => { - if (status === AuthStatus.LoggedIn) { - this.isLoggedIn = true - this.computeIsUserHasAdminAccess() - console.log('Logged in.') - } else if (status === AuthStatus.LoggedOut) { - this.isLoggedIn = false - this.computeIsUserHasAdminAccess() - console.log('Logged out.') - } else { - console.error('Unknown auth status: ' + status) - } - } - ) - } - - isRegistrationAllowed () { - return this.serverService.getConfig().signup.allowed - } - - getFirstAdminRightAvailable () { - const user = this.authService.getUser() - if (!user) return undefined - - const adminRights = [ - UserRight.MANAGE_USERS, - UserRight.MANAGE_SERVER_FOLLOW, - UserRight.MANAGE_VIDEO_ABUSES, - UserRight.MANAGE_VIDEO_BLACKLIST - ] - - for (const adminRight of adminRights) { - if (user.hasRight(adminRight)) { - return adminRight - } - } - - return undefined - } - - getFirstAdminRouteAvailable () { - const right = this.getFirstAdminRightAvailable() - - return this.routesPerRight[right] - } - - logout () { - this.authService.logout() - // Redirect to home page - this.router.navigate(['/videos/list']) - } - - private computeIsUserHasAdminAccess () { - const right = this.getFirstAdminRightAvailable() - - this.userHasAdminAccess = right !== undefined - } -} diff --git a/client/src/app/menu/index.ts b/client/src/app/menu/index.ts new file mode 100644 index 000000000..c905ed20a --- /dev/null +++ b/client/src/app/menu/index.ts @@ -0,0 +1,2 @@ +export * from './menu.component' +export * from './menu-admin.component' diff --git a/client/src/app/menu/menu-admin.component.html b/client/src/app/menu/menu-admin.component.html new file mode 100644 index 000000000..9857b2e3e --- /dev/null +++ b/client/src/app/menu/menu-admin.component.html @@ -0,0 +1,35 @@ + + + + + diff --git a/client/src/app/menu/menu-admin.component.ts b/client/src/app/menu/menu-admin.component.ts new file mode 100644 index 000000000..1babf5eb6 --- /dev/null +++ b/client/src/app/menu/menu-admin.component.ts @@ -0,0 +1,33 @@ +import { Component } from '@angular/core' + +import { AuthService } from '../core/auth/auth.service' +import { UserRight } from '../../../../shared' + +@Component({ + selector: 'my-menu-admin', + templateUrl: './menu-admin.component.html', + styleUrls: [ './menu.component.scss' ] +}) +export class MenuAdminComponent { + constructor (private auth: AuthService) {} + + hasUsersRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_USERS) + } + + hasServerFollowRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_SERVER_FOLLOW) + } + + hasVideoAbusesRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) + } + + hasVideoBlacklistRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) + } + + hasJobsRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS) + } +} diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html new file mode 100644 index 000000000..bb0caaef5 --- /dev/null +++ b/client/src/app/menu/menu.component.html @@ -0,0 +1,48 @@ + +
+
+
{{ user.username }}
+
{{ user.email }}
+
+ +
+ + + +
+
+ + + + + + +
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss new file mode 100644 index 000000000..8a4910605 --- /dev/null +++ b/client/src/app/menu/menu.component.scss @@ -0,0 +1,131 @@ +menu { + background-color: $black-background; + margin: 0; + padding: 0; + height: 100%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + z-index: 1000; + color: $menu-color; + + @media screen and (max-width: 550px) { + font-size: 90%; + } + + .logged-in-block { + height: 100px; + background-color: rgba(255, 255, 255, 0.15); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 35px; + + .logged-in-info { + flex-grow: 1; + margin-left: 40px; + + .logged-in-username { + font-size: 16px; + font-weight: $font-semibold; + } + + .logged-in-email { + font-size: 13px; + color: #C6C6C6; + } + } + + .logged-in-more { + margin-right: 20px; + + .glyphicon { + cursor: pointer; + font-size: 18px; + } + } + } + + .button-block { + margin: 30px 25px 35px 25px; + + .login-button, .create-account-button { + font-weight: $font-semibold; + font-size: 15px; + height: $button-height; + line-height: $button-height; + width: 190px; + border-radius: 3px; + text-align: center; + + &.login-button { + background-color: $orange-color; + margin-bottom: 10px; + } + + &.create-account-button { + background-color: rgba(255, 255, 255, 0.25); + } + } + } + + .block-title { + text-transform: uppercase; + font-weight: $font-bold; // Bold + font-size: 13px; + margin-bottom: 25px; + } + + .panel-block { + margin-bottom: 45px; + margin-left: 26px; + + a { + display: flex; + + .icon { + width: 22px; + height: 22px; + display: inline-block; + margin-right: 18px; + background-size: contain; + + &.icon-videos-trending { + position: relative; + top: -2px; + background-image: url('../../assets/menu/trending.svg'); + } + + &.icon-videos-recently-added { + width: 23px; + height: 23px; + position: relative; + top: -1px; + background-image: url('../../assets/menu/recently-added.svg'); + } + + &.icon-administration { + width: 23px; + height: 23px; + + background-image: url('../../assets/menu/administration.svg'); + } + } + } + } + + a { + color: $menu-color; + height: 22px; + line-height: 22px; + display: block; + font-size: 16px; + cursor: pointer; + margin-bottom: 15px; + + &:hover, &:focus { + text-decoration: none !important; + outline: none !important; + } + } +} diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts new file mode 100644 index 000000000..4c35bb3a5 --- /dev/null +++ b/client/src/app/menu/menu.component.ts @@ -0,0 +1,97 @@ +import { Component, OnInit } from '@angular/core' +import { Router } from '@angular/router' +import { UserRight } from '../../../../shared/models/users/user-right.enum' +import { AuthService, AuthStatus, ServerService } from '../core' +import { User } from '../shared/users/user.model' + +@Component({ + selector: 'my-menu', + templateUrl: './menu.component.html', + styleUrls: [ './menu.component.scss' ] +}) +export class MenuComponent implements OnInit { + user: User + isLoggedIn: boolean + userHasAdminAccess = false + + private routesPerRight = { + [UserRight.MANAGE_USERS]: '/admin/users', + [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends', + [UserRight.MANAGE_VIDEO_ABUSES]: '/admin/video-abuses', + [UserRight.MANAGE_VIDEO_BLACKLIST]: '/admin/video-blacklist' + } + + constructor ( + private authService: AuthService, + private serverService: ServerService, + private router: Router + ) {} + + ngOnInit () { + this.isLoggedIn = this.authService.isLoggedIn() + if (this.isLoggedIn === true) this.user = this.authService.getUser() + this.computeIsUserHasAdminAccess() + + this.authService.loginChangedSource.subscribe( + status => { + if (status === AuthStatus.LoggedIn) { + this.isLoggedIn = true + this.user = this.authService.getUser() + this.computeIsUserHasAdminAccess() + console.log('Logged in.') + } else if (status === AuthStatus.LoggedOut) { + this.isLoggedIn = false + this.user = undefined + this.computeIsUserHasAdminAccess() + console.log('Logged out.') + } else { + console.error('Unknown auth status: ' + status) + } + } + ) + } + + isRegistrationAllowed () { + return this.serverService.getConfig().signup.allowed + } + + getFirstAdminRightAvailable () { + const user = this.authService.getUser() + if (!user) return undefined + + const adminRights = [ + UserRight.MANAGE_USERS, + UserRight.MANAGE_SERVER_FOLLOW, + UserRight.MANAGE_VIDEO_ABUSES, + UserRight.MANAGE_VIDEO_BLACKLIST + ] + + for (const adminRight of adminRights) { + if (user.hasRight(adminRight)) { + return adminRight + } + } + + return undefined + } + + getFirstAdminRouteAvailable () { + const right = this.getFirstAdminRightAvailable() + + return this.routesPerRight[right] + } + + logout (event: Event) { + event.preventDefault() + + this.authService.logout() + // Redirect to home page + this.router.navigate(['/videos/list']) + } + + private computeIsUserHasAdminAccess () { + const right = this.getFirstAdminRightAvailable() + + this.userHasAdminAccess = right !== undefined + } +} diff --git a/client/src/app/shared/search/search.component.html b/client/src/app/shared/search/search.component.html index 75e9dfa59..0e3de150c 100644 --- a/client/src/app/shared/search/search.component.html +++ b/client/src/app/shared/search/search.component.html @@ -1,22 +1,6 @@ -
+ - - - - -
- - -
-
+Upload diff --git a/client/src/app/shared/search/search.component.scss b/client/src/app/shared/search/search.component.scss index 583f9586f..e69de29bb 100644 --- a/client/src/app/shared/search/search.component.scss +++ b/client/src/app/shared/search/search.component.scss @@ -1,51 +0,0 @@ -.icon-addon { - background-color: #fff; - border-radius: 0; - border-color: $header-border-color; - border-width: 0 0 1px 0; - text-align: right; - - .glyphicon-search { - width: 30px; - font-size: 20px; - } -} - -input, button, .input-group { - height: 100%; -} - -input, .input-group-btn { - border-radius: 0; - border-top: none; - border-left: none; -} - -input { - height: $header-height; - border-right: none; - font-weight: bold; - box-shadow: none; - - &, &:focus { - border-bottom: 1px solid $header-border-color !important; - outline: none !important; - box-shadow: none !important; - } -} - -button { - - &, &:hover, &:focus, &:active, &:visited { - background-color: #fff !important; - border-color: $header-border-color !important; - color: #858585 !important; - outline: none !important; - - height: $header-height; - border-width: 0 0 1px 0; - font-weight: bold; - text-decoration: none; - box-shadow: none; - } -} diff --git a/client/src/assets/logo.png b/client/src/assets/logo.png deleted file mode 100644 index c1d77a24c..000000000 Binary files a/client/src/assets/logo.png and /dev/null differ diff --git a/client/src/assets/logo.svg b/client/src/assets/logo.svg new file mode 100644 index 000000000..8777acd5b --- /dev/null +++ b/client/src/assets/logo.svg @@ -0,0 +1,118 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/client/src/assets/menu/administration.svg b/client/src/assets/menu/administration.svg new file mode 100644 index 000000000..b6da837d2 --- /dev/null +++ b/client/src/assets/menu/administration.svg @@ -0,0 +1,14 @@ + + + + filter + Created with Sketch. + + + + + + + + + diff --git a/client/src/assets/menu/recently-added.svg b/client/src/assets/menu/recently-added.svg new file mode 100644 index 000000000..6473837f8 --- /dev/null +++ b/client/src/assets/menu/recently-added.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/client/src/assets/menu/trending.svg b/client/src/assets/menu/trending.svg new file mode 100644 index 000000000..ffc65cc04 --- /dev/null +++ b/client/src/assets/menu/trending.svg @@ -0,0 +1,16 @@ + + + + graph + Created with Sketch. + + + + + + + + + + + diff --git a/client/src/sass/_variables.scss b/client/src/sass/_variables.scss index f0ffb43ba..640746722 100644 --- a/client/src/sass/_variables.scss +++ b/client/src/sass/_variables.scss @@ -1,12 +1,19 @@ +$font-regular: 400; +$font-semibold: 600; +$font-bold: 700; + $grey-color: #555; +$orange-color: #F1680D; -$black-background: #1d2125; +$black-background: #000; $grey-background: #f6f2f2; -$menu-color-link: #9cabb8; -$menu-color-block: #686f77; +$button-height: 30px; + +$menu-color: #fff; +$menu-width: 240px; -$header-height: 65px; +$header-height: 50px; $header-border-color: #e9eff6; $footer-height: 30px; diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 47e1b6df0..58f07612b 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -1,3 +1,5 @@ +$FontPathSourceSansPro: "../fonts/source-sans-pro"; +@import '~source-sans-pro/source-sans-pro'; @import '~primeng/resources/themes/bootstrap/theme.css'; @import '~primeng/resources/primeng.css'; @import '~video.js/dist/video-js.css'; @@ -7,6 +9,12 @@ display: none !important; } +body { + font-family: 'Source Sans Pro'; + font-weight: $font-regular; + color: #000; +} + input.readonly { /* Force blank on readonly inputs */ background-color: #fff !important; diff --git a/client/yarn.lock b/client/yarn.lock index c5a47bb89..8f148e431 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -4708,9 +4708,9 @@ ngc-webpack@3.2.2: source-map "^0.5.6" ts-node "^3.2.0" -ngx-bootstrap@1.9.3: - version "1.9.3" - resolved "https://registry.yarnpkg.com/ngx-bootstrap/-/ngx-bootstrap-1.9.3.tgz#28e75d14fb1beaee609383d7694de4eb3ba03b26" +ngx-bootstrap@2.0.0-beta.9: + version "2.0.0-beta.9" + resolved "https://registry.yarnpkg.com/ngx-bootstrap/-/ngx-bootstrap-2.0.0-beta.9.tgz#9aa7c88269534e7a5440481f31b137549f749796" ngx-chips@1.5.3: version "1.5.3" @@ -6602,6 +6602,10 @@ source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" +source-sans-pro@^2.0.10: + version "2.0.10" + resolved "https://registry.yarnpkg.com/source-sans-pro/-/source-sans-pro-2.0.10.tgz#c1ca859cf164a088944c5e83745085e87cd533a9" + spdx-correct@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" -- cgit v1.2.3 From 26c6ee80d0fecfce595e8970f15717560b4f4ceb Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 1 Dec 2017 13:08:46 +0100 Subject: Implement header design --- client/config/webpack.common.js | 3 +- client/config/webpack.video-embed.js | 3 +- client/src/app/app.component.html | 19 ++-- client/src/app/app.component.scss | 101 ++++++--------------- client/src/app/menu/menu.component.html | 2 +- client/src/app/menu/menu.component.scss | 5 +- client/src/app/shared/search/search.component.html | 10 +- client/src/app/shared/search/search.component.scss | 55 +++++++++++ client/src/assets/header/menu.svg | 14 +++ client/src/assets/header/search.svg | 12 +++ client/src/assets/header/upload.svg | 16 ++++ client/src/sass/_mixins.scss | 6 ++ client/src/sass/_variables.scss | 8 +- 13 files changed, 158 insertions(+), 96 deletions(-) create mode 100644 client/src/assets/header/menu.svg create mode 100644 client/src/assets/header/search.svg create mode 100644 client/src/assets/header/upload.svg create mode 100644 client/src/sass/_mixins.scss (limited to 'client') diff --git a/client/config/webpack.common.js b/client/config/webpack.common.js index 583f4ba07..acf22dab1 100644 --- a/client/config/webpack.common.js +++ b/client/config/webpack.common.js @@ -146,7 +146,8 @@ module.exports = function (options) { loader: 'sass-resources-loader', options: { resources: [ - helpers.root('src/sass/_variables.scss') + helpers.root('src/sass/_variables.scss'), + helpers.root('src/sass/_mixins.scss') ] } } diff --git a/client/config/webpack.video-embed.js b/client/config/webpack.video-embed.js index fe40194cf..2b70b6681 100644 --- a/client/config/webpack.video-embed.js +++ b/client/config/webpack.video-embed.js @@ -74,7 +74,8 @@ module.exports = function (options) { loader: 'sass-resources-loader', options: { resources: [ - helpers.root('src/sass/_variables.scss') + helpers.root('src/sass/_variables.scss'), + helpers.root('src/sass/_mixins.scss') ] } } diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index f4672c7ec..640524e23 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -1,14 +1,13 @@ -
-
+
+
-
- -
+ -
- -
+ + + PeerTube +
@@ -16,7 +15,7 @@
-
+
-
+
diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss index 28e86097c..f245d0563 100644 --- a/client/src/app/app.component.scss +++ b/client/src/app/app.component.scss @@ -28,8 +28,10 @@ .header { height: $header-height; position: fixed; + top: 0; width: 100%; background-color: #fff; + z-index: 1000; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.16); display: flex; @@ -37,33 +39,36 @@ width: $menu-width; z-index: 1001; height: $header-height; - line-height: $header-height; - margin-top: 0; - margin-bottom: 0; display: flex; - padding: 0; - - .hamburger-block { - margin-right: 10px; - margin-left: 25px; - - .glyphicon { - cursor: pointer; + align-items: center; + + .icon { + cursor: pointer; + width: 22px; + height: 22px; + display: inline-block; + background-size: contain; + + &.icon-menu { + background-image: url('../assets/header/menu.svg'); + margin: 0 18px 0 24px; } } #peertube-title { - a { - color: inherit !important; - display: block; + font-size: 20px; + font-weight: $font-bold; + color: inherit !important; + display: flex; + align-items: center; + + @include disable-default-a-behaviour; + + .icon.icon-logo { + display: inline-block; background: url('../assets/logo.svg') no-repeat; - width: 24px; + width: 20px; height: 24px; - - &:hover { - color: inherit !important; - text-decoration: none !important; - } } } @@ -71,65 +76,15 @@ #peertube-title { display: none; } - - .hamburger-block { - width: 100%; - text-align: center; - } - } - - @media screen and (min-width: 500px) and (max-width: 600px) { - #peertube-title a { - width: 80px; - } - } - - @media screen and (min-width: 600px) and (max-width: 700px) { - #peertube-title a { - width: 100px; - } - } - - @media screen and (min-width: 1000px) { - #peertube-title a { - width: 120px; - } - } - - @media screen and (min-width: 1000px) { - #peertube-title a { - width: 120px; - } - } - - @media screen and (min-width: 1200px) { - padding-left: 15px; - - .hamburger-block { - margin-right: 15px; - } - - #peertube-title a { - width: 135px; - } - } - - @media screen and (min-width: 1600px) { - .hamburger-block { - margin-right: 20px; - } - - #peertube-title a { - width: 180px; - } } } .header-right { - text-align: right; height: $header-height; - margin-left: $menu-width; + display: flex; + align-items: center; flex-grow: 1; + justify-content: flex-end; } } diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html index bb0caaef5..fb31c0734 100644 --- a/client/src/app/menu/menu.component.html +++ b/client/src/app/menu/menu.component.html @@ -19,7 +19,7 @@
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index 8a4910605..2c2106733 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss @@ -123,9 +123,6 @@ menu { cursor: pointer; margin-bottom: 15px; - &:hover, &:focus { - text-decoration: none !important; - outline: none !important; - } + @include disable-default-a-behaviour; } } diff --git a/client/src/app/shared/search/search.component.html b/client/src/app/shared/search/search.component.html index 0e3de150c..9bc9bafe4 100644 --- a/client/src/app/shared/search/search.component.html +++ b/client/src/app/shared/search/search.component.html @@ -1,6 +1,10 @@ + -Upload + + + Upload + diff --git a/client/src/app/shared/search/search.component.scss b/client/src/app/shared/search/search.component.scss index e69de29bb..ffd891904 100644 --- a/client/src/app/shared/search/search.component.scss +++ b/client/src/app/shared/search/search.component.scss @@ -0,0 +1,55 @@ +#search-video { + display: inline-block; + height: $button-height; + width: $search-input-width; + margin-right: 15px; + padding-right: 25px; // For the search icon + background: #fff; + border: 1px solid #C6C6C6; + border-radius: 3px; + padding-left: 15px; + + &::placeholder { + color: #000; + } +} + +.icon.icon-search { + display: inline-block; + background: url('../../../assets/header/search.svg') no-repeat; + background-size: contain; + width: 25px; + height: 21px; + vertical-align: middle; + cursor: pointer; + // yolo + position: absolute; + margin-left: -50px; + margin-top: 5px; +} + +.upload-button { + display: inline-block; + color: #fff; + font-weight: $font-semibold; + font-size: 15px; + height: $button-height; + line-height: $button-height; + border-radius: 3px; + text-align: center; + margin-right: 25px; + background-color: $orange-color; + padding: 0 17px 0 13px; + + @include disable-default-a-behaviour; + + .icon.icon-upload { + display: inline-block; + background: url('../../../assets/header/upload.svg') no-repeat; + background-size: contain; + width: 22px; + height: 24px; + vertical-align: middle; + margin-right: 6px; + } +} diff --git a/client/src/assets/header/menu.svg b/client/src/assets/header/menu.svg new file mode 100644 index 000000000..7101bf73b --- /dev/null +++ b/client/src/assets/header/menu.svg @@ -0,0 +1,14 @@ + + + + menu + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/client/src/assets/header/search.svg b/client/src/assets/header/search.svg new file mode 100644 index 000000000..489b59e9b --- /dev/null +++ b/client/src/assets/header/search.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/client/src/assets/header/upload.svg b/client/src/assets/header/upload.svg new file mode 100644 index 000000000..2b07caf76 --- /dev/null +++ b/client/src/assets/header/upload.svg @@ -0,0 +1,16 @@ + + + + cloud-upload + Created with Sketch. + + + + + + + + + + + diff --git a/client/src/sass/_mixins.scss b/client/src/sass/_mixins.scss new file mode 100644 index 000000000..681657d90 --- /dev/null +++ b/client/src/sass/_mixins.scss @@ -0,0 +1,6 @@ +@mixin disable-default-a-behaviour { + &:hover, &:focus { + text-decoration: none !important; + outline: none !important; + } +} diff --git a/client/src/sass/_variables.scss b/client/src/sass/_variables.scss index 640746722..e32b37462 100644 --- a/client/src/sass/_variables.scss +++ b/client/src/sass/_variables.scss @@ -10,12 +10,14 @@ $grey-background: #f6f2f2; $button-height: 30px; -$menu-color: #fff; -$menu-width: 240px; - $header-height: 50px; $header-border-color: #e9eff6; +$search-input-width: 375px; + +$menu-color: #fff; +$menu-width: 240px; + $footer-height: 30px; $footer-margin: 30px; -- cgit v1.2.3 From 9bf9d2a5c223bf006496ae7adf0c0bd7a7975108 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 1 Dec 2017 14:46:22 +0100 Subject: Begin videos list new design --- client/package.json | 2 +- client/src/app/menu/menu.component.html | 4 +- client/src/app/shared/misc/from-now.pipe.ts | 37 +++++++++ .../src/app/shared/misc/number-formatter.pipe.ts | 19 +++++ client/src/app/shared/shared.module.ts | 26 +++--- .../app/videos/+video-edit/video-add.component.ts | 2 +- client/src/app/videos/video-list/index.ts | 3 +- .../app/videos/video-list/my-videos.component.ts | 2 +- .../video-list/shared/abstract-video-list.html | 18 +---- .../video-list/shared/abstract-video-list.scss | 14 ---- .../video-list/shared/abstract-video-list.ts | 28 ++----- client/src/app/videos/video-list/shared/index.ts | 1 - .../shared/video-miniature.component.html | 13 +-- .../shared/video-miniature.component.scss | 66 +++++---------- .../video-list/shared/video-sort.component.html | 5 -- .../video-list/shared/video-sort.component.ts | 39 --------- .../app/videos/video-list/video-list.component.ts | 94 ---------------------- .../video-list/video-recently-added.component.ts | 33 ++++++++ .../videos/video-list/video-trending.component.ts | 33 ++++++++ client/src/app/videos/videos-routing.module.ts | 26 ++++-- client/src/app/videos/videos.module.ts | 10 ++- client/src/sass/application.scss | 26 ++---- client/yarn.lock | 8 +- 23 files changed, 213 insertions(+), 296 deletions(-) create mode 100644 client/src/app/shared/misc/from-now.pipe.ts create mode 100644 client/src/app/shared/misc/number-formatter.pipe.ts delete mode 100644 client/src/app/videos/video-list/shared/video-sort.component.html delete mode 100644 client/src/app/videos/video-list/shared/video-sort.component.ts delete mode 100644 client/src/app/videos/video-list/video-list.component.ts create mode 100644 client/src/app/videos/video-list/video-recently-added.component.ts create mode 100644 client/src/app/videos/video-list/video-trending.component.ts (limited to 'client') diff --git a/client/package.json b/client/package.json index c551c995a..310860fec 100644 --- a/client/package.json +++ b/client/package.json @@ -43,7 +43,6 @@ "@types/webpack": "^3.0.0", "@types/webtorrent": "^0.98.4", "add-asset-html-webpack-plugin": "^2.0.1", - "angular-pipes": "^6.0.0", "angular2-notifications": "^0.7.7", "angular2-template-loader": "^0.6.0", "assets-webpack-plugin": "^3.4.0", @@ -72,6 +71,7 @@ "ngc-webpack": "3.2.2", "ngx-bootstrap": "2.0.0-beta.9", "ngx-chips": "1.5.3", + "ngx-pipes": "^2.0.5", "node-sass": "^4.1.1", "normalize.css": "^7.0.0", "optimize-js-plugin": "0.0.4", diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html index fb31c0734..21f8d8ba4 100644 --- a/client/src/app/menu/menu.component.html +++ b/client/src/app/menu/menu.component.html @@ -26,12 +26,12 @@
Videos
- + Trending - + Recently added diff --git a/client/src/app/shared/misc/from-now.pipe.ts b/client/src/app/shared/misc/from-now.pipe.ts new file mode 100644 index 000000000..25e5d6a85 --- /dev/null +++ b/client/src/app/shared/misc/from-now.pipe.ts @@ -0,0 +1,37 @@ +import { Pipe, PipeTransform } from '@angular/core' + +// Thanks: https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts + +@Pipe({name: 'fromNow'}) +export class FromNowPipe implements PipeTransform { + + transform (value: number) { + const seconds = Math.floor((Date.now() - value) / 1000) + + let interval = Math.floor(seconds / 31536000) + if (interval > 1) { + return interval + ' years ago' + } + + interval = Math.floor(seconds / 2592000) + if (interval > 1) return interval + ' months ago' + if (interval === 1) return interval + ' month ago' + + interval = Math.floor(seconds / 604800) + if (interval > 1) return interval + ' weeks ago' + if (interval === 1) return interval + ' week ago' + + interval = Math.floor(seconds / 86400) + if (interval > 1) return interval + ' days ago' + if (interval === 1) return interval + ' day ago' + + interval = Math.floor(seconds / 3600) + if (interval > 1) return interval + ' hours ago' + if (interval === 1) return interval + ' hour ago' + + interval = Math.floor(seconds / 60) + if (interval >= 1) return interval + ' min ago' + + return Math.floor(seconds) + ' sec ago' + } +} diff --git a/client/src/app/shared/misc/number-formatter.pipe.ts b/client/src/app/shared/misc/number-formatter.pipe.ts new file mode 100644 index 000000000..2491fb1d6 --- /dev/null +++ b/client/src/app/shared/misc/number-formatter.pipe.ts @@ -0,0 +1,19 @@ +import { Pipe, PipeTransform } from '@angular/core' + +// Thanks: https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts + +@Pipe({name: 'numberFormatter'}) +export class NumberFormatterPipe implements PipeTransform { + private dictionary: Array<{max: number, type: string}> = [ + { max: 1000, type: '' }, + { max: 1000000, type: 'K' }, + { max: 1000000000, type: 'M' } + ] + + transform (value: number) { + const format = this.dictionary.find(d => value < d.max) || this.dictionary[this.dictionary.length - 1] + const calc = Math.floor(value / (format.max / 1000)) + + return `${calc}${format.type}` + } +} diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 456ce851e..c7ea6e603 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -1,25 +1,26 @@ -import { NgModule } from '@angular/core' -import { HttpClientModule } from '@angular/common/http' import { CommonModule } from '@angular/common' +import { HttpClientModule } from '@angular/common/http' +import { NgModule } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { RouterModule } from '@angular/router' -import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe' -import { KeysPipe } from 'angular-pipes/src/object/keys.pipe' import { BsDropdownModule } from 'ngx-bootstrap/dropdown' -import { ProgressbarModule } from 'ngx-bootstrap/progressbar' -import { PaginationModule } from 'ngx-bootstrap/pagination' import { ModalModule } from 'ngx-bootstrap/modal' -import { DataTableModule } from 'primeng/components/datatable/datatable' +import { PaginationModule } from 'ngx-bootstrap/pagination' +import { ProgressbarModule } from 'ngx-bootstrap/progressbar' +import { BytesPipe, KeysPipe } from 'ngx-pipes' import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' +import { DataTableModule } from 'primeng/components/datatable/datatable' import { AUTH_INTERCEPTOR_PROVIDER } from './auth' +import { LoaderComponent } from './misc/loader.component' import { RestExtractor, RestService } from './rest' import { SearchComponent, SearchService } from './search' import { UserService } from './users' import { VideoAbuseService } from './video-abuse' import { VideoBlacklistService } from './video-blacklist' -import { LoaderComponent } from './misc/loader.component' +import { NumberFormatterPipe } from './misc/number-formatter.pipe' +import { FromNowPipe } from './misc/from-now.pipe' @NgModule({ imports: [ @@ -42,7 +43,9 @@ import { LoaderComponent } from './misc/loader.component' BytesPipe, KeysPipe, SearchComponent, - LoaderComponent + LoaderComponent, + NumberFormatterPipe, + FromNowPipe ], exports: [ @@ -62,7 +65,10 @@ import { LoaderComponent } from './misc/loader.component' KeysPipe, SearchComponent, - LoaderComponent + LoaderComponent, + + NumberFormatterPipe, + FromNowPipe ], providers: [ diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts index 1704cf486..76bfbb515 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts @@ -184,7 +184,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { this.notificationsService.success('Success', 'Video uploaded.') // Display all the videos once it's finished - this.router.navigate([ '/videos/list' ]) + this.router.navigate([ '/videos/trending' ]) } }, diff --git a/client/src/app/videos/video-list/index.ts b/client/src/app/videos/video-list/index.ts index ed2bb1657..a5a60364a 100644 --- a/client/src/app/videos/video-list/index.ts +++ b/client/src/app/videos/video-list/index.ts @@ -1,3 +1,4 @@ export * from './my-videos.component' -export * from './video-list.component' +export * from './video-recently-added.component' +export * from './video-trending.component' export * from './shared' diff --git a/client/src/app/videos/video-list/my-videos.component.ts b/client/src/app/videos/video-list/my-videos.component.ts index 648741a40..146db8262 100644 --- a/client/src/app/videos/video-list/my-videos.component.ts +++ b/client/src/app/videos/video-list/my-videos.component.ts @@ -27,7 +27,7 @@ export class MyVideosComponent extends AbstractVideoList implements OnInit, OnDe } ngOnDestroy () { - this.subActivatedRoute.unsubscribe() + super.ngOnDestroy() } getVideosObservable () { diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.html b/client/src/app/videos/video-list/shared/abstract-video-list.html index 680fba3f5..ab5530e68 100644 --- a/client/src/app/videos/video-list/shared/abstract-video-list.html +++ b/client/src/app/videos/video-list/shared/abstract-video-list.html @@ -1,20 +1,8 @@ -
-
-
-
- {{ pagination.totalItems }} videos - - -
- - -
-
+
+ {{ titlePage }}
-
-
There is no video.
- +
= new BehaviorSubject(false) pagination: VideoPagination = { currentPage: 1, itemsPerPage: 25, totalItems: null } - sort: SortField + sort: SortField = '-createdAt' videos: Video[] = [] protected notificationsService: NotificationsService @@ -28,6 +22,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { protected subActivatedRoute: Subscription + abstract titlePage: string abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}> ngOnInit () { @@ -44,7 +39,6 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { } getVideos () { - this.loading.next(true) this.videos = [] const observable = this.getVideosObservable() @@ -53,17 +47,11 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { ({ videos, totalVideos }) => { this.videos = videos this.pagination.totalItems = totalVideos - - this.loading.next(false) }, error => this.notificationsService.error('Error', error.text) ) } - isThereNoVideo () { - return !this.loading.getValue() && this.videos.length === 0 - } - onPageChanged (event: { page: number }) { // Be sure the current page is set this.pagination.currentPage = event.page @@ -71,12 +59,6 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { this.navigateToNewParams() } - onSort (sort: SortField) { - this.sort = sort - - this.navigateToNewParams() - } - protected buildRouteParams () { // There is always a sort and a current page const params = { diff --git a/client/src/app/videos/video-list/shared/index.ts b/client/src/app/videos/video-list/shared/index.ts index d8f73bcda..170ca4832 100644 --- a/client/src/app/videos/video-list/shared/index.ts +++ b/client/src/app/videos/video-list/shared/index.ts @@ -1,3 +1,2 @@ export * from './abstract-video-list' export * from './video-miniature.component' -export * from './video-sort.component' diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.html b/client/src/app/videos/video-list/shared/video-miniature.component.html index 6bbd29666..aea85b6c6 100644 --- a/client/src/app/videos/video-list/shared/video-miniature.component.html +++ b/client/src/app/videos/video-list/shared/video-miniature.component.html @@ -6,8 +6,7 @@ video thumbnail
- {{ video.views }} views - {{ video.durationLabel }} + {{ video.durationLabel }}
@@ -21,13 +20,7 @@ -
- - {{ tag }} - -
- - - {{ video.createdAt | date:'short' }} + {{ video.createdAt | fromNow }} - {{ video.views | numberFormatter }} views +
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.scss b/client/src/app/videos/video-list/shared/video-miniature.component.scss index 507ace098..ed15864d9 100644 --- a/client/src/app/videos/video-list/shared/video-miniature.component.scss +++ b/client/src/app/videos/video-list/shared/video-miniature.component.scss @@ -1,14 +1,14 @@ .video-miniature { - margin: 15px 10px; display: inline-block; - position: relative; - height: 190px; + padding-right: 15px; + margin-bottom: 30px; + height: 175px; vertical-align: top; .video-miniature-thumbnail { display: inline-block; position: relative; - border-radius: 3px; + border-radius: 4px; overflow: hidden; &:hover { @@ -22,38 +22,33 @@ .video-miniature-thumbnail-overlay { position: absolute; - right: 0px; - bottom: 0px; + right: 5px; + bottom: 5px; display: inline-block; background-color: rgba(0, 0, 0, 0.7); color: #fff; - padding: 3px 5px; - font-size: 11px; - font-weight: bold; - width: 100%; - - .video-miniature-thumbnail-overlay-views { - - } - - .video-miniature-thumbnail-overlay-duration { - float: right; - } + font-size: 12px; + font-weight: $font-bold; + border-radius: 3px; + padding: 0 5px; } } .video-miniature-information { width: 200px; + margin-top: 2px; + line-height: normal; .video-miniature-name { - height: 23px; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: bold; transition: color 0.2s; - font-size: 15px; + font-size: 16px; + font-weight: $font-semibold; + color: #000; &:hover { text-decoration: none; @@ -63,39 +58,16 @@ filter: blur(3px); padding-left: 4px; } - - .video-miniature-tags { - // Fix for chrome when tags are long - width: 201px; - - .video-miniature-tag { - font-size: 13px; - cursor: pointer; - position: relative; - top: -2px; - - .label { - transition: background-color 0.2s; - } - } - } } - .video-miniature-account, .video-miniature-created-at { + .video-miniature-created-at-views { display: block; - margin-left: 1px; - font-size: 11px; - color: $video-miniature-other-infos; - opacity: 0.9; + font-size: 13px; } .video-miniature-account { - transition: color 0.2s; - - &:hover { - color: #23527c; - text-decoration: none; - } + font-size: 12px; + color: #585858; } } } diff --git a/client/src/app/videos/video-list/shared/video-sort.component.html b/client/src/app/videos/video-list/shared/video-sort.component.html deleted file mode 100644 index 3bece0b22..000000000 --- a/client/src/app/videos/video-list/shared/video-sort.component.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/client/src/app/videos/video-list/shared/video-sort.component.ts b/client/src/app/videos/video-list/shared/video-sort.component.ts deleted file mode 100644 index 8aa89d32b..000000000 --- a/client/src/app/videos/video-list/shared/video-sort.component.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core' - -import { SortField } from '../../shared' - -@Component({ - selector: 'my-video-sort', - templateUrl: './video-sort.component.html' -}) - -export class VideoSortComponent { - @Output() sort = new EventEmitter() - - @Input() currentSort: SortField - - sortChoices: { [ P in SortField ]: string } = { - 'name': 'Name - Asc', - '-name': 'Name - Desc', - 'duration': 'Duration - Asc', - '-duration': 'Duration - Desc', - 'createdAt': 'Created Date - Asc', - '-createdAt': 'Created Date - Desc', - 'views': 'Views - Asc', - '-views': 'Views - Desc', - 'likes': 'Likes - Asc', - '-likes': 'Likes - Desc' - } - - get choiceKeys () { - return Object.keys(this.sortChoices) - } - - getStringChoice (choiceKey: SortField) { - return this.sortChoices[choiceKey] - } - - onSortChange () { - this.sort.emit(this.currentSort) - } -} diff --git a/client/src/app/videos/video-list/video-list.component.ts b/client/src/app/videos/video-list/video-list.component.ts deleted file mode 100644 index 784162679..000000000 --- a/client/src/app/videos/video-list/video-list.component.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' -import { ActivatedRoute, Router } from '@angular/router' -import { Subscription } from 'rxjs/Subscription' - -import { NotificationsService } from 'angular2-notifications' - -import { VideoService } from '../shared' -import { Search, SearchField, SearchService } from '../../shared' -import { AbstractVideoList } from './shared' - -@Component({ - selector: 'my-videos-list', - styleUrls: [ './shared/abstract-video-list.scss' ], - templateUrl: './shared/abstract-video-list.html' -}) -export class VideoListComponent extends AbstractVideoList implements OnInit, OnDestroy { - private search: Search - private subSearch: Subscription - - constructor ( - protected router: Router, - protected route: ActivatedRoute, - protected notificationsService: NotificationsService, - private videoService: VideoService, - private searchService: SearchService - ) { - super() - } - - ngOnInit () { - // Subscribe to route changes - this.subActivatedRoute = this.route.params.subscribe(routeParams => { - this.loadRouteParams(routeParams) - - // Update the search service component - this.searchService.updateSearch.next(this.search) - this.getVideos() - }) - - // Subscribe to search changes - this.subSearch = this.searchService.searchUpdated.subscribe(search => { - this.search = search - // Reset pagination - this.pagination.currentPage = 1 - - this.navigateToNewParams() - }) - } - - ngOnDestroy () { - super.ngOnDestroy() - - this.subSearch.unsubscribe() - } - - getVideosObservable () { - let observable = null - if (this.search.value) { - observable = this.videoService.searchVideos(this.search, this.pagination, this.sort) - } else { - observable = this.videoService.getVideos(this.pagination, this.sort) - } - - return observable - } - - protected buildRouteParams () { - const params = super.buildRouteParams() - - // Maybe there is a search - if (this.search.value) { - params['field'] = this.search.field - params['search'] = this.search.value - } - - return params - } - - protected loadRouteParams (routeParams: { [ key: string ]: any }) { - super.loadRouteParams(routeParams) - - if (routeParams['search'] !== undefined) { - this.search = { - value: routeParams['search'], - field: routeParams['field'] as SearchField - } - } else { - this.search = { - value: '', - field: 'name' - } - } - } -} diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts new file mode 100644 index 000000000..dbba264df --- /dev/null +++ b/client/src/app/videos/video-list/video-recently-added.component.ts @@ -0,0 +1,33 @@ +import { Component, OnDestroy, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { VideoService } from '../shared' +import { AbstractVideoList } from './shared' + +@Component({ + selector: 'my-videos-recently-added', + styleUrls: [ './shared/abstract-video-list.scss' ], + templateUrl: './shared/abstract-video-list.html' +}) +export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { + titlePage = 'Recently added' + + constructor (protected router: Router, + protected route: ActivatedRoute, + protected notificationsService: NotificationsService, + private videoService: VideoService) { + super() + } + + ngOnInit () { + super.ngOnInit() + } + + ngOnDestroy () { + super.ngOnDestroy() + } + + getVideosObservable () { + return this.videoService.getVideos(this.pagination, this.sort) + } +} diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts new file mode 100644 index 000000000..b97966c12 --- /dev/null +++ b/client/src/app/videos/video-list/video-trending.component.ts @@ -0,0 +1,33 @@ +import { Component, OnDestroy, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { VideoService } from '../shared' +import { AbstractVideoList } from './shared' + +@Component({ + selector: 'my-videos-trending', + styleUrls: [ './shared/abstract-video-list.scss' ], + templateUrl: './shared/abstract-video-list.html' +}) +export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { + titlePage = 'Trending' + + constructor (protected router: Router, + protected route: ActivatedRoute, + protected notificationsService: NotificationsService, + private videoService: VideoService) { + super() + } + + ngOnInit () { + super.ngOnInit() + } + + ngOnDestroy () { + super.ngOnDestroy() + } + + getVideosObservable () { + return this.videoService.getVideos(this.pagination, this.sort) + } +} diff --git a/client/src/app/videos/videos-routing.module.ts b/client/src/app/videos/videos-routing.module.ts index 3ca3e5486..1f894df7a 100644 --- a/client/src/app/videos/videos-routing.module.ts +++ b/client/src/app/videos/videos-routing.module.ts @@ -1,9 +1,9 @@ import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' - import { MetaGuard } from '@ngx-meta/core' - -import { VideoListComponent, MyVideosComponent } from './video-list' +import { MyVideosComponent } from './video-list' +import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' +import { VideoTrendingComponent } from './video-list/video-trending.component' import { VideosComponent } from './videos.component' const videosRoutes: Routes = [ @@ -12,6 +12,11 @@ const videosRoutes: Routes = [ component: VideosComponent, canActivateChild: [ MetaGuard ], children: [ + { + path: 'list', + pathMatch: 'full', + redirectTo: 'recently-added' + }, { path: 'mine', component: MyVideosComponent, @@ -22,11 +27,20 @@ const videosRoutes: Routes = [ } }, { - path: 'list', - component: VideoListComponent, + path: 'trending', + component: VideoTrendingComponent, + data: { + meta: { + title: 'Trending videos' + } + } + }, + { + path: 'recently-added', + component: VideoRecentlyAddedComponent, data: { meta: { - title: 'Videos list' + title: 'Recently added videos' } } }, diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts index 4f3054c3a..93193000c 100644 --- a/client/src/app/videos/videos.module.ts +++ b/client/src/app/videos/videos.module.ts @@ -1,7 +1,9 @@ import { NgModule } from '@angular/core' import { SharedModule } from '../shared' import { VideoService } from './shared' -import { MyVideosComponent, VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list' +import { MyVideosComponent, VideoMiniatureComponent } from './video-list' +import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' +import { VideoTrendingComponent } from './video-list/video-trending.component' import { VideosRoutingModule } from './videos-routing.module' import { VideosComponent } from './videos.component' @@ -14,10 +16,10 @@ import { VideosComponent } from './videos.component' declarations: [ VideosComponent, - VideoListComponent, + VideoTrendingComponent, + VideoRecentlyAddedComponent, MyVideosComponent, - VideoMiniatureComponent, - VideoSortComponent + VideoMiniatureComponent ], exports: [ diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 58f07612b..fc61a22da 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -33,24 +33,14 @@ input.readonly { } .main-col { - .content-padding { - padding: 15px 30px; - - @media screen and (max-width: 800px) { - padding: 15px 10px; - } - - @media screen and (min-width: 1400px) { - padding: 15px 40px; - } - - @media screen and (min-width: 1600px) { - padding: 15px 50px; - } - - @media screen and (min-width: 1800px) { - padding: 15px 60px; - } + padding: 30px; + + .title-page { + font-size: 16px; + font-weight: $font-bold; + display: inline-block; + border-bottom: 2px solid $orange-color; + margin-bottom: 25px; } } diff --git a/client/yarn.lock b/client/yarn.lock index 8f148e431..fa1802a29 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -264,10 +264,6 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" -angular-pipes@^6.0.0: - version "6.5.3" - resolved "https://registry.yarnpkg.com/angular-pipes/-/angular-pipes-6.5.3.tgz#6bed37c51ebc2adaf3412663bfe25179d0489b02" - angular2-notifications@^0.7.7: version "0.7.8" resolved "https://registry.yarnpkg.com/angular2-notifications/-/angular2-notifications-0.7.8.tgz#ecbcb95a8d2d402af94a9a080d6664c70d33a029" @@ -4718,6 +4714,10 @@ ngx-chips@1.5.3: dependencies: ng2-material-dropdown "0.7.10" +ngx-pipes@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.0.5.tgz#743b827e350b1e66f5bdae49e90a02fa631d4c54" + no-case@^2.2.0: version "2.3.2" resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" -- cgit v1.2.3 From 2bbb34127fccd187ed690949b6791e49fdd77194 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 1 Dec 2017 16:17:32 +0100 Subject: Add auto scroll to videos list --- client/package.json | 1 + client/src/app/app-routing.module.ts | 2 +- client/src/app/shared/misc/from-now.pipe.ts | 2 +- client/src/app/shared/search/search.component.ts | 33 ++---------- client/src/app/shared/shared.module.ts | 7 +-- .../video-list/shared/abstract-video-list.html | 14 ++--- .../video-list/shared/abstract-video-list.ts | 63 ++++++++++++++++------ .../video-list/video-recently-added.component.ts | 1 + .../videos/video-list/video-trending.component.ts | 1 + client/src/app/videos/videos.module.ts | 4 +- client/yarn.lock | 4 ++ 11 files changed, 73 insertions(+), 59 deletions(-) (limited to 'client') diff --git a/client/package.json b/client/package.json index 310860fec..45f555f29 100644 --- a/client/package.json +++ b/client/package.json @@ -71,6 +71,7 @@ "ngc-webpack": "3.2.2", "ngx-bootstrap": "2.0.0-beta.9", "ngx-chips": "1.5.3", + "ngx-infinite-scroll": "^0.7.0", "ngx-pipes": "^2.0.5", "node-sass": "^4.1.1", "normalize.css": "^7.0.0", diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 0f9484344..fe72c9181 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -6,7 +6,7 @@ import { PreloadSelectedModulesList } from './core' const routes: Routes = [ { path: '', - redirectTo: '/videos/list', + redirectTo: '/videos/trending', pathMatch: 'full' }, { diff --git a/client/src/app/shared/misc/from-now.pipe.ts b/client/src/app/shared/misc/from-now.pipe.ts index 25e5d6a85..80dab02ba 100644 --- a/client/src/app/shared/misc/from-now.pipe.ts +++ b/client/src/app/shared/misc/from-now.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core' -// Thanks: https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts +// Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site @Pipe({name: 'fromNow'}) export class FromNowPipe implements PipeTransform { diff --git a/client/src/app/shared/search/search.component.ts b/client/src/app/shared/search/search.component.ts index 6ef19c97a..f49ecc8ad 100644 --- a/client/src/app/shared/search/search.component.ts +++ b/client/src/app/shared/search/search.component.ts @@ -1,8 +1,6 @@ import { Component, OnInit } from '@angular/core' import { Router } from '@angular/router' - import { Search } from './search.model' -import { SearchField } from './search-field.type' import { SearchService } from './search.service' @Component({ @@ -12,12 +10,6 @@ import { SearchService } from './search.service' }) export class SearchComponent implements OnInit { - fieldChoices = { - name: 'Name', - account: 'Account', - host: 'Host', - tags: 'Tags' - } searchCriteria: Search = { field: 'name', value: '' @@ -40,30 +32,11 @@ export class SearchComponent implements OnInit { ) } - get choiceKeys () { - return Object.keys(this.fieldChoices) - } - - choose ($event: MouseEvent, choice: SearchField) { - $event.preventDefault() - $event.stopPropagation() - - this.searchCriteria.field = choice - - if (this.searchCriteria.value) { - this.doSearch() - } - } - doSearch () { - if (this.router.url.indexOf('/videos/list') === -1) { - this.router.navigate([ '/videos/list' ]) - } + // if (this.router.url.indexOf('/videos/list') === -1) { + // this.router.navigate([ '/videos/list' ]) + // } this.searchService.searchUpdated.next(this.searchCriteria) } - - getStringChoice (choiceKey: SearchField) { - return this.fieldChoices[choiceKey] - } } diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index c7ea6e603..7618748e9 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -6,21 +6,20 @@ import { RouterModule } from '@angular/router' import { BsDropdownModule } from 'ngx-bootstrap/dropdown' import { ModalModule } from 'ngx-bootstrap/modal' -import { PaginationModule } from 'ngx-bootstrap/pagination' import { ProgressbarModule } from 'ngx-bootstrap/progressbar' import { BytesPipe, KeysPipe } from 'ngx-pipes' import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' import { DataTableModule } from 'primeng/components/datatable/datatable' import { AUTH_INTERCEPTOR_PROVIDER } from './auth' +import { FromNowPipe } from './misc/from-now.pipe' import { LoaderComponent } from './misc/loader.component' +import { NumberFormatterPipe } from './misc/number-formatter.pipe' import { RestExtractor, RestService } from './rest' import { SearchComponent, SearchService } from './search' import { UserService } from './users' import { VideoAbuseService } from './video-abuse' import { VideoBlacklistService } from './video-blacklist' -import { NumberFormatterPipe } from './misc/number-formatter.pipe' -import { FromNowPipe } from './misc/from-now.pipe' @NgModule({ imports: [ @@ -32,7 +31,6 @@ import { FromNowPipe } from './misc/from-now.pipe' BsDropdownModule.forRoot(), ModalModule.forRoot(), - PaginationModule.forRoot(), ProgressbarModule.forRoot(), DataTableModule, @@ -57,7 +55,6 @@ import { FromNowPipe } from './misc/from-now.pipe' BsDropdownModule, ModalModule, - PaginationModule, ProgressbarModule, DataTableModule, PrimeSharedModule, diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.html b/client/src/app/videos/video-list/shared/abstract-video-list.html index ab5530e68..69e16319e 100644 --- a/client/src/app/videos/video-list/shared/abstract-video-list.html +++ b/client/src/app/videos/video-list/shared/abstract-video-list.html @@ -2,15 +2,17 @@ {{ titlePage }}
-
+
- - diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.ts b/client/src/app/videos/video-list/shared/abstract-video-list.ts index 262ea4e21..44cdc1d9f 100644 --- a/client/src/app/videos/video-list/shared/abstract-video-list.ts +++ b/client/src/app/videos/video-list/shared/abstract-video-list.ts @@ -19,44 +19,77 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { protected notificationsService: NotificationsService protected router: Router protected route: ActivatedRoute - protected subActivatedRoute: Subscription + protected abstract currentRoute: string + abstract titlePage: string + private loadedPages: { [ id: number ]: boolean } = {} + abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}> ngOnInit () { // Subscribe to route changes - this.subActivatedRoute = this.route.params.subscribe(routeParams => { - this.loadRouteParams(routeParams) - - this.getVideos() - }) + const routeParams = this.route.snapshot.params + this.loadRouteParams(routeParams) + this.loadMoreVideos('after') } ngOnDestroy () { this.subActivatedRoute.unsubscribe() } - getVideos () { - this.videos = [] + onNearOfTop () { + if (this.pagination.currentPage > 1) { + this.previousPage() + } + } + + onNearOfBottom () { + if (this.hasMoreVideos()) { + this.nextPage() + } + } + + loadMoreVideos (where: 'before' | 'after') { + if (this.loadedPages[this.pagination.currentPage] === true) return const observable = this.getVideosObservable() observable.subscribe( ({ videos, totalVideos }) => { - this.videos = videos + this.loadedPages[this.pagination.currentPage] = true this.pagination.totalItems = totalVideos + + if (where === 'before') { + this.videos = videos.concat(this.videos) + } else { + this.videos = this.videos.concat(videos) + } }, error => this.notificationsService.error('Error', error.text) ) } - onPageChanged (event: { page: number }) { - // Be sure the current page is set - this.pagination.currentPage = event.page + protected hasMoreVideos () { + if (!this.pagination.totalItems) return true + + const maxPage = this.pagination.totalItems/this.pagination.itemsPerPage + return maxPage > this.pagination.currentPage + } + + protected previousPage () { + this.pagination.currentPage-- + + this.setNewRouteParams() + this.loadMoreVideos('before') + } + + protected nextPage () { + this.pagination.currentPage++ - this.navigateToNewParams() + this.setNewRouteParams() + this.loadMoreVideos('after') } protected buildRouteParams () { @@ -79,8 +112,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { } } - protected navigateToNewParams () { + protected setNewRouteParams () { const routeParams = this.buildRouteParams() - this.router.navigate([ '/videos/list', routeParams ]) + this.router.navigate([ this.currentRoute, routeParams ]) } } diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts index dbba264df..9bf69bd78 100644 --- a/client/src/app/videos/video-list/video-recently-added.component.ts +++ b/client/src/app/videos/video-list/video-recently-added.component.ts @@ -11,6 +11,7 @@ import { AbstractVideoList } from './shared' }) export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { titlePage = 'Recently added' + currentRoute = '/videos/recently-added' constructor (protected router: Router, protected route: ActivatedRoute, diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts index b97966c12..a1df68711 100644 --- a/client/src/app/videos/video-list/video-trending.component.ts +++ b/client/src/app/videos/video-list/video-trending.component.ts @@ -11,6 +11,7 @@ import { AbstractVideoList } from './shared' }) export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { titlePage = 'Trending' + currentRoute = '/videos/trending' constructor (protected router: Router, protected route: ActivatedRoute, diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts index 93193000c..f3981d275 100644 --- a/client/src/app/videos/videos.module.ts +++ b/client/src/app/videos/videos.module.ts @@ -1,4 +1,5 @@ import { NgModule } from '@angular/core' +import { InfiniteScrollModule } from 'ngx-infinite-scroll' import { SharedModule } from '../shared' import { VideoService } from './shared' import { MyVideosComponent, VideoMiniatureComponent } from './video-list' @@ -10,7 +11,8 @@ import { VideosComponent } from './videos.component' @NgModule({ imports: [ VideosRoutingModule, - SharedModule + SharedModule, + InfiniteScrollModule ], declarations: [ diff --git a/client/yarn.lock b/client/yarn.lock index fa1802a29..bd6870061 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -4714,6 +4714,10 @@ ngx-chips@1.5.3: dependencies: ng2-material-dropdown "0.7.10" +ngx-infinite-scroll@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-0.7.0.tgz#a390c61c6a05ac14485e1c5bc8b4e6f6bd62fd6a" + ngx-pipes@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.0.5.tgz#743b827e350b1e66f5bdae49e90a02fa631d4c54" -- cgit v1.2.3 From c30745f342480b59fb0856a059c8c2fbffbcfc6a Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 1 Dec 2017 17:38:26 +0100 Subject: Add account settings new design --- client/src/app/+admin/users/shared/user.service.ts | 14 ++--- .../account-change-password.component.html | 24 -------- .../account-change-password.component.ts | 65 --------------------- .../app/account/account-change-password/index.ts | 1 - .../account-details/account-details.component.html | 16 ----- .../account-details/account-details.component.ts | 68 ---------------------- client/src/app/account/account-details/index.ts | 1 - client/src/app/account/account-routing.module.ts | 28 +++++++-- .../account-change-password.component.html | 18 ++++++ .../account-change-password.component.scss | 9 +++ .../account-change-password.component.ts | 62 ++++++++++++++++++++ .../account-change-password/index.ts | 1 + .../account-details/account-details.component.html | 14 +++++ .../account-details/account-details.component.scss | 11 ++++ .../account-details/account-details.component.ts | 61 +++++++++++++++++++ .../account-settings/account-details/index.ts | 1 + .../account-settings.component.html | 9 +++ .../account-settings.component.scss | 13 +++++ .../account-settings/account-settings.component.ts | 18 ++++++ client/src/app/account/account.component.html | 26 ++------- client/src/app/account/account.component.scss | 3 - client/src/app/account/account.component.ts | 24 +------- client/src/app/account/account.module.ts | 9 +-- client/src/app/app.component.scss | 8 --- client/src/app/menu/menu.component.html | 2 +- client/src/app/menu/menu.component.scss | 29 +++++---- client/src/app/shared/search/search.component.scss | 22 +------ client/src/app/videos/video-list/index.ts | 1 - .../app/videos/video-list/my-videos.component.ts | 36 ------------ .../video-list/shared/abstract-video-list.html | 31 +++++----- .../video-list/shared/abstract-video-list.scss | 23 -------- .../video-list/shared/abstract-video-list.ts | 4 +- client/src/app/videos/videos-routing.module.ts | 10 ---- client/src/app/videos/videos.module.ts | 3 +- client/src/sass/_mixins.scss | 35 +++++++++++ client/src/sass/_variables.scss | 2 + client/src/sass/application.scss | 44 ++++++++++++-- client/src/sass/pre-customizations.scss | 1 + 38 files changed, 377 insertions(+), 370 deletions(-) delete mode 100644 client/src/app/account/account-change-password/account-change-password.component.html delete mode 100644 client/src/app/account/account-change-password/account-change-password.component.ts delete mode 100644 client/src/app/account/account-change-password/index.ts delete mode 100644 client/src/app/account/account-details/account-details.component.html delete mode 100644 client/src/app/account/account-details/account-details.component.ts delete mode 100644 client/src/app/account/account-details/index.ts create mode 100644 client/src/app/account/account-settings/account-change-password/account-change-password.component.html create mode 100644 client/src/app/account/account-settings/account-change-password/account-change-password.component.scss create mode 100644 client/src/app/account/account-settings/account-change-password/account-change-password.component.ts create mode 100644 client/src/app/account/account-settings/account-change-password/index.ts create mode 100644 client/src/app/account/account-settings/account-details/account-details.component.html create mode 100644 client/src/app/account/account-settings/account-details/account-details.component.scss create mode 100644 client/src/app/account/account-settings/account-details/account-details.component.ts create mode 100644 client/src/app/account/account-settings/account-details/index.ts create mode 100644 client/src/app/account/account-settings/account-settings.component.html create mode 100644 client/src/app/account/account-settings/account-settings.component.scss create mode 100644 client/src/app/account/account-settings/account-settings.component.ts delete mode 100644 client/src/app/videos/video-list/my-videos.component.ts (limited to 'client') diff --git a/client/src/app/+admin/users/shared/user.service.ts b/client/src/app/+admin/users/shared/user.service.ts index e4bd5df37..dc77cc1d8 100644 --- a/client/src/app/+admin/users/shared/user.service.ts +++ b/client/src/app/+admin/users/shared/user.service.ts @@ -1,14 +1,12 @@ -import { Injectable } from '@angular/core' import { HttpClient, HttpParams } from '@angular/common/http' -import { Observable } from 'rxjs/Observable' +import { Injectable } from '@angular/core' +import { BytesPipe } from 'ngx-pipes' +import { SortMeta } from 'primeng/components/common/sortmeta' import 'rxjs/add/operator/catch' import 'rxjs/add/operator/map' - -import { SortMeta } from 'primeng/components/common/sortmeta' -import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe' - -import { RestExtractor, User, RestPagination, RestService } from '../../../shared' -import { UserCreate, UserUpdate, ResultList } from '../../../../../../shared' +import { Observable } from 'rxjs/Observable' +import { ResultList, UserCreate, UserUpdate } from '../../../../../../shared' +import { RestExtractor, RestPagination, RestService, User } from '../../../shared' @Injectable() export class UserService { diff --git a/client/src/app/account/account-change-password/account-change-password.component.html b/client/src/app/account/account-change-password/account-change-password.component.html deleted file mode 100644 index 92d9f900a..000000000 --- a/client/src/app/account/account-change-password/account-change-password.component.html +++ /dev/null @@ -1,24 +0,0 @@ -
{{ error }}
- -
-
- - -
- {{ formErrors['new-password'] }} -
-
- -
- - -
- - -
diff --git a/client/src/app/account/account-change-password/account-change-password.component.ts b/client/src/app/account/account-change-password/account-change-password.component.ts deleted file mode 100644 index 69edec54b..000000000 --- a/client/src/app/account/account-change-password/account-change-password.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Component, OnInit } from '@angular/core' -import { FormBuilder, FormGroup } from '@angular/forms' -import { Router } from '@angular/router' - -import { NotificationsService } from 'angular2-notifications' - -import { FormReactive, UserService, USER_PASSWORD } from '../../shared' - -@Component({ - selector: 'my-account-change-password', - templateUrl: './account-change-password.component.html' -}) - -export class AccountChangePasswordComponent extends FormReactive implements OnInit { - error: string = null - - form: FormGroup - formErrors = { - 'new-password': '', - 'new-confirmed-password': '' - } - validationMessages = { - 'new-password': USER_PASSWORD.MESSAGES, - 'new-confirmed-password': USER_PASSWORD.MESSAGES - } - - constructor ( - private formBuilder: FormBuilder, - private notificationsService: NotificationsService, - private userService: UserService - ) { - super() - } - - buildForm () { - this.form = this.formBuilder.group({ - 'new-password': [ '', USER_PASSWORD.VALIDATORS ], - 'new-confirmed-password': [ '', USER_PASSWORD.VALIDATORS ] - }) - - this.form.valueChanges.subscribe(data => this.onValueChanged(data)) - } - - ngOnInit () { - this.buildForm() - } - - changePassword () { - const newPassword = this.form.value['new-password'] - const newConfirmedPassword = this.form.value['new-confirmed-password'] - - this.error = null - - if (newPassword !== newConfirmedPassword) { - this.error = 'The new password and the confirmed password do not correspond.' - return - } - - this.userService.changePassword(newPassword).subscribe( - () => this.notificationsService.success('Success', 'Password updated.'), - - err => this.error = err.message - ) - } -} diff --git a/client/src/app/account/account-change-password/index.ts b/client/src/app/account/account-change-password/index.ts deleted file mode 100644 index 44c330b66..000000000 --- a/client/src/app/account/account-change-password/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './account-change-password.component' diff --git a/client/src/app/account/account-details/account-details.component.html b/client/src/app/account/account-details/account-details.component.html deleted file mode 100644 index 8f4f176af..000000000 --- a/client/src/app/account/account-details/account-details.component.html +++ /dev/null @@ -1,16 +0,0 @@ -
{{ error }}
- -
-
- - -
- {{ formErrors['displayNSFW'] }} -
-
- - -
diff --git a/client/src/app/account/account-details/account-details.component.ts b/client/src/app/account/account-details/account-details.component.ts deleted file mode 100644 index d7a6e6871..000000000 --- a/client/src/app/account/account-details/account-details.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Component, OnInit, Input } from '@angular/core' -import { FormBuilder, FormGroup } from '@angular/forms' -import { Router } from '@angular/router' - -import { NotificationsService } from 'angular2-notifications' - -import { AuthService } from '../../core' -import { - FormReactive, - User, - UserService, - USER_PASSWORD -} from '../../shared' -import { UserUpdateMe } from '../../../../../shared' - -@Component({ - selector: 'my-account-details', - templateUrl: './account-details.component.html' -}) - -export class AccountDetailsComponent extends FormReactive implements OnInit { - @Input() user: User = null - - error: string = null - - form: FormGroup - formErrors = {} - validationMessages = {} - - constructor ( - private authService: AuthService, - private formBuilder: FormBuilder, - private notificationsService: NotificationsService, - private userService: UserService - ) { - super() - } - - buildForm () { - this.form = this.formBuilder.group({ - displayNSFW: [ this.user.displayNSFW ] - }) - - this.form.valueChanges.subscribe(data => this.onValueChanged(data)) - } - - ngOnInit () { - this.buildForm() - } - - updateDetails () { - const displayNSFW = this.form.value['displayNSFW'] - const details: UserUpdateMe = { - displayNSFW - } - - this.error = null - this.userService.updateMyDetails(details).subscribe( - () => { - this.notificationsService.success('Success', 'Information updated.') - - this.authService.refreshUserInformation() - }, - - err => this.error = err.message - ) - } -} diff --git a/client/src/app/account/account-details/index.ts b/client/src/app/account/account-details/index.ts deleted file mode 100644 index 4829f608a..000000000 --- a/client/src/app/account/account-details/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './account-details.component' diff --git a/client/src/app/account/account-routing.module.ts b/client/src/app/account/account-routing.module.ts index 74d9aa03e..2e9de1cfb 100644 --- a/client/src/app/account/account-routing.module.ts +++ b/client/src/app/account/account-routing.module.ts @@ -5,17 +5,33 @@ import { MetaGuard } from '@ngx-meta/core' import { LoginGuard } from '../core' import { AccountComponent } from './account.component' +import { AccountSettingsComponent } from './account-settings/account-settings.component' const accountRoutes: Routes = [ { path: 'account', component: AccountComponent, - canActivate: [ MetaGuard, LoginGuard ], - data: { - meta: { - title: 'My account' - } - } + canActivateChild: [ MetaGuard, LoginGuard ], + children: [ + { + path: 'settings', + component: AccountSettingsComponent, + data: { + meta: { + title: 'Account settings' + } + } + }, + // { + // path: 'videos', + // component: AccountVideosComponent, + // data: { + // meta: { + // title: 'Account videos' + // } + // } + // } + ] } ] diff --git a/client/src/app/account/account-settings/account-change-password/account-change-password.component.html b/client/src/app/account/account-settings/account-change-password/account-change-password.component.html new file mode 100644 index 000000000..bfb55218f --- /dev/null +++ b/client/src/app/account/account-settings/account-change-password/account-change-password.component.html @@ -0,0 +1,18 @@ +
{{ error }}
+ +
+ +
+ {{ formErrors['new-password'] }} +
+ + + + +
diff --git a/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss b/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss new file mode 100644 index 000000000..593355b70 --- /dev/null +++ b/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss @@ -0,0 +1,9 @@ +input[type=password] { + @include peertube-input-text(340px); + display: block; + margin-bottom: 10px; +} + +input[type=submit] { + @include peertube-button; +} diff --git a/client/src/app/account/account-settings/account-change-password/account-change-password.component.ts b/client/src/app/account/account-settings/account-change-password/account-change-password.component.ts new file mode 100644 index 000000000..8979e1734 --- /dev/null +++ b/client/src/app/account/account-settings/account-change-password/account-change-password.component.ts @@ -0,0 +1,62 @@ +import { Component, OnInit } from '@angular/core' +import { FormBuilder, FormGroup } from '@angular/forms' +import { NotificationsService } from 'angular2-notifications' +import { FormReactive, USER_PASSWORD, UserService } from '../../../shared' + +@Component({ + selector: 'my-account-change-password', + templateUrl: './account-change-password.component.html', + styleUrls: [ './account-change-password.component.scss' ] +}) +export class AccountChangePasswordComponent extends FormReactive implements OnInit { + error: string = null + + form: FormGroup + formErrors = { + 'new-password': '', + 'new-confirmed-password': '' + } + validationMessages = { + 'new-password': USER_PASSWORD.MESSAGES, + 'new-confirmed-password': USER_PASSWORD.MESSAGES + } + + constructor ( + private formBuilder: FormBuilder, + private notificationsService: NotificationsService, + private userService: UserService + ) { + super() + } + + buildForm () { + this.form = this.formBuilder.group({ + 'new-password': [ '', USER_PASSWORD.VALIDATORS ], + 'new-confirmed-password': [ '', USER_PASSWORD.VALIDATORS ] + }) + + this.form.valueChanges.subscribe(data => this.onValueChanged(data)) + } + + ngOnInit () { + this.buildForm() + } + + changePassword () { + const newPassword = this.form.value['new-password'] + const newConfirmedPassword = this.form.value['new-confirmed-password'] + + this.error = null + + if (newPassword !== newConfirmedPassword) { + this.error = 'The new password and the confirmed password do not correspond.' + return + } + + this.userService.changePassword(newPassword).subscribe( + () => this.notificationsService.success('Success', 'Password updated.'), + + err => this.error = err.message + ) + } +} diff --git a/client/src/app/account/account-settings/account-change-password/index.ts b/client/src/app/account/account-settings/account-change-password/index.ts new file mode 100644 index 000000000..44c330b66 --- /dev/null +++ b/client/src/app/account/account-settings/account-change-password/index.ts @@ -0,0 +1 @@ +export * from './account-change-password.component' diff --git a/client/src/app/account/account-settings/account-details/account-details.component.html b/client/src/app/account/account-settings/account-details/account-details.component.html new file mode 100644 index 000000000..c3cf6b629 --- /dev/null +++ b/client/src/app/account/account-settings/account-details/account-details.component.html @@ -0,0 +1,14 @@ +
{{ error }}
+ +
+ + +
+ {{ formErrors['displayNSFW'] }} +
+ + +
diff --git a/client/src/app/account/account-settings/account-details/account-details.component.scss b/client/src/app/account/account-settings/account-details/account-details.component.scss new file mode 100644 index 000000000..b1810d4f9 --- /dev/null +++ b/client/src/app/account/account-settings/account-details/account-details.component.scss @@ -0,0 +1,11 @@ +label { + font-size: 15px; + font-weight: $font-regular; + margin-left: 5px; +} + +input[type=submit] { + @include peertube-button; + + display: block; +} diff --git a/client/src/app/account/account-settings/account-details/account-details.component.ts b/client/src/app/account/account-settings/account-details/account-details.component.ts new file mode 100644 index 000000000..d835c53e5 --- /dev/null +++ b/client/src/app/account/account-settings/account-details/account-details.component.ts @@ -0,0 +1,61 @@ +import { Component, Input, OnInit } from '@angular/core' +import { FormBuilder, FormGroup } from '@angular/forms' +import { NotificationsService } from 'angular2-notifications' +import { UserUpdateMe } from '../../../../../../shared' +import { AuthService } from '../../../core' +import { FormReactive, User, UserService } from '../../../shared' + +@Component({ + selector: 'my-account-details', + templateUrl: './account-details.component.html', + styleUrls: [ './account-details.component.scss' ] +}) + +export class AccountDetailsComponent extends FormReactive implements OnInit { + @Input() user: User = null + + error: string = null + + form: FormGroup + formErrors = {} + validationMessages = {} + + constructor ( + private authService: AuthService, + private formBuilder: FormBuilder, + private notificationsService: NotificationsService, + private userService: UserService + ) { + super() + } + + buildForm () { + this.form = this.formBuilder.group({ + displayNSFW: [ this.user.displayNSFW ] + }) + + this.form.valueChanges.subscribe(data => this.onValueChanged(data)) + } + + ngOnInit () { + this.buildForm() + } + + updateDetails () { + const displayNSFW = this.form.value['displayNSFW'] + const details: UserUpdateMe = { + displayNSFW + } + + this.error = null + this.userService.updateMyDetails(details).subscribe( + () => { + this.notificationsService.success('Success', 'Information updated.') + + this.authService.refreshUserInformation() + }, + + err => this.error = err.message + ) + } +} diff --git a/client/src/app/account/account-settings/account-details/index.ts b/client/src/app/account/account-settings/account-details/index.ts new file mode 100644 index 000000000..4829f608a --- /dev/null +++ b/client/src/app/account/account-settings/account-details/index.ts @@ -0,0 +1 @@ +export * from './account-details.component' diff --git a/client/src/app/account/account-settings/account-settings.component.html b/client/src/app/account/account-settings/account-settings.component.html new file mode 100644 index 000000000..2509eb5aa --- /dev/null +++ b/client/src/app/account/account-settings/account-settings.component.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/client/src/app/account/account-settings/account-settings.component.scss b/client/src/app/account/account-settings/account-settings.component.scss new file mode 100644 index 000000000..a0822631d --- /dev/null +++ b/client/src/app/account/account-settings/account-settings.component.scss @@ -0,0 +1,13 @@ +.user-info { + font-size: 20px; + font-weight: $font-bold; +} + +.account-title { + text-transform: uppercase; + color: $orange-color; + font-weight: $font-bold; + font-size: 13px; + margin-top: 55px; + margin-bottom: 30px; +} diff --git a/client/src/app/account/account-settings/account-settings.component.ts b/client/src/app/account/account-settings/account-settings.component.ts new file mode 100644 index 000000000..c3b670e02 --- /dev/null +++ b/client/src/app/account/account-settings/account-settings.component.ts @@ -0,0 +1,18 @@ +import { Component, OnInit } from '@angular/core' +import { User } from '../../shared' +import { AuthService } from '../../core' + +@Component({ + selector: 'my-account-settings', + templateUrl: './account-settings.component.html', + styleUrls: [ './account-settings.component.scss' ] +}) +export class AccountSettingsComponent implements OnInit { + user: User = null + + constructor (private authService: AuthService) {} + + ngOnInit () { + this.user = this.authService.getUser() + } +} diff --git a/client/src/app/account/account.component.html b/client/src/app/account/account.component.html index 177e54999..d82a4ca4d 100644 --- a/client/src/app/account/account.component.html +++ b/client/src/app/account/account.component.html @@ -1,25 +1,11 @@
-
-

Account

+ +
+
diff --git a/client/src/app/account/account.component.scss b/client/src/app/account/account.component.scss index 61b80d0a7..e69de29bb 100644 --- a/client/src/app/account/account.component.scss +++ b/client/src/app/account/account.component.scss @@ -1,3 +0,0 @@ -.panel { - margin-top: 40px; -} diff --git a/client/src/app/account/account.component.ts b/client/src/app/account/account.component.ts index 929934f67..3d3677ab0 100644 --- a/client/src/app/account/account.component.ts +++ b/client/src/app/account/account.component.ts @@ -1,28 +1,8 @@ -import { Component, OnInit } from '@angular/core' -import { FormBuilder, FormGroup } from '@angular/forms' -import { Router } from '@angular/router' - -import { NotificationsService } from 'angular2-notifications' - -import { AuthService } from '../core' -import { - FormReactive, - User, - UserService, - USER_PASSWORD -} from '../shared' +import { Component } from '@angular/core' @Component({ selector: 'my-account', templateUrl: './account.component.html', styleUrls: [ './account.component.scss' ] }) -export class AccountComponent implements OnInit { - user: User = null - - constructor (private authService: AuthService) {} - - ngOnInit () { - this.user = this.authService.getUser() - } -} +export class AccountComponent {} diff --git a/client/src/app/account/account.module.ts b/client/src/app/account/account.module.ts index 380e9d235..ff444ddeb 100644 --- a/client/src/app/account/account.module.ts +++ b/client/src/app/account/account.module.ts @@ -1,11 +1,11 @@ import { NgModule } from '@angular/core' - +import { SharedModule } from '../shared' import { AccountRoutingModule } from './account-routing.module' +import { AccountChangePasswordComponent } from './account-settings/account-change-password/account-change-password.component' +import { AccountDetailsComponent } from './account-settings/account-details/account-details.component' +import { AccountSettingsComponent } from './account-settings/account-settings.component' import { AccountComponent } from './account.component' -import { AccountChangePasswordComponent } from './account-change-password' -import { AccountDetailsComponent } from './account-details' import { AccountService } from './account.service' -import { SharedModule } from '../shared' @NgModule({ imports: [ @@ -15,6 +15,7 @@ import { SharedModule } from '../shared' declarations: [ AccountComponent, + AccountSettingsComponent, AccountChangePasswordComponent, AccountDetailsComponent ], diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss index f245d0563..1baffa5c8 100644 --- a/client/src/app/app.component.scss +++ b/client/src/app/app.component.scss @@ -2,14 +2,6 @@ min-height: calc(100vh - #{$header-height} - #{$footer-height} - #{$footer-margin}); } -.main-col { - margin-left: $menu-width; - - &.expanded { - margin-left: 0; - } -} - .sub-header-container { margin-top: $header-height; } diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html index 21f8d8ba4..0ed8ec518 100644 --- a/client/src/app/menu/menu.component.html +++ b/client/src/app/menu/menu.component.html @@ -1,7 +1,7 @@
-
{{ user.username }}
+ {{ user.username }}
{{ user.email }}
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index 2c2106733..9d67ca66c 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss @@ -28,6 +28,10 @@ menu { .logged-in-username { font-size: 16px; font-weight: $font-semibold; + color: $menu-color; + cursor: pointer; + + @include disable-default-a-behaviour; } .logged-in-email { @@ -57,6 +61,12 @@ menu { width: 190px; border-radius: 3px; text-align: center; + color: $menu-color; + display: block; + cursor: pointer; + margin-bottom: 15px; + + @include disable-default-a-behaviour; &.login-button { background-color: $orange-color; @@ -82,6 +92,13 @@ menu { a { display: flex; + color: $menu-color; + cursor: pointer; + height: 22px; + line-height: 22px; + font-size: 16px; + margin-bottom: 15px; + @include disable-default-a-behaviour; .icon { width: 22px; @@ -113,16 +130,4 @@ menu { } } } - - a { - color: $menu-color; - height: 22px; - line-height: 22px; - display: block; - font-size: 16px; - cursor: pointer; - margin-bottom: 15px; - - @include disable-default-a-behaviour; - } } diff --git a/client/src/app/shared/search/search.component.scss b/client/src/app/shared/search/search.component.scss index ffd891904..191d3d597 100644 --- a/client/src/app/shared/search/search.component.scss +++ b/client/src/app/shared/search/search.component.scss @@ -1,13 +1,7 @@ #search-video { - display: inline-block; - height: $button-height; - width: $search-input-width; + @include peertube-input-text($search-input-width); margin-right: 15px; padding-right: 25px; // For the search icon - background: #fff; - border: 1px solid #C6C6C6; - border-radius: 3px; - padding-left: 15px; &::placeholder { color: #000; @@ -29,19 +23,9 @@ } .upload-button { - display: inline-block; - color: #fff; - font-weight: $font-semibold; - font-size: 15px; - height: $button-height; - line-height: $button-height; - border-radius: 3px; - text-align: center; - margin-right: 25px; - background-color: $orange-color; - padding: 0 17px 0 13px; + @include peertube-button-link; - @include disable-default-a-behaviour; + margin-right: 25px; .icon.icon-upload { display: inline-block; diff --git a/client/src/app/videos/video-list/index.ts b/client/src/app/videos/video-list/index.ts index a5a60364a..594e31984 100644 --- a/client/src/app/videos/video-list/index.ts +++ b/client/src/app/videos/video-list/index.ts @@ -1,4 +1,3 @@ -export * from './my-videos.component' export * from './video-recently-added.component' export * from './video-trending.component' export * from './shared' diff --git a/client/src/app/videos/video-list/my-videos.component.ts b/client/src/app/videos/video-list/my-videos.component.ts deleted file mode 100644 index 146db8262..000000000 --- a/client/src/app/videos/video-list/my-videos.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' -import { ActivatedRoute, Router } from '@angular/router' - -import { NotificationsService } from 'angular2-notifications' - -import { AbstractVideoList } from './shared' -import { VideoService } from '../shared' - -@Component({ - selector: 'my-videos', - styleUrls: [ './shared/abstract-video-list.scss' ], - templateUrl: './shared/abstract-video-list.html' -}) -export class MyVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { - - constructor ( - protected router: Router, - protected route: ActivatedRoute, - protected notificationsService: NotificationsService, - private videoService: VideoService - ) { - super() - } - - ngOnInit () { - super.ngOnInit() - } - - ngOnDestroy () { - super.ngOnDestroy() - } - - getVideosObservable () { - return this.videoService.getMyVideos(this.pagination, this.sort) - } -} diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.html b/client/src/app/videos/video-list/shared/abstract-video-list.html index 69e16319e..bd4f6b1f8 100644 --- a/client/src/app/videos/video-list/shared/abstract-video-list.html +++ b/client/src/app/videos/video-list/shared/abstract-video-list.html @@ -1,18 +1,19 @@ -
- {{ titlePage }} -
+
+
+ {{ titlePage }} +
-
- - + + +
diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.scss b/client/src/app/videos/video-list/shared/abstract-video-list.scss index de174802b..e69de29bb 100644 --- a/client/src/app/videos/video-list/shared/abstract-video-list.scss +++ b/client/src/app/videos/video-list/shared/abstract-video-list.scss @@ -1,23 +0,0 @@ -.videos-info { - @media screen and (max-width: 400px) { - margin-left: 0; - } - - border-bottom: 1px solid #f1f1f1; - height: 40px; - line-height: 40px; - - .videos-total-results { - font-size: 13px; - } - - my-loader { - display: inline-block; - margin-left: 5px; - } -} - -pagination { - display: block; - text-align: center; -} diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.ts b/client/src/app/videos/video-list/shared/abstract-video-list.ts index 44cdc1d9f..a684ffef4 100644 --- a/client/src/app/videos/video-list/shared/abstract-video-list.ts +++ b/client/src/app/videos/video-list/shared/abstract-video-list.ts @@ -36,7 +36,9 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { } ngOnDestroy () { - this.subActivatedRoute.unsubscribe() + if (this.subActivatedRoute) { + this.subActivatedRoute.unsubscribe() + } } onNearOfTop () { diff --git a/client/src/app/videos/videos-routing.module.ts b/client/src/app/videos/videos-routing.module.ts index 1f894df7a..204851c81 100644 --- a/client/src/app/videos/videos-routing.module.ts +++ b/client/src/app/videos/videos-routing.module.ts @@ -1,7 +1,6 @@ import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' import { MetaGuard } from '@ngx-meta/core' -import { MyVideosComponent } from './video-list' import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' import { VideoTrendingComponent } from './video-list/video-trending.component' import { VideosComponent } from './videos.component' @@ -17,15 +16,6 @@ const videosRoutes: Routes = [ pathMatch: 'full', redirectTo: 'recently-added' }, - { - path: 'mine', - component: MyVideosComponent, - data: { - meta: { - title: 'My videos' - } - } - }, { path: 'trending', component: VideoTrendingComponent, diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts index f3981d275..1d6194158 100644 --- a/client/src/app/videos/videos.module.ts +++ b/client/src/app/videos/videos.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core' import { InfiniteScrollModule } from 'ngx-infinite-scroll' import { SharedModule } from '../shared' import { VideoService } from './shared' -import { MyVideosComponent, VideoMiniatureComponent } from './video-list' +import { VideoMiniatureComponent } from './video-list' import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' import { VideoTrendingComponent } from './video-list/video-trending.component' import { VideosRoutingModule } from './videos-routing.module' @@ -20,7 +20,6 @@ import { VideosComponent } from './videos.component' VideoTrendingComponent, VideoRecentlyAddedComponent, - MyVideosComponent, VideoMiniatureComponent ], diff --git a/client/src/sass/_mixins.scss b/client/src/sass/_mixins.scss index 681657d90..5798b8f6e 100644 --- a/client/src/sass/_mixins.scss +++ b/client/src/sass/_mixins.scss @@ -4,3 +4,38 @@ outline: none !important; } } + +@mixin peertube-input-text($width) { + display: inline-block; + height: $button-height; + width: $width; + background: #fff; + border: 1px solid #C6C6C6; + border-radius: 3px; + padding-left: 15px; + + &::placeholder { + color: #585858; + } +} + +@mixin peertube-button { + border: none; + color: #fff; + font-weight: $font-semibold; + font-size: 15px; + height: $button-height; + line-height: $button-height; + border-radius: 3px; + text-align: center; + background-color: $orange-color; + padding: 0 17px 0 13px; + cursor: pointer; +} + +@mixin peertube-button-link { + display: inline-block; + + @include peertube-button; + @include disable-default-a-behaviour; +} diff --git a/client/src/sass/_variables.scss b/client/src/sass/_variables.scss index e32b37462..d05452367 100644 --- a/client/src/sass/_variables.scss +++ b/client/src/sass/_variables.scss @@ -8,6 +8,8 @@ $orange-color: #F1680D; $black-background: #000; $grey-background: #f6f2f2; +$expanded-horizontal-margins: 150px; + $button-height: 30px; $header-height: 50px; diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index fc61a22da..db63db5f5 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -33,14 +33,50 @@ input.readonly { } .main-col { - padding: 30px; + margin-left: $menu-width; .title-page { + color: #000; font-size: 16px; - font-weight: $font-bold; display: inline-block; - border-bottom: 2px solid $orange-color; - margin-bottom: 25px; + margin-right: 55px; + font-weight: $font-semibold; + @include disable-default-a-behaviour; + + &.active, &.title-page-single { + border-bottom: 2px solid $orange-color; + font-weight: $font-bold; + margin-top: 30px; + margin-bottom: 25px; + } + } + + .margin-content { + margin-left: 10px; + margin-right: 10px; + } + + .sub-menu { + background-color: #F7F7F7; + width: 100%; + height: 81px; + margin-bottom: 30px; + display: flex; + align-items: center; + } + + // Override some properties if the main content is expanded (no menu on the left) + &.expanded { + margin-left: 0; + + .margin-content { + margin-left: $expanded-horizontal-margins; + margin-right: $expanded-horizontal-margins; + } + + .sub-menu { + padding-left: $expanded-horizontal-margins; + } } } diff --git a/client/src/sass/pre-customizations.scss b/client/src/sass/pre-customizations.scss index 693489828..52eef50f2 100644 --- a/client/src/sass/pre-customizations.scss +++ b/client/src/sass/pre-customizations.scss @@ -1,4 +1,5 @@ @import '_variables.scss'; +@import '_mixins.scss'; $bootstrap-sass-asset-helper: false !default; // -- cgit v1.2.3 From 202f6b6c9dcc9b0aec4b0c1b15e455c22a7952a7 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 1 Dec 2017 18:56:26 +0100 Subject: Begin videos of an account --- client/src/app/account/account-routing.module.ts | 19 +-- .../account-videos/account-videos.component.html | 9 ++ .../account-videos/account-videos.component.scss | 0 .../account-videos/account-videos.component.ts | 35 ++++ client/src/app/account/account.module.ts | 4 +- client/src/app/shared/shared.module.ts | 12 +- .../src/app/shared/video/abstract-video-list.html | 19 +++ .../src/app/shared/video/abstract-video-list.scss | 0 client/src/app/shared/video/abstract-video-list.ts | 121 ++++++++++++++ client/src/app/shared/video/sort-field.type.ts | 5 + client/src/app/shared/video/video-details.model.ts | 84 ++++++++++ client/src/app/shared/video/video-edit.model.ts | 50 ++++++ .../src/app/shared/video/video-pagination.model.ts | 5 + .../shared/video/video-thumbnail.component.html | 10 ++ .../shared/video/video-thumbnail.component.scss | 28 ++++ .../app/shared/video/video-thumbnail.component.ts | 12 ++ client/src/app/shared/video/video.model.ts | 90 +++++++++++ client/src/app/shared/video/video.service.ts | 170 ++++++++++++++++++++ .../videos/+video-edit/shared/video-edit.module.ts | 3 +- .../app/videos/+video-edit/video-add.component.ts | 22 ++- .../videos/+video-edit/video-update.component.ts | 19 ++- .../+video-watch/video-download.component.ts | 4 +- .../videos/+video-watch/video-report.component.ts | 8 +- .../videos/+video-watch/video-share.component.ts | 4 +- .../videos/+video-watch/video-watch.component.ts | 4 +- .../app/videos/+video-watch/video-watch.module.ts | 5 +- client/src/app/videos/shared/index.ts | 6 - client/src/app/videos/shared/sort-field.type.ts | 5 - .../src/app/videos/shared/video-details.model.ts | 84 ---------- client/src/app/videos/shared/video-edit.model.ts | 50 ------ .../app/videos/shared/video-pagination.model.ts | 5 - client/src/app/videos/shared/video.model.ts | 90 ----------- client/src/app/videos/shared/video.service.ts | 176 --------------------- .../video-list/shared/abstract-video-list.html | 19 --- .../video-list/shared/abstract-video-list.scss | 0 .../video-list/shared/abstract-video-list.ts | 121 -------------- client/src/app/videos/video-list/shared/index.ts | 1 - .../shared/video-miniature.component.html | 11 +- .../shared/video-miniature.component.scss | 29 ---- .../video-list/shared/video-miniature.component.ts | 4 +- .../video-list/video-recently-added.component.ts | 8 +- .../videos/video-list/video-trending.component.ts | 8 +- client/src/app/videos/videos.module.ts | 9 +- 43 files changed, 704 insertions(+), 664 deletions(-) create mode 100644 client/src/app/account/account-videos/account-videos.component.html create mode 100644 client/src/app/account/account-videos/account-videos.component.scss create mode 100644 client/src/app/account/account-videos/account-videos.component.ts create mode 100644 client/src/app/shared/video/abstract-video-list.html create mode 100644 client/src/app/shared/video/abstract-video-list.scss create mode 100644 client/src/app/shared/video/abstract-video-list.ts create mode 100644 client/src/app/shared/video/sort-field.type.ts create mode 100644 client/src/app/shared/video/video-details.model.ts create mode 100644 client/src/app/shared/video/video-edit.model.ts create mode 100644 client/src/app/shared/video/video-pagination.model.ts create mode 100644 client/src/app/shared/video/video-thumbnail.component.html create mode 100644 client/src/app/shared/video/video-thumbnail.component.scss create mode 100644 client/src/app/shared/video/video-thumbnail.component.ts create mode 100644 client/src/app/shared/video/video.model.ts create mode 100644 client/src/app/shared/video/video.service.ts delete mode 100644 client/src/app/videos/shared/sort-field.type.ts delete mode 100644 client/src/app/videos/shared/video-details.model.ts delete mode 100644 client/src/app/videos/shared/video-edit.model.ts delete mode 100644 client/src/app/videos/shared/video-pagination.model.ts delete mode 100644 client/src/app/videos/shared/video.model.ts delete mode 100644 client/src/app/videos/shared/video.service.ts delete mode 100644 client/src/app/videos/video-list/shared/abstract-video-list.html delete mode 100644 client/src/app/videos/video-list/shared/abstract-video-list.scss delete mode 100644 client/src/app/videos/video-list/shared/abstract-video-list.ts (limited to 'client') diff --git a/client/src/app/account/account-routing.module.ts b/client/src/app/account/account-routing.module.ts index 2e9de1cfb..070b9b5c5 100644 --- a/client/src/app/account/account-routing.module.ts +++ b/client/src/app/account/account-routing.module.ts @@ -6,6 +6,7 @@ import { MetaGuard } from '@ngx-meta/core' import { LoginGuard } from '../core' import { AccountComponent } from './account.component' import { AccountSettingsComponent } from './account-settings/account-settings.component' +import { AccountVideosComponent } from './account-videos/account-videos.component' const accountRoutes: Routes = [ { @@ -22,15 +23,15 @@ const accountRoutes: Routes = [ } } }, - // { - // path: 'videos', - // component: AccountVideosComponent, - // data: { - // meta: { - // title: 'Account videos' - // } - // } - // } + { + path: 'videos', + component: AccountVideosComponent, + data: { + meta: { + title: 'Account videos' + } + } + } ] } ] diff --git a/client/src/app/account/account-videos/account-videos.component.html b/client/src/app/account/account-videos/account-videos.component.html new file mode 100644 index 000000000..6c8ac4508 --- /dev/null +++ b/client/src/app/account/account-videos/account-videos.component.html @@ -0,0 +1,9 @@ +
+
+ +
+
diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/account/account-videos/account-videos.component.ts b/client/src/app/account/account-videos/account-videos.component.ts new file mode 100644 index 000000000..ff945825d --- /dev/null +++ b/client/src/app/account/account-videos/account-videos.component.ts @@ -0,0 +1,35 @@ +import { Component, OnDestroy, OnInit } from '@angular/core' +import { AbstractVideoList } from '../../shared/video/abstract-video-list' +import { ActivatedRoute } from '@angular/router' +import { Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { VideoService } from '../../shared/video/video.service' + +@Component({ + selector: 'my-account-videos', + templateUrl: './account-videos.component.html', + styleUrls: [ './account-videos.component.scss' ] +}) +export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { + titlePage = 'My videos' + currentRoute = '/account/videos' + + constructor (protected router: Router, + protected route: ActivatedRoute, + protected notificationsService: NotificationsService, + private videoService: VideoService) { + super() + } + + ngOnInit () { + super.ngOnInit() + } + + ngOnDestroy () { + super.ngOnDestroy() + } + + getVideosObservable () { + return this.videoService.getMyVideos(this.pagination, this.sort) + } +} diff --git a/client/src/app/account/account.module.ts b/client/src/app/account/account.module.ts index ff444ddeb..020199e23 100644 --- a/client/src/app/account/account.module.ts +++ b/client/src/app/account/account.module.ts @@ -6,6 +6,7 @@ import { AccountDetailsComponent } from './account-settings/account-details/acco import { AccountSettingsComponent } from './account-settings/account-settings.component' import { AccountComponent } from './account.component' import { AccountService } from './account.service' +import { AccountVideosComponent } from './account-videos/account-videos.component' @NgModule({ imports: [ @@ -17,7 +18,8 @@ import { AccountService } from './account.service' AccountComponent, AccountSettingsComponent, AccountChangePasswordComponent, - AccountDetailsComponent + AccountDetailsComponent, + AccountVideosComponent ], exports: [ diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 7618748e9..e76f7636a 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -20,6 +20,9 @@ import { SearchComponent, SearchService } from './search' import { UserService } from './users' import { VideoAbuseService } from './video-abuse' import { VideoBlacklistService } from './video-blacklist' +import { VideoThumbnailComponent } from './video/video-thumbnail.component' +import { VideoService } from './video/video.service' +import { InfiniteScrollModule } from 'ngx-infinite-scroll' @NgModule({ imports: [ @@ -34,7 +37,8 @@ import { VideoBlacklistService } from './video-blacklist' ProgressbarModule.forRoot(), DataTableModule, - PrimeSharedModule + PrimeSharedModule, + InfiniteScrollModule ], declarations: [ @@ -42,6 +46,7 @@ import { VideoBlacklistService } from './video-blacklist' KeysPipe, SearchComponent, LoaderComponent, + VideoThumbnailComponent, NumberFormatterPipe, FromNowPipe ], @@ -58,11 +63,13 @@ import { VideoBlacklistService } from './video-blacklist' ProgressbarModule, DataTableModule, PrimeSharedModule, + InfiniteScrollModule, BytesPipe, KeysPipe, SearchComponent, LoaderComponent, + VideoThumbnailComponent, NumberFormatterPipe, FromNowPipe @@ -75,7 +82,8 @@ import { VideoBlacklistService } from './video-blacklist' SearchService, VideoAbuseService, VideoBlacklistService, - UserService + UserService, + VideoService ] }) export class SharedModule { } diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html new file mode 100644 index 000000000..bd4f6b1f8 --- /dev/null +++ b/client/src/app/shared/video/abstract-video-list.html @@ -0,0 +1,19 @@ +
+
+ {{ titlePage }} +
+ +
+ + +
+
diff --git a/client/src/app/shared/video/abstract-video-list.scss b/client/src/app/shared/video/abstract-video-list.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts new file mode 100644 index 000000000..cf717cf4c --- /dev/null +++ b/client/src/app/shared/video/abstract-video-list.ts @@ -0,0 +1,121 @@ +import { OnDestroy, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { Observable } from 'rxjs/Observable' +import { Subscription } from 'rxjs/Subscription' +import { SortField } from './sort-field.type' +import { VideoPagination } from './video-pagination.model' +import { Video } from './video.model' + +export abstract class AbstractVideoList implements OnInit, OnDestroy { + pagination: VideoPagination = { + currentPage: 1, + itemsPerPage: 25, + totalItems: null + } + sort: SortField = '-createdAt' + videos: Video[] = [] + + protected notificationsService: NotificationsService + protected router: Router + protected route: ActivatedRoute + protected subActivatedRoute: Subscription + + protected abstract currentRoute: string + + abstract titlePage: string + private loadedPages: { [ id: number ]: boolean } = {} + + abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}> + + ngOnInit () { + // Subscribe to route changes + const routeParams = this.route.snapshot.params + this.loadRouteParams(routeParams) + this.loadMoreVideos('after') + } + + ngOnDestroy () { + if (this.subActivatedRoute) { + this.subActivatedRoute.unsubscribe() + } + } + + onNearOfTop () { + if (this.pagination.currentPage > 1) { + this.previousPage() + } + } + + onNearOfBottom () { + if (this.hasMoreVideos()) { + this.nextPage() + } + } + + loadMoreVideos (where: 'before' | 'after') { + if (this.loadedPages[this.pagination.currentPage] === true) return + + const observable = this.getVideosObservable() + + observable.subscribe( + ({ videos, totalVideos }) => { + this.loadedPages[this.pagination.currentPage] = true + this.pagination.totalItems = totalVideos + + if (where === 'before') { + this.videos = videos.concat(this.videos) + } else { + this.videos = this.videos.concat(videos) + } + }, + error => this.notificationsService.error('Error', error.text) + ) + } + + protected hasMoreVideos () { + if (!this.pagination.totalItems) return true + + const maxPage = this.pagination.totalItems / this.pagination.itemsPerPage + return maxPage > this.pagination.currentPage + } + + protected previousPage () { + this.pagination.currentPage-- + + this.setNewRouteParams() + this.loadMoreVideos('before') + } + + protected nextPage () { + this.pagination.currentPage++ + + this.setNewRouteParams() + this.loadMoreVideos('after') + } + + protected buildRouteParams () { + // There is always a sort and a current page + const params = { + sort: this.sort, + page: this.pagination.currentPage + } + + return params + } + + protected loadRouteParams (routeParams: { [ key: string ]: any }) { + this.sort = routeParams['sort'] as SortField || '-createdAt' + + if (routeParams['page'] !== undefined) { + this.pagination.currentPage = parseInt(routeParams['page'], 10) + } else { + this.pagination.currentPage = 1 + } + } + + protected setNewRouteParams () { + const routeParams = this.buildRouteParams() + this.router.navigate([ this.currentRoute, routeParams ]) + } +} diff --git a/client/src/app/shared/video/sort-field.type.ts b/client/src/app/shared/video/sort-field.type.ts new file mode 100644 index 000000000..776f360f8 --- /dev/null +++ b/client/src/app/shared/video/sort-field.type.ts @@ -0,0 +1,5 @@ +export type SortField = 'name' | '-name' + | 'duration' | '-duration' + | 'createdAt' | '-createdAt' + | 'views' | '-views' + | 'likes' | '-likes' diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts new file mode 100644 index 000000000..93c380b73 --- /dev/null +++ b/client/src/app/shared/video/video-details.model.ts @@ -0,0 +1,84 @@ +import { Video } from '../../shared/video/video.model' +import { AuthUser } from '../../core' +import { + VideoDetails as VideoDetailsServerModel, + VideoFile, + VideoChannel, + VideoResolution, + UserRight, + VideoPrivacy +} from '../../../../../shared' + +export class VideoDetails extends Video implements VideoDetailsServerModel { + account: string + by: string + createdAt: Date + updatedAt: Date + categoryLabel: string + category: number + licenceLabel: string + licence: number + languageLabel: string + language: number + description: string + duration: number + durationLabel: string + id: number + uuid: string + isLocal: boolean + name: string + serverHost: string + tags: string[] + thumbnailPath: string + thumbnailUrl: string + previewPath: string + previewUrl: string + embedPath: string + embedUrl: string + views: number + likes: number + dislikes: number + nsfw: boolean + descriptionPath: string + files: VideoFile[] + channel: VideoChannel + privacy: VideoPrivacy + privacyLabel: string + + constructor (hash: VideoDetailsServerModel) { + super(hash) + + this.privacy = hash.privacy + this.privacyLabel = hash.privacyLabel + this.descriptionPath = hash.descriptionPath + this.files = hash.files + this.channel = hash.channel + } + + getAppropriateMagnetUri (actualDownloadSpeed = 0) { + if (this.files === undefined || this.files.length === 0) return '' + if (this.files.length === 1) return this.files[0].magnetUri + + // Find first video that is good for our download speed (remember they are sorted) + let betterResolutionFile = this.files.find(f => actualDownloadSpeed > (f.size / this.duration)) + + // If the download speed is too bad, return the lowest resolution we have + if (betterResolutionFile === undefined) { + betterResolutionFile = this.files.find(f => f.resolution === VideoResolution.H_240P) + } + + return betterResolutionFile.magnetUri + } + + isRemovableBy (user: AuthUser) { + return user && this.isLocal === true && (this.account === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO)) + } + + isBlackistableBy (user: AuthUser) { + return user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true && this.isLocal === false + } + + isUpdatableBy (user: AuthUser) { + return user && this.isLocal === true && user.username === this.account + } +} diff --git a/client/src/app/shared/video/video-edit.model.ts b/client/src/app/shared/video/video-edit.model.ts new file mode 100644 index 000000000..88d23a59f --- /dev/null +++ b/client/src/app/shared/video/video-edit.model.ts @@ -0,0 +1,50 @@ +import { VideoDetails } from './video-details.model' +import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' + +export class VideoEdit { + category: number + licence: number + language: number + description: string + name: string + tags: string[] + nsfw: boolean + channel: number + privacy: VideoPrivacy + uuid?: string + id?: number + + constructor (videoDetails: VideoDetails) { + this.id = videoDetails.id + this.uuid = videoDetails.uuid + this.category = videoDetails.category + this.licence = videoDetails.licence + this.language = videoDetails.language + this.description = videoDetails.description + this.name = videoDetails.name + this.tags = videoDetails.tags + this.nsfw = videoDetails.nsfw + this.channel = videoDetails.channel.id + this.privacy = videoDetails.privacy + } + + patch (values: Object) { + Object.keys(values).forEach((key) => { + this[key] = values[key] + }) + } + + toJSON () { + return { + category: this.category, + licence: this.licence, + language: this.language, + description: this.description, + name: this.name, + tags: this.tags, + nsfw: this.nsfw, + channel: this.channel, + privacy: this.privacy + } + } +} diff --git a/client/src/app/shared/video/video-pagination.model.ts b/client/src/app/shared/video/video-pagination.model.ts new file mode 100644 index 000000000..9e71769cb --- /dev/null +++ b/client/src/app/shared/video/video-pagination.model.ts @@ -0,0 +1,5 @@ +export interface VideoPagination { + currentPage: number + itemsPerPage: number + totalItems: number +} diff --git a/client/src/app/shared/video/video-thumbnail.component.html b/client/src/app/shared/video/video-thumbnail.component.html new file mode 100644 index 000000000..5c698e8f6 --- /dev/null +++ b/client/src/app/shared/video/video-thumbnail.component.html @@ -0,0 +1,10 @@ + +video thumbnail + +
+ {{ video.durationLabel }} +
+
diff --git a/client/src/app/shared/video/video-thumbnail.component.scss b/client/src/app/shared/video/video-thumbnail.component.scss new file mode 100644 index 000000000..ab4f9bcb1 --- /dev/null +++ b/client/src/app/shared/video/video-thumbnail.component.scss @@ -0,0 +1,28 @@ +.video-thumbnail { + display: inline-block; + position: relative; + border-radius: 4px; + overflow: hidden; + + &:hover { + text-decoration: none !important; + } + + img.blur-filter { + filter: blur(5px); + transform : scale(1.03); + } + + .video-thumbnail-overlay { + position: absolute; + right: 5px; + bottom: 5px; + display: inline-block; + background-color: rgba(0, 0, 0, 0.7); + color: #fff; + font-size: 12px; + font-weight: $font-bold; + border-radius: 3px; + padding: 0 5px; + } +} diff --git a/client/src/app/shared/video/video-thumbnail.component.ts b/client/src/app/shared/video/video-thumbnail.component.ts new file mode 100644 index 000000000..e543e9903 --- /dev/null +++ b/client/src/app/shared/video/video-thumbnail.component.ts @@ -0,0 +1,12 @@ +import { Component, Input } from '@angular/core' +import { Video } from './video.model' + +@Component({ + selector: 'my-video-thumbnail', + styleUrls: [ './video-thumbnail.component.scss' ], + templateUrl: './video-thumbnail.component.html' +}) +export class VideoThumbnailComponent { + @Input() video: Video + @Input() nsfw = false +} diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts new file mode 100644 index 000000000..6929c8755 --- /dev/null +++ b/client/src/app/shared/video/video.model.ts @@ -0,0 +1,90 @@ +import { Video as VideoServerModel } from '../../../../../shared' +import { User } from '../' + +export class Video implements VideoServerModel { + account: string + by: string + createdAt: Date + updatedAt: Date + categoryLabel: string + category: number + licenceLabel: string + licence: number + languageLabel: string + language: number + description: string + duration: number + durationLabel: string + id: number + uuid: string + isLocal: boolean + name: string + serverHost: string + tags: string[] + thumbnailPath: string + thumbnailUrl: string + previewPath: string + previewUrl: string + embedPath: string + embedUrl: string + views: number + likes: number + dislikes: number + nsfw: boolean + + private static createByString (account: string, serverHost: string) { + return account + '@' + serverHost + } + + private static createDurationString (duration: number) { + const minutes = Math.floor(duration / 60) + const seconds = duration % 60 + const minutesPadding = minutes >= 10 ? '' : '0' + const secondsPadding = seconds >= 10 ? '' : '0' + + return minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString() + } + + constructor (hash: VideoServerModel) { + let absoluteAPIUrl = API_URL + if (!absoluteAPIUrl) { + // The API is on the same domain + absoluteAPIUrl = window.location.origin + } + + this.account = hash.account + this.createdAt = new Date(hash.createdAt.toString()) + this.categoryLabel = hash.categoryLabel + this.category = hash.category + this.licenceLabel = hash.licenceLabel + this.licence = hash.licence + this.languageLabel = hash.languageLabel + this.language = hash.language + this.description = hash.description + this.duration = hash.duration + this.durationLabel = Video.createDurationString(hash.duration) + this.id = hash.id + this.uuid = hash.uuid + this.isLocal = hash.isLocal + this.name = hash.name + this.serverHost = hash.serverHost + this.tags = hash.tags + this.thumbnailPath = hash.thumbnailPath + this.thumbnailUrl = absoluteAPIUrl + hash.thumbnailPath + this.previewPath = hash.previewPath + this.previewUrl = absoluteAPIUrl + hash.previewPath + this.embedPath = hash.embedPath + this.embedUrl = absoluteAPIUrl + hash.embedPath + this.views = hash.views + this.likes = hash.likes + this.dislikes = hash.dislikes + this.nsfw = hash.nsfw + + this.by = Video.createByString(hash.account, hash.serverHost) + } + + isVideoNSFWForUser (user: User) { + // If the video is NSFW and the user is not logged in, or the user does not want to display NSFW videos... + return (this.nsfw && (!user || user.displayNSFW === false)) + } +} diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts new file mode 100644 index 000000000..b2a26417c --- /dev/null +++ b/client/src/app/shared/video/video.service.ts @@ -0,0 +1,170 @@ +import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' +import { Injectable } from '@angular/core' +import 'rxjs/add/operator/catch' +import 'rxjs/add/operator/map' +import { Observable } from 'rxjs/Observable' +import { Video as VideoServerModel, VideoDetails as VideoDetailsServerModel } from '../../../../../shared' +import { ResultList } from '../../../../../shared/models/result-list.model' +import { UserVideoRateUpdate } from '../../../../../shared/models/videos/user-video-rate-update.model' +import { UserVideoRate } from '../../../../../shared/models/videos/user-video-rate.model' +import { VideoRateType } from '../../../../../shared/models/videos/video-rate.type' +import { VideoUpdate } from '../../../../../shared/models/videos/video-update.model' +import { RestExtractor } from '../rest/rest-extractor.service' +import { RestService } from '../rest/rest.service' +import { Search } from '../search/search.model' +import { UserService } from '../users/user.service' +import { SortField } from './sort-field.type' +import { VideoDetails } from './video-details.model' +import { VideoEdit } from './video-edit.model' +import { VideoPagination } from './video-pagination.model' +import { Video } from './video.model' + +@Injectable() +export class VideoService { + private static BASE_VIDEO_URL = API_URL + '/api/v1/videos/' + + constructor ( + private authHttp: HttpClient, + private restExtractor: RestExtractor, + private restService: RestService + ) {} + + getVideo (uuid: string): Observable { + return this.authHttp.get(VideoService.BASE_VIDEO_URL + uuid) + .map(videoHash => new VideoDetails(videoHash)) + .catch((res) => this.restExtractor.handleError(res)) + } + + viewVideo (uuid: string): Observable { + return this.authHttp.post(VideoService.BASE_VIDEO_URL + uuid + '/views', {}) + .map(this.restExtractor.extractDataBool) + .catch(this.restExtractor.handleError) + } + + updateVideo (video: VideoEdit) { + const language = video.language ? video.language : null + + const body: VideoUpdate = { + name: video.name, + category: video.category, + licence: video.licence, + language, + description: video.description, + privacy: video.privacy, + tags: video.tags, + nsfw: video.nsfw + } + + return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, body) + .map(this.restExtractor.extractDataBool) + .catch(this.restExtractor.handleError) + } + + uploadVideo (video: FormData) { + const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true }) + + return this.authHttp + .request(req) + .catch(this.restExtractor.handleError) + } + + getMyVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { + const pagination = this.videoPaginationToRestPagination(videoPagination) + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination, sort) + + return this.authHttp.get(UserService.BASE_USERS_URL + '/me/videos', { params }) + .map(this.extractVideos) + .catch((res) => this.restExtractor.handleError(res)) + } + + getVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { + const pagination = this.videoPaginationToRestPagination(videoPagination) + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination, sort) + + return this.authHttp + .get(VideoService.BASE_VIDEO_URL, { params }) + .map(this.extractVideos) + .catch((res) => this.restExtractor.handleError(res)) + } + + searchVideos (search: Search, videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { + const url = VideoService.BASE_VIDEO_URL + 'search/' + encodeURIComponent(search.value) + + const pagination = this.videoPaginationToRestPagination(videoPagination) + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination, sort) + + if (search.field) params.set('field', search.field) + + return this.authHttp + .get>(url, { params }) + .map(this.extractVideos) + .catch((res) => this.restExtractor.handleError(res)) + } + + removeVideo (id: number) { + return this.authHttp + .delete(VideoService.BASE_VIDEO_URL + id) + .map(this.restExtractor.extractDataBool) + .catch((res) => this.restExtractor.handleError(res)) + } + + loadCompleteDescription (descriptionPath: string) { + return this.authHttp + .get(API_URL + descriptionPath) + .map(res => res['description']) + .catch((res) => this.restExtractor.handleError(res)) + } + + setVideoLike (id: number) { + return this.setVideoRate(id, 'like') + } + + setVideoDislike (id: number) { + return this.setVideoRate(id, 'dislike') + } + + getUserVideoRating (id: number): Observable { + const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating' + + return this.authHttp + .get(url) + .catch(res => this.restExtractor.handleError(res)) + } + + private videoPaginationToRestPagination (videoPagination: VideoPagination) { + const start: number = (videoPagination.currentPage - 1) * videoPagination.itemsPerPage + const count: number = videoPagination.itemsPerPage + + return { start, count } + } + + private setVideoRate (id: number, rateType: VideoRateType) { + const url = VideoService.BASE_VIDEO_URL + id + '/rate' + const body: UserVideoRateUpdate = { + rating: rateType + } + + return this.authHttp + .put(url, body) + .map(this.restExtractor.extractDataBool) + .catch(res => this.restExtractor.handleError(res)) + } + + private extractVideos (result: ResultList) { + const videosJson = result.data + const totalVideos = result.total + const videos = [] + + for (const videoJson of videosJson) { + videos.push(new Video(videoJson)) + } + + return { videos, totalVideos } + } +} diff --git a/client/src/app/videos/+video-edit/shared/video-edit.module.ts b/client/src/app/videos/+video-edit/shared/video-edit.module.ts index c64cea920..cdab694f9 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.module.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.module.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core' import { TagInputModule } from 'ngx-chips' import { TabsModule } from 'ngx-bootstrap/tabs' -import { VideoService, MarkdownService, VideoDescriptionComponent } from '../../shared' +import { MarkdownService, VideoDescriptionComponent } from '../../shared' import { SharedModule } from '../../../shared' @NgModule({ @@ -26,7 +26,6 @@ import { SharedModule } from '../../../shared' ], providers: [ - VideoService, MarkdownService ] }) diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts index 76bfbb515..989addbd7 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts @@ -1,25 +1,23 @@ +import { HttpEventType, HttpResponse } from '@angular/common/http' import { Component, OnInit, ViewChild } from '@angular/core' import { FormBuilder, FormGroup } from '@angular/forms' import { Router } from '@angular/router' - import { NotificationsService } from 'angular2-notifications' - +import { VideoService } from 'app/shared/video/video.service' +import { VideoCreate } from '../../../../../shared' +import { AuthService, ServerService } from '../../core' import { FormReactive, - VIDEO_NAME, VIDEO_CATEGORY, - VIDEO_LICENCE, - VIDEO_LANGUAGE, - VIDEO_DESCRIPTION, - VIDEO_TAGS, VIDEO_CHANNEL, + VIDEO_DESCRIPTION, VIDEO_FILE, - VIDEO_PRIVACY + VIDEO_LANGUAGE, + VIDEO_LICENCE, + VIDEO_NAME, + VIDEO_PRIVACY, + VIDEO_TAGS } from '../../shared' -import { AuthService, ServerService } from '../../core' -import { VideoService } from '../shared' -import { VideoCreate } from '../../../../../shared' -import { HttpEventType, HttpResponse } from '@angular/common/http' @Component({ selector: 'my-videos-add', diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts index 0e966cb50..017781866 100644 --- a/client/src/app/videos/+video-edit/video-update.component.ts +++ b/client/src/app/videos/+video-edit/video-update.component.ts @@ -1,23 +1,22 @@ import { Component, OnInit } from '@angular/core' import { FormBuilder, FormGroup } from '@angular/forms' import { ActivatedRoute, Router } from '@angular/router' -import 'rxjs/add/observable/forkJoin' - import { NotificationsService } from 'angular2-notifications' - +import 'rxjs/add/observable/forkJoin' +import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' import { ServerService } from '../../core' import { FormReactive, - VIDEO_NAME, VIDEO_CATEGORY, - VIDEO_LICENCE, - VIDEO_LANGUAGE, VIDEO_DESCRIPTION, - VIDEO_TAGS, - VIDEO_PRIVACY + VIDEO_LANGUAGE, + VIDEO_LICENCE, + VIDEO_NAME, + VIDEO_PRIVACY, + VIDEO_TAGS } from '../../shared' -import { VideoEdit, VideoService } from '../shared' -import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' +import { VideoService } from '../../shared/video/video.service' +import { VideoEdit } from '../../shared/video/video-edit.model' @Component({ selector: 'my-videos-update', diff --git a/client/src/app/videos/+video-watch/video-download.component.ts b/client/src/app/videos/+video-watch/video-download.component.ts index c32f8d586..010a246cd 100644 --- a/client/src/app/videos/+video-watch/video-download.component.ts +++ b/client/src/app/videos/+video-watch/video-download.component.ts @@ -1,8 +1,6 @@ import { Component, Input, ViewChild } from '@angular/core' - import { ModalDirective } from 'ngx-bootstrap/modal' - -import { VideoDetails } from '../shared' +import { VideoDetails } from '../../shared/video/video-details.model' @Component({ selector: 'my-video-download', diff --git a/client/src/app/videos/+video-watch/video-report.component.ts b/client/src/app/videos/+video-watch/video-report.component.ts index fc9b5a9d4..b94e4144e 100644 --- a/client/src/app/videos/+video-watch/video-report.component.ts +++ b/client/src/app/videos/+video-watch/video-report.component.ts @@ -1,11 +1,9 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core' import { FormBuilder, FormGroup } from '@angular/forms' - -import { ModalDirective } from 'ngx-bootstrap/modal' import { NotificationsService } from 'angular2-notifications' - -import { FormReactive, VideoAbuseService, VIDEO_ABUSE_REASON } from '../../shared' -import { VideoDetails, VideoService } from '../shared' +import { ModalDirective } from 'ngx-bootstrap/modal' +import { FormReactive, VIDEO_ABUSE_REASON, VideoAbuseService } from '../../shared' +import { VideoDetails } from '../../shared/video/video-details.model' @Component({ selector: 'my-video-report', diff --git a/client/src/app/videos/+video-watch/video-share.component.ts b/client/src/app/videos/+video-watch/video-share.component.ts index aeef65ecf..4df9adf29 100644 --- a/client/src/app/videos/+video-watch/video-share.component.ts +++ b/client/src/app/videos/+video-watch/video-share.component.ts @@ -1,8 +1,6 @@ import { Component, Input, ViewChild } from '@angular/core' - import { ModalDirective } from 'ngx-bootstrap/modal' - -import { VideoDetails } from '../shared' +import { VideoDetails } from '../../shared/video/video-details.model' @Component({ selector: 'my-video-share', 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 b26f3092f..eac676be8 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -2,6 +2,7 @@ import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/co import { ActivatedRoute, Router } from '@angular/router' import { MetaService } from '@ngx-meta/core' import { NotificationsService } from 'angular2-notifications' +import { VideoService } from 'app/shared/video/video.service' import { Observable } from 'rxjs/Observable' import { Subscription } from 'rxjs/Subscription' import videojs from 'video.js' @@ -9,10 +10,11 @@ import { UserVideoRateType, VideoRateType } from '../../../../../shared' import '../../../assets/player/peertube-videojs-plugin' import { AuthService, ConfirmService } from '../../core' import { VideoBlacklistService } from '../../shared' -import { MarkdownService, VideoDetails, VideoService } from '../shared' +import { MarkdownService } from '../shared' import { VideoDownloadComponent } from './video-download.component' import { VideoReportComponent } from './video-report.component' import { VideoShareComponent } from './video-share.component' +import { VideoDetails } from '../../shared/video/video-details.model' @Component({ selector: 'my-video-watch', diff --git a/client/src/app/videos/+video-watch/video-watch.module.ts b/client/src/app/videos/+video-watch/video-watch.module.ts index 1b983200d..0b1dd5c15 100644 --- a/client/src/app/videos/+video-watch/video-watch.module.ts +++ b/client/src/app/videos/+video-watch/video-watch.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core' import { VideoWatchRoutingModule } from './video-watch-routing.module' -import { VideoService, MarkdownService } from '../shared' +import { MarkdownService } from '../shared' import { SharedModule } from '../../shared' import { VideoWatchComponent } from './video-watch.component' @@ -28,8 +28,7 @@ import { VideoDownloadComponent } from './video-download.component' ], providers: [ - MarkdownService, - VideoService + MarkdownService ] }) export class VideoWatchModule { } diff --git a/client/src/app/videos/shared/index.ts b/client/src/app/videos/shared/index.ts index 3f1458088..3c72ed895 100644 --- a/client/src/app/videos/shared/index.ts +++ b/client/src/app/videos/shared/index.ts @@ -1,8 +1,2 @@ -export * from './sort-field.type' export * from './markdown.service' -export * from './video.model' -export * from './video-details.model' -export * from './video-edit.model' -export * from './video.service' export * from './video-description.component' -export * from './video-pagination.model' diff --git a/client/src/app/videos/shared/sort-field.type.ts b/client/src/app/videos/shared/sort-field.type.ts deleted file mode 100644 index 776f360f8..000000000 --- a/client/src/app/videos/shared/sort-field.type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type SortField = 'name' | '-name' - | 'duration' | '-duration' - | 'createdAt' | '-createdAt' - | 'views' | '-views' - | 'likes' | '-likes' diff --git a/client/src/app/videos/shared/video-details.model.ts b/client/src/app/videos/shared/video-details.model.ts deleted file mode 100644 index 64cb4f847..000000000 --- a/client/src/app/videos/shared/video-details.model.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Video } from './video.model' -import { AuthUser } from '../../core' -import { - VideoDetails as VideoDetailsServerModel, - VideoFile, - VideoChannel, - VideoResolution, - UserRight, - VideoPrivacy -} from '../../../../../shared' - -export class VideoDetails extends Video implements VideoDetailsServerModel { - account: string - by: string - createdAt: Date - updatedAt: Date - categoryLabel: string - category: number - licenceLabel: string - licence: number - languageLabel: string - language: number - description: string - duration: number - durationLabel: string - id: number - uuid: string - isLocal: boolean - name: string - serverHost: string - tags: string[] - thumbnailPath: string - thumbnailUrl: string - previewPath: string - previewUrl: string - embedPath: string - embedUrl: string - views: number - likes: number - dislikes: number - nsfw: boolean - descriptionPath: string - files: VideoFile[] - channel: VideoChannel - privacy: VideoPrivacy - privacyLabel: string - - constructor (hash: VideoDetailsServerModel) { - super(hash) - - this.privacy = hash.privacy - this.privacyLabel = hash.privacyLabel - this.descriptionPath = hash.descriptionPath - this.files = hash.files - this.channel = hash.channel - } - - getAppropriateMagnetUri (actualDownloadSpeed = 0) { - if (this.files === undefined || this.files.length === 0) return '' - if (this.files.length === 1) return this.files[0].magnetUri - - // Find first video that is good for our download speed (remember they are sorted) - let betterResolutionFile = this.files.find(f => actualDownloadSpeed > (f.size / this.duration)) - - // If the download speed is too bad, return the lowest resolution we have - if (betterResolutionFile === undefined) { - betterResolutionFile = this.files.find(f => f.resolution === VideoResolution.H_240P) - } - - return betterResolutionFile.magnetUri - } - - isRemovableBy (user: AuthUser) { - return user && this.isLocal === true && (this.account === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO)) - } - - isBlackistableBy (user: AuthUser) { - return user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true && this.isLocal === false - } - - isUpdatableBy (user: AuthUser) { - return user && this.isLocal === true && user.username === this.account - } -} diff --git a/client/src/app/videos/shared/video-edit.model.ts b/client/src/app/videos/shared/video-edit.model.ts deleted file mode 100644 index 88d23a59f..000000000 --- a/client/src/app/videos/shared/video-edit.model.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { VideoDetails } from './video-details.model' -import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' - -export class VideoEdit { - category: number - licence: number - language: number - description: string - name: string - tags: string[] - nsfw: boolean - channel: number - privacy: VideoPrivacy - uuid?: string - id?: number - - constructor (videoDetails: VideoDetails) { - this.id = videoDetails.id - this.uuid = videoDetails.uuid - this.category = videoDetails.category - this.licence = videoDetails.licence - this.language = videoDetails.language - this.description = videoDetails.description - this.name = videoDetails.name - this.tags = videoDetails.tags - this.nsfw = videoDetails.nsfw - this.channel = videoDetails.channel.id - this.privacy = videoDetails.privacy - } - - patch (values: Object) { - Object.keys(values).forEach((key) => { - this[key] = values[key] - }) - } - - toJSON () { - return { - category: this.category, - licence: this.licence, - language: this.language, - description: this.description, - name: this.name, - tags: this.tags, - nsfw: this.nsfw, - channel: this.channel, - privacy: this.privacy - } - } -} diff --git a/client/src/app/videos/shared/video-pagination.model.ts b/client/src/app/videos/shared/video-pagination.model.ts deleted file mode 100644 index 9e71769cb..000000000 --- a/client/src/app/videos/shared/video-pagination.model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface VideoPagination { - currentPage: number - itemsPerPage: number - totalItems: number -} diff --git a/client/src/app/videos/shared/video.model.ts b/client/src/app/videos/shared/video.model.ts deleted file mode 100644 index 0dd41d71b..000000000 --- a/client/src/app/videos/shared/video.model.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Video as VideoServerModel } from '../../../../../shared' -import { User } from '../../shared' - -export class Video implements VideoServerModel { - account: string - by: string - createdAt: Date - updatedAt: Date - categoryLabel: string - category: number - licenceLabel: string - licence: number - languageLabel: string - language: number - description: string - duration: number - durationLabel: string - id: number - uuid: string - isLocal: boolean - name: string - serverHost: string - tags: string[] - thumbnailPath: string - thumbnailUrl: string - previewPath: string - previewUrl: string - embedPath: string - embedUrl: string - views: number - likes: number - dislikes: number - nsfw: boolean - - private static createByString (account: string, serverHost: string) { - return account + '@' + serverHost - } - - private static createDurationString (duration: number) { - const minutes = Math.floor(duration / 60) - const seconds = duration % 60 - const minutesPadding = minutes >= 10 ? '' : '0' - const secondsPadding = seconds >= 10 ? '' : '0' - - return minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString() - } - - constructor (hash: VideoServerModel) { - let absoluteAPIUrl = API_URL - if (!absoluteAPIUrl) { - // The API is on the same domain - absoluteAPIUrl = window.location.origin - } - - this.account = hash.account - this.createdAt = new Date(hash.createdAt.toString()) - this.categoryLabel = hash.categoryLabel - this.category = hash.category - this.licenceLabel = hash.licenceLabel - this.licence = hash.licence - this.languageLabel = hash.languageLabel - this.language = hash.language - this.description = hash.description - this.duration = hash.duration - this.durationLabel = Video.createDurationString(hash.duration) - this.id = hash.id - this.uuid = hash.uuid - this.isLocal = hash.isLocal - this.name = hash.name - this.serverHost = hash.serverHost - this.tags = hash.tags - this.thumbnailPath = hash.thumbnailPath - this.thumbnailUrl = absoluteAPIUrl + hash.thumbnailPath - this.previewPath = hash.previewPath - this.previewUrl = absoluteAPIUrl + hash.previewPath - this.embedPath = hash.embedPath - this.embedUrl = absoluteAPIUrl + hash.embedPath - this.views = hash.views - this.likes = hash.likes - this.dislikes = hash.dislikes - this.nsfw = hash.nsfw - - this.by = Video.createByString(hash.account, hash.serverHost) - } - - isVideoNSFWForUser (user: User) { - // If the video is NSFW and the user is not logged in, or the user does not want to display NSFW videos... - return (this.nsfw && (!user || user.displayNSFW === false)) - } -} diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts deleted file mode 100644 index 5d25a26d4..000000000 --- a/client/src/app/videos/shared/video.service.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { Injectable } from '@angular/core' -import { Observable } from 'rxjs/Observable' -import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' -import 'rxjs/add/operator/catch' -import 'rxjs/add/operator/map' - -import { SortField } from './sort-field.type' -import { - RestExtractor, - RestService, - UserService, - Search -} from '../../shared' -import { Video } from './video.model' -import { VideoDetails } from './video-details.model' -import { VideoEdit } from './video-edit.model' -import { VideoPagination } from './video-pagination.model' -import { - UserVideoRate, - VideoRateType, - VideoUpdate, - UserVideoRateUpdate, - Video as VideoServerModel, - VideoDetails as VideoDetailsServerModel, - ResultList -} from '../../../../../shared' - -@Injectable() -export class VideoService { - private static BASE_VIDEO_URL = API_URL + '/api/v1/videos/' - - constructor ( - private authHttp: HttpClient, - private restExtractor: RestExtractor, - private restService: RestService - ) {} - - getVideo (uuid: string): Observable { - return this.authHttp.get(VideoService.BASE_VIDEO_URL + uuid) - .map(videoHash => new VideoDetails(videoHash)) - .catch((res) => this.restExtractor.handleError(res)) - } - - viewVideo (uuid: string): Observable { - return this.authHttp.post(VideoService.BASE_VIDEO_URL + uuid + '/views', {}) - .map(this.restExtractor.extractDataBool) - .catch(this.restExtractor.handleError) - } - - updateVideo (video: VideoEdit) { - const language = video.language ? video.language : null - - const body: VideoUpdate = { - name: video.name, - category: video.category, - licence: video.licence, - language, - description: video.description, - privacy: video.privacy, - tags: video.tags, - nsfw: video.nsfw - } - - return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, body) - .map(this.restExtractor.extractDataBool) - .catch(this.restExtractor.handleError) - } - - uploadVideo (video: FormData) { - const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true }) - - return this.authHttp - .request(req) - .catch(this.restExtractor.handleError) - } - - getMyVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { - const pagination = this.videoPaginationToRestPagination(videoPagination) - - let params = new HttpParams() - params = this.restService.addRestGetParams(params, pagination, sort) - - return this.authHttp.get(UserService.BASE_USERS_URL + '/me/videos', { params }) - .map(this.extractVideos) - .catch((res) => this.restExtractor.handleError(res)) - } - - getVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { - const pagination = this.videoPaginationToRestPagination(videoPagination) - - let params = new HttpParams() - params = this.restService.addRestGetParams(params, pagination, sort) - - return this.authHttp - .get(VideoService.BASE_VIDEO_URL, { params }) - .map(this.extractVideos) - .catch((res) => this.restExtractor.handleError(res)) - } - - searchVideos (search: Search, videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { - const url = VideoService.BASE_VIDEO_URL + 'search/' + encodeURIComponent(search.value) - - const pagination = this.videoPaginationToRestPagination(videoPagination) - - let params = new HttpParams() - params = this.restService.addRestGetParams(params, pagination, sort) - - if (search.field) params.set('field', search.field) - - return this.authHttp - .get>(url, { params }) - .map(this.extractVideos) - .catch((res) => this.restExtractor.handleError(res)) - } - - removeVideo (id: number) { - return this.authHttp - .delete(VideoService.BASE_VIDEO_URL + id) - .map(this.restExtractor.extractDataBool) - .catch((res) => this.restExtractor.handleError(res)) - } - - loadCompleteDescription (descriptionPath: string) { - return this.authHttp - .get(API_URL + descriptionPath) - .map(res => res['description']) - .catch((res) => this.restExtractor.handleError(res)) - } - - setVideoLike (id: number) { - return this.setVideoRate(id, 'like') - } - - setVideoDislike (id: number) { - return this.setVideoRate(id, 'dislike') - } - - getUserVideoRating (id: number): Observable { - const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating' - - return this.authHttp - .get(url) - .catch(res => this.restExtractor.handleError(res)) - } - - private videoPaginationToRestPagination (videoPagination: VideoPagination) { - const start: number = (videoPagination.currentPage - 1) * videoPagination.itemsPerPage - const count: number = videoPagination.itemsPerPage - - return { start, count } - } - - private setVideoRate (id: number, rateType: VideoRateType) { - const url = VideoService.BASE_VIDEO_URL + id + '/rate' - const body: UserVideoRateUpdate = { - rating: rateType - } - - return this.authHttp - .put(url, body) - .map(this.restExtractor.extractDataBool) - .catch(res => this.restExtractor.handleError(res)) - } - - private extractVideos (result: ResultList) { - const videosJson = result.data - const totalVideos = result.total - const videos = [] - - for (const videoJson of videosJson) { - videos.push(new Video(videoJson)) - } - - return { videos, totalVideos } - } -} diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.html b/client/src/app/videos/video-list/shared/abstract-video-list.html deleted file mode 100644 index bd4f6b1f8..000000000 --- a/client/src/app/videos/video-list/shared/abstract-video-list.html +++ /dev/null @@ -1,19 +0,0 @@ -
-
- {{ titlePage }} -
- -
- - -
-
diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.scss b/client/src/app/videos/video-list/shared/abstract-video-list.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.ts b/client/src/app/videos/video-list/shared/abstract-video-list.ts deleted file mode 100644 index a684ffef4..000000000 --- a/client/src/app/videos/video-list/shared/abstract-video-list.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { OnDestroy, OnInit } from '@angular/core' -import { ActivatedRoute, Router } from '@angular/router' - -import { NotificationsService } from 'angular2-notifications' -import { Observable } from 'rxjs/Observable' -import { Subscription } from 'rxjs/Subscription' - -import { SortField, Video, VideoPagination } from '../../shared' - -export abstract class AbstractVideoList implements OnInit, OnDestroy { - pagination: VideoPagination = { - currentPage: 1, - itemsPerPage: 25, - totalItems: null - } - sort: SortField = '-createdAt' - videos: Video[] = [] - - protected notificationsService: NotificationsService - protected router: Router - protected route: ActivatedRoute - protected subActivatedRoute: Subscription - - protected abstract currentRoute: string - - abstract titlePage: string - private loadedPages: { [ id: number ]: boolean } = {} - - abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}> - - ngOnInit () { - // Subscribe to route changes - const routeParams = this.route.snapshot.params - this.loadRouteParams(routeParams) - this.loadMoreVideos('after') - } - - ngOnDestroy () { - if (this.subActivatedRoute) { - this.subActivatedRoute.unsubscribe() - } - } - - onNearOfTop () { - if (this.pagination.currentPage > 1) { - this.previousPage() - } - } - - onNearOfBottom () { - if (this.hasMoreVideos()) { - this.nextPage() - } - } - - loadMoreVideos (where: 'before' | 'after') { - if (this.loadedPages[this.pagination.currentPage] === true) return - - const observable = this.getVideosObservable() - - observable.subscribe( - ({ videos, totalVideos }) => { - this.loadedPages[this.pagination.currentPage] = true - this.pagination.totalItems = totalVideos - - if (where === 'before') { - this.videos = videos.concat(this.videos) - } else { - this.videos = this.videos.concat(videos) - } - }, - error => this.notificationsService.error('Error', error.text) - ) - } - - protected hasMoreVideos () { - if (!this.pagination.totalItems) return true - - const maxPage = this.pagination.totalItems/this.pagination.itemsPerPage - return maxPage > this.pagination.currentPage - } - - protected previousPage () { - this.pagination.currentPage-- - - this.setNewRouteParams() - this.loadMoreVideos('before') - } - - protected nextPage () { - this.pagination.currentPage++ - - this.setNewRouteParams() - this.loadMoreVideos('after') - } - - protected buildRouteParams () { - // There is always a sort and a current page - const params = { - sort: this.sort, - page: this.pagination.currentPage - } - - return params - } - - protected loadRouteParams (routeParams: { [ key: string ]: any }) { - this.sort = routeParams['sort'] as SortField || '-createdAt' - - if (routeParams['page'] !== undefined) { - this.pagination.currentPage = parseInt(routeParams['page'], 10) - } else { - this.pagination.currentPage = 1 - } - } - - protected setNewRouteParams () { - const routeParams = this.buildRouteParams() - this.router.navigate([ this.currentRoute, routeParams ]) - } -} diff --git a/client/src/app/videos/video-list/shared/index.ts b/client/src/app/videos/video-list/shared/index.ts index 170ca4832..2778f2d9e 100644 --- a/client/src/app/videos/video-list/shared/index.ts +++ b/client/src/app/videos/video-list/shared/index.ts @@ -1,2 +1 @@ -export * from './abstract-video-list' export * from './video-miniature.component' diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.html b/client/src/app/videos/video-list/shared/video-miniature.component.html index aea85b6c6..f2756ca3d 100644 --- a/client/src/app/videos/video-list/shared/video-miniature.component.html +++ b/client/src/app/videos/video-list/shared/video-miniature.component.html @@ -1,14 +1,5 @@
- - video thumbnail - -
- {{ video.durationLabel }} -
-
+
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.scss b/client/src/app/videos/video-list/shared/video-miniature.component.scss index ed15864d9..658d7af9d 100644 --- a/client/src/app/videos/video-list/shared/video-miniature.component.scss +++ b/client/src/app/videos/video-list/shared/video-miniature.component.scss @@ -5,35 +5,6 @@ height: 175px; vertical-align: top; - .video-miniature-thumbnail { - display: inline-block; - position: relative; - border-radius: 4px; - overflow: hidden; - - &:hover { - text-decoration: none !important; - } - - img.blur-filter { - filter: blur(5px); - transform : scale(1.03); - } - - .video-miniature-thumbnail-overlay { - position: absolute; - right: 5px; - bottom: 5px; - display: inline-block; - background-color: rgba(0, 0, 0, 0.7); - color: #fff; - font-size: 12px; - font-weight: $font-bold; - border-radius: 3px; - padding: 0 5px; - } - } - .video-miniature-information { width: 200px; margin-top: 2px; diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.ts b/client/src/app/videos/video-list/shared/video-miniature.component.ts index e5a87907b..e8fc8e911 100644 --- a/client/src/app/videos/video-list/shared/video-miniature.component.ts +++ b/client/src/app/videos/video-list/shared/video-miniature.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core' - -import { SortField, Video } from '../../shared' import { User } from '../../../shared' +import { SortField } from '../../../shared/video/sort-field.type' +import { Video } from '../../../shared/video/video.model' @Component({ selector: 'my-video-miniature', diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts index 9bf69bd78..d48804414 100644 --- a/client/src/app/videos/video-list/video-recently-added.component.ts +++ b/client/src/app/videos/video-list/video-recently-added.component.ts @@ -1,13 +1,13 @@ import { Component, OnDestroy, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from '../shared' -import { AbstractVideoList } from './shared' +import { VideoService } from '../../shared/video/video.service' +import { AbstractVideoList } from '../../shared/video/abstract-video-list' @Component({ selector: 'my-videos-recently-added', - styleUrls: [ './shared/abstract-video-list.scss' ], - templateUrl: './shared/abstract-video-list.html' + styleUrls: [ '../../shared/video/abstract-video-list.scss' ], + templateUrl: '../../shared/video/abstract-video-list.html' }) export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { titlePage = 'Recently added' diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts index a1df68711..9108289c9 100644 --- a/client/src/app/videos/video-list/video-trending.component.ts +++ b/client/src/app/videos/video-list/video-trending.component.ts @@ -1,13 +1,13 @@ import { Component, OnDestroy, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from '../shared' -import { AbstractVideoList } from './shared' +import { VideoService } from '../../shared/video/video.service' +import { AbstractVideoList } from 'app/shared/video/abstract-video-list' @Component({ selector: 'my-videos-trending', - styleUrls: [ './shared/abstract-video-list.scss' ], - templateUrl: './shared/abstract-video-list.html' + styleUrls: [ '../../shared/video/abstract-video-list.scss' ], + templateUrl: '../../shared/video/abstract-video-list.html' }) export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { titlePage = 'Trending' diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts index 1d6194158..6d846fd3b 100644 --- a/client/src/app/videos/videos.module.ts +++ b/client/src/app/videos/videos.module.ts @@ -1,7 +1,5 @@ import { NgModule } from '@angular/core' -import { InfiniteScrollModule } from 'ngx-infinite-scroll' import { SharedModule } from '../shared' -import { VideoService } from './shared' import { VideoMiniatureComponent } from './video-list' import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' import { VideoTrendingComponent } from './video-list/video-trending.component' @@ -11,8 +9,7 @@ import { VideosComponent } from './videos.component' @NgModule({ imports: [ VideosRoutingModule, - SharedModule, - InfiniteScrollModule + SharedModule ], declarations: [ @@ -27,8 +24,6 @@ import { VideosComponent } from './videos.component' VideosComponent ], - providers: [ - VideoService - ] + providers: [] }) export class VideosModule { } -- cgit v1.2.3 From 2295ce6c4e7ba805cc100ff961527bebc2cd89e5 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 4 Dec 2017 10:34:40 +0100 Subject: Add account avatar --- .../account-settings.component.html | 10 ++++-- .../account-settings.component.scss | 21 ++++++++++-- .../account-settings/account-settings.component.ts | 4 +++ client/src/app/core/auth/auth.service.ts | 36 ++++++++------------- client/src/app/menu/menu.component.html | 2 ++ client/src/app/menu/menu.component.scss | 8 ++++- client/src/app/menu/menu.component.ts | 4 +++ client/src/app/shared/users/user.model.ts | 25 ++++++-------- client/src/assets/default-avatar.png | Bin 0 -> 1674 bytes client/src/sass/_mixins.scss | 5 +++ 10 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 client/src/assets/default-avatar.png (limited to 'client') diff --git a/client/src/app/account/account-settings/account-settings.component.html b/client/src/app/account/account-settings/account-settings.component.html index 2509eb5aa..9e9f688d2 100644 --- a/client/src/app/account/account-settings/account-settings.component.html +++ b/client/src/app/account/account-settings/account-settings.component.html @@ -1,7 +1,13 @@ - -- cgit v1.2.3 From 62e23e40dad8fbd6148a9201fba2e73ced3f1877 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 4 Dec 2017 11:40:45 +0100 Subject: Fix client build --- client/src/app/shared/shared.module.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'client') diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index e76f7636a..f7ced040d 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -7,7 +7,8 @@ import { RouterModule } from '@angular/router' import { BsDropdownModule } from 'ngx-bootstrap/dropdown' import { ModalModule } from 'ngx-bootstrap/modal' import { ProgressbarModule } from 'ngx-bootstrap/progressbar' -import { BytesPipe, KeysPipe } from 'ngx-pipes' +import { InfiniteScrollModule } from 'ngx-infinite-scroll' +import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' import { DataTableModule } from 'primeng/components/datatable/datatable' @@ -22,7 +23,6 @@ import { VideoAbuseService } from './video-abuse' import { VideoBlacklistService } from './video-blacklist' import { VideoThumbnailComponent } from './video/video-thumbnail.component' import { VideoService } from './video/video.service' -import { InfiniteScrollModule } from 'ngx-infinite-scroll' @NgModule({ imports: [ @@ -38,12 +38,11 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll' DataTableModule, PrimeSharedModule, - InfiniteScrollModule + InfiniteScrollModule, + NgPipesModule ], declarations: [ - BytesPipe, - KeysPipe, SearchComponent, LoaderComponent, VideoThumbnailComponent, -- cgit v1.2.3 From be6a4802326b1748e85c0d6fdadf06e70e6ecbb0 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 5 Dec 2017 10:44:11 +0100 Subject: Design video player --- .../videos/+video-watch/video-watch.component.html | 11 +- .../videos/+video-watch/video-watch.component.scss | 18 +- .../videos/+video-watch/video-watch.component.ts | 11 +- client/src/assets/player/images/arrow-down.svg | 14 + client/src/assets/player/images/arrow-up.svg | 14 + client/src/assets/player/images/fullscreen.svg | 18 + client/src/assets/player/images/volume-mute.svg | 16 + client/src/assets/player/images/volume.svg | 13 + .../src/assets/player/peertube-videojs-plugin.ts | 98 +++- client/src/sass/video-js-custom.scss | 559 ++++++++++----------- 10 files changed, 445 insertions(+), 327 deletions(-) create mode 100644 client/src/assets/player/images/arrow-down.svg create mode 100644 client/src/assets/player/images/arrow-up.svg create mode 100644 client/src/assets/player/images/fullscreen.svg create mode 100644 client/src/assets/player/images/volume-mute.svg create mode 100644 client/src/assets/player/images/volume.svg (limited to 'client') diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index f528d73c3..aa1f2f77e 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -15,20 +15,13 @@
-
- +
+
Video not found :'(
- -
-
Download: {{ downloadSpeed | bytes }}/s
-
Upload: {{ uploadSpeed | bytes }}/s
-
Number of peers: {{ numPeers }}
-
-
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index cad21dd18..06c2de7c6 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -1,6 +1,12 @@ #video-container { - width: 100%; - height: 100%; + background-color: #000; + display: flex; + justify-content: center; + + #video-element { + width: 888px; + height: 500px; + } } #video-not-found { @@ -11,14 +17,6 @@ font-weight: bold; } -.embed-responsive { - height: 500px; - - @media screen and (max-width: 600px) { - height: 300px; - } -} - #torrent-info { font-size: 10px; margin-top: 10px; 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 eac676be8..48842602e 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -26,13 +26,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { @ViewChild('videoShareModal') videoShareModal: VideoShareComponent @ViewChild('videoReportModal') videoReportModal: VideoReportComponent - downloadSpeed: number error = false loading = false - numPeers: number player: videojs.Player playerElement: HTMLMediaElement - uploadSpeed: number userRating: UserVideoRateType = null video: VideoDetails = null videoPlayerLoaded = false @@ -283,7 +280,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { return this.router.navigate([ '/videos/list' ]) } - this.playerElement = this.elementRef.nativeElement.querySelector('#video-container') + this.playerElement = this.elementRef.nativeElement.querySelector('#video-element') const videojsOptions = { controls: true, @@ -306,12 +303,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.on('customError', (event, data) => { self.handleError(data.err) }) - - this.on('torrentInfo', (event, data) => { - self.downloadSpeed = data.downloadSpeed - self.numPeers = data.numPeers - self.uploadSpeed = data.uploadSpeed - }) }) this.setVideoDescriptionHTML() diff --git a/client/src/assets/player/images/arrow-down.svg b/client/src/assets/player/images/arrow-down.svg new file mode 100644 index 000000000..3377cdab2 --- /dev/null +++ b/client/src/assets/player/images/arrow-down.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/assets/player/images/arrow-up.svg b/client/src/assets/player/images/arrow-up.svg new file mode 100644 index 000000000..b1a7890a8 --- /dev/null +++ b/client/src/assets/player/images/arrow-up.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/assets/player/images/fullscreen.svg b/client/src/assets/player/images/fullscreen.svg new file mode 100644 index 000000000..44e0041a4 --- /dev/null +++ b/client/src/assets/player/images/fullscreen.svg @@ -0,0 +1,18 @@ + + + + fullscreen + Created with Sketch. + + + + + + + + + + + + + diff --git a/client/src/assets/player/images/volume-mute.svg b/client/src/assets/player/images/volume-mute.svg new file mode 100644 index 000000000..0c7c296bc --- /dev/null +++ b/client/src/assets/player/images/volume-mute.svg @@ -0,0 +1,16 @@ + + + + volume-mute + Created with Sketch. + + + + + + + + + + + diff --git a/client/src/assets/player/images/volume.svg b/client/src/assets/player/images/volume.svg new file mode 100644 index 000000000..590913add --- /dev/null +++ b/client/src/assets/player/images/volume.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts index c54d8b5ea..977455bff 100644 --- a/client/src/assets/player/peertube-videojs-plugin.ts +++ b/client/src/assets/player/peertube-videojs-plugin.ts @@ -2,9 +2,24 @@ import videojs, { Player } from 'video.js' import * as WebTorrent from 'webtorrent' +import { VideoFile } from '../../../../shared' import { renderVideo } from './video-renderer' -import { VideoFile } from '../../../../shared' + +// https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts +// Don't import all Angular stuff, just copy the code with shame +const dictionaryBytes: Array<{max: number, type: string}> = [ + { max: 1024, type: 'B' }, + { max: 1048576, type: 'KB' }, + { max: 1073741824, type: 'MB' }, + { max: 1.0995116e12, type: 'GB' } +] +function bytes (value) { + const format = dictionaryBytes.find(d => value < d.max) || dictionaryBytes[dictionaryBytes.length - 1] + const calc = Math.floor(value / (format.max / 1024)).toString() + + return [ calc, format.type ] +} // videojs typings don't have some method we need const videojsUntyped = videojs as any @@ -62,6 +77,7 @@ const ResolutionMenuButton = videojsUntyped.extend(MenuButton, { update: function () { this.label.innerHTML = this.player_.getCurrentResolutionLabel() + this.hide() return MenuButton.prototype.update.call(this) }, @@ -74,8 +90,7 @@ MenuButton.registerComponent('ResolutionMenuButton', ResolutionMenuButton) const Button = videojsUntyped.getComponent('Button') const PeertubeLinkButton = videojsUntyped.extend(Button, { constructor: function (player) { - Button.apply(this, arguments) - this.player = player + Button.call(this, player) }, createEl: function () { @@ -90,11 +105,78 @@ const PeertubeLinkButton = videojsUntyped.extend(Button, { }, handleClick: function () { - this.player.pause() + this.player_.pause() } }) Button.registerComponent('PeerTubeLinkButton', PeertubeLinkButton) +const WebTorrentButton = videojsUntyped.extend(Button, { + constructor: function (player) { + Button.call(this, player) + }, + + createEl: function () { + const div = document.createElement('div') + + const downloadIcon = document.createElement('span') + downloadIcon.classList.add('icon', 'icon-download') + div.appendChild(downloadIcon) + + const downloadSpeedText = document.createElement('span') + downloadSpeedText.classList.add('download-speed-text') + const downloadSpeedNumber = document.createElement('span') + downloadSpeedNumber.classList.add('download-speed-number') + const downloadSpeedUnit = document.createElement('span') + downloadSpeedText.appendChild(downloadSpeedNumber) + downloadSpeedText.appendChild(downloadSpeedUnit) + div.appendChild(downloadSpeedText) + + const uploadIcon = document.createElement('span') + uploadIcon.classList.add('icon', 'icon-upload') + div.appendChild(uploadIcon) + + const uploadSpeedText = document.createElement('span') + uploadSpeedText.classList.add('upload-speed-text') + const uploadSpeedNumber = document.createElement('span') + uploadSpeedNumber.classList.add('upload-speed-number') + const uploadSpeedUnit = document.createElement('span') + uploadSpeedText.appendChild(uploadSpeedNumber) + uploadSpeedText.appendChild(uploadSpeedUnit) + div.appendChild(uploadSpeedText) + + const peersText = document.createElement('span') + peersText.textContent = ' peers' + peersText.classList.add('peers-text') + const peersNumber = document.createElement('span') + peersNumber.classList.add('peers-number') + div.appendChild(peersNumber) + div.appendChild(peersText) + + div.className = 'vjs-webtorrent' + // Hide the stats before we get the info + div.style.display = 'none' + + this.player_.on('torrentInfo', (event, data) => { + const downloadSpeed = bytes(data.downloadSpeed) + const uploadSpeed = bytes(data.uploadSpeed) + const numPeers = data.numPeers + + downloadSpeedNumber.textContent = downloadSpeed[0] + downloadSpeedUnit.textContent = ' ' + downloadSpeed[1] + + uploadSpeedNumber.textContent = uploadSpeed[0] + uploadSpeedUnit.textContent = ' ' + uploadSpeed[1] + + peersNumber.textContent = numPeers + + div.style.display = 'block' + }) + + return div + } +}) +Button.registerComponent('WebTorrentButton', WebTorrentButton) + type PeertubePluginOptions = { videoFiles: VideoFile[] playerElement: HTMLVideoElement @@ -223,6 +305,12 @@ const peertubePlugin = function (options: PeertubePluginOptions) { } } + const webTorrentButton = new WebTorrentButton(player) + controlBar.webTorrent = controlBar.el().insertBefore(webTorrentButton.el(), controlBar.progressControl.el()) + controlBar.webTorrent.dispose = function () { + this.parentNode.removeChild(this) + } + if (options.autoplay === true) { player.updateVideoFile() } else { @@ -245,7 +333,7 @@ const peertubePlugin = function (options: PeertubePluginOptions) { }, 1000) }) - function handleError (err: Error|string) { + function handleError (err: Error | string) { return player.trigger('customError', { err }) } } diff --git a/client/src/sass/video-js-custom.scss b/client/src/sass/video-js-custom.scss index 34a958764..1200c07a5 100644 --- a/client/src/sass/video-js-custom.scss +++ b/client/src/sass/video-js-custom.scss @@ -1,341 +1,314 @@ -// Thanks: https://github.com/kmoskwiak/videojs-resolution-switcher/pull/92/files -.vjs-resolution-button-label { - font-size: 1em; - line-height: 3em; - position: absolute; - top: 0; - left: -1px; - width: 100%; - height: 100%; - text-align: center; - box-sizing: inherit; -} - -.vjs-resolution-button { - outline: 0 !important; +// Thanks: https://github.com/zanechua/videojs-sublime-inspired-skin +$primary-foreground-color: #fff; +$primary-background-color: #000; +$font-size: 13px; +$control-bar-height: 34px; - .vjs-menu { - .vjs-menu-content { - width: 4em; - left: 50%; /* Center the menu, in it's parent */ - margin-left: -2em; /* half of width, to center */ - } +.video-js.vjs-peertube-skin { + font-size: $font-size; + color: $primary-foreground-color; - li { - text-transform: none; - font-size: 1em; - } + .vjs-button > .vjs-icon-placeholder::before { + line-height: $control-bar-height; } -} - -// Thanks: https://github.com/zanechua/videojs-sublime-inspired-skin -// Video JS Sublime Skin -// The following are SCSS variables to automate some of the values. -// But don't feel limited by them. Change/replace whatever you want. + .vjs-mouse-display:before, + .vjs-play-progress:before, + .vjs-volume-level:before { + content: ''; /* Remove Circle From Progress Bar */ + } -// The color of icons, text, and the big play button border. -// Try changing to #0f0 -$primary-foreground-color: #fff; // #fff default + .vjs-audio-button { + display: none; + } -// The default color of control backgrounds is mostly black but with a little -// bit of blue so it can still be seen on all-black video frames, which are common. -// Try changing to #900 -$primary-background-color: #2B333F; // #2B333F default + .vjs-big-play-button { + font-size: 8em; -// Try changing to true -$center-big-play-button: true; // true default + $big-play-width: 3em; + $big-play-height: 1.5em; -.video-js { - /* The base font size controls the size of everything, not just text. - All dimensions use em-based sizes so that the scale along with the font size. - Try increasing it to 15px and see what happens. */ - font-size: 10px; + line-height: $big-play-height; + height: $big-play-height; + width: $big-play-width; - /* The main font color changes the ICON COLORS as well as the text */ - color: $primary-foreground-color; -} + border: 0; + border-radius: 0.3em; -/* The "Big Play Button" is the play button that shows before the video plays. - To center it set the align values to center and middle. The typical location - of the button is the center, but there is trend towards moving it to a corner - where it gets out of the way of valuable content in the poster image.*/ -.vjs-sublime-skin .vjs-big-play-button { - /* The font size is what makes the big play button...big. - All width/height values use ems, which are a multiple of the font size. - If the .video-js font-size is 10px, then 3em equals 30px.*/ - font-size: 8em; - - /* We're using SCSS vars here because the values are used in multiple places. - Now that font size is set, the following em values will be a multiple of the - new font size. If the font-size is 3em (30px), then setting any of - the following values to 3em would equal 30px. 3 * font-size. */ - $big-play-width: 3em; - /* 1.5em = 45px default */ - $big-play-height: 1.5em; - - line-height: $big-play-height; - height: $big-play-height; - width: $big-play-width; - - /* 0.06666em = 2px default */ - border: 0; - /* 0.3em = 9px default */ - border-radius: 0.3em; - - @if $center-big-play-button { - /* Align center */ left: 50%; top: 50%; margin-left: -($big-play-width / 2); margin-top: -($big-play-height / 2); - } @else { - /* Align top left. 0.5em = 15px default */ - left: 0.5em; - top: 0.5em; } -} - -/* The default color of control backgrounds is mostly black but with a little - bit of blue so it can still be seen on all-black video frames, which are common. */ -.video-js .vjs-control-bar, -.video-js .vjs-big-play-button, -.video-js .vjs-menu-button .vjs-menu-content { - /* IE8 - has no alpha support */ - background-color: $primary-background-color; - /* Opacity: 1.0 = 100%, 0.0 = 0% */ - background-color: rgba($primary-background-color, 0.7); - background-color: transparent; -} - -// Make a slightly lighter version of the main background -// for the slider background. -$slider-bg-color: lighten($primary-background-color, 33%); - -/* Slider - used for Volume bar and Progress bar */ -.video-js .vjs-slider { - background-color: $slider-bg-color; - background-color: rgba($slider-bg-color, 0.5); - background-color: rgba(255,255,255,.3); - border-radius: 2px; - height: 6.5px; -} - -/* The slider bar color is used for the progress bar and the volume bar - (the first two can be removed after a fix that's coming) */ -.video-js .vjs-volume-level, -.video-js .vjs-play-progress, -.video-js .vjs-slider-bar { - background: $primary-foreground-color; -} - -/* Enlarged Slider to enable easier tracking. Adjust all the height:6.5px to preferred height for the slider if necessary. */ -.video-js .vjs-progress-holder .vjs-load-progress, -.video-js .vjs-progress-holder .vjs-load-progress div, -.video-js .vjs-progress-holder .vjs-play-progress, -.video-js .vjs-progress-holder .vjs-tooltip-progress-bar { - height: 6.5px; -} - -/* The main progress bar also has a bar that shows how much has been loaded. */ -.video-js .vjs-load-progress { - /* For IE8 we'll lighten the color */ - background: ligthen($slider-bg-color, 25%); - /* Otherwise we'll rely on stacked opacities */ - background: rgba($slider-bg-color, 0.5); -} - -/* The load progress bar also has internal divs that represent - smaller disconnected loaded time ranges */ -.video-js .vjs-load-progress div { - /* For IE8 we'll lighten the color */ - background: ligthen($slider-bg-color, 50%); - /* Otherwise we'll rely on stacked opacities */ - background: rgba($slider-bg-color, 0.75); -} - -//Skin Style Starts -.vjs-sublime-skin .vjs-poster { - outline: none; /* Remove Blue Outline on Click*/ - outline: 0; -} -.vjs-sublime-skin:hover .vjs-big-play-button { + &:hover .vjs-big-play-button { background-color: transparent; -} - -.vjs-sublime-skin .vjs-fullscreen-control:before, -.vjs-sublime-skin.vjs-fullscreen .vjs-fullscreen-control:before { - content: ''; /* Remove Fullscreen Exit Icon */ -} - -.vjs-sublime-skin.vjs-fullscreen .vjs-fullscreen-control { - background: #fff; -} - -.vjs-sublime-skin .vjs-fullscreen-control { - border: 3px solid #fff; - box-sizing: border-box; - cursor: pointer; - margin-top: -7px; - top: 50%; - height: 14px; - width: 22px; - margin-right: 10px; -} + } -.vjs-sublime-skin.vjs-fullscreen .vjs-fullscreen-control:after { - background: #000; - content: ""; - display: block; - position: absolute; - bottom: 0; - left: 0; - height: 5px; - width: 5px; -} + .vjs-control-bar, + .vjs-big-play-button, + .vjs-menu-button .vjs-menu-content { + background-color: rgba($primary-background-color, 0.5); + } -.vjs-sublime-skin .vjs-progress-holder { - margin: 0; -} + $slider-bg-color: lighten($primary-background-color, 33%); -.vjs-sublime-skin .vjs-progress-control .vjs-progress-holder:after { + .vjs-slider { + background-color: rgba(255, 255, 255, .3); border-radius: 2px; - display: block; - height: 6.5px; -} - -.vjs-sublime-skin .vjs-progress-control .vjs-load-progres, -.vjs-sublime-skin .vjs-progress-control .vjs-play-progress { - border-radius: 2px; - height: 6.5px; -} + height: 5px; + } -.vjs-sublime-skin .vjs-playback-rate { - display: none; /* Remove Playback Rate */ -} + /* The slider bar color is used for the progress bar and the volume bar + (the first two can be removed after a fix that's coming) */ + .vjs-volume-level, + .vjs-play-progress, + .vjs-slider-bar { + background: $primary-foreground-color; + } -.vjs-sublime-skin .vjs-progress-control { - margin-right: 50px; -} + .vjs-load-progress { + background: rgba($slider-bg-color, 0.5); + } -.vjs-sublime-skin .vjs-time-control { - right: 55px; -} + .vjs-load-progress div { + background: rgba($slider-bg-color, 0.75); + } -.vjs-sublime-skin .vjs-volume-menu-button:before { - width: 1.2em; - z-index: 1; -} + .vjs-poster { + outline: none; /* Remove Blue Outline on Click*/ + outline: 0; + } -.vjs-sublime-skin .vjs-volume-menu-button .vjs-menu, -.vjs-sublime-skin .vjs-volume-menu-button:focus .vjs-menu, -.vjs-sublime-skin .vjs-volume-menu-button.vjs-slider-active .vjs-menu { - display: block; - opacity: 1; -} + .vjs-control-bar { + height: $control-bar-height; -.vjs-sublime-skin .vjs-volume-menu-button, -.vjs-sublime-skin .vjs-volume-panel { - width: 6em; - position: absolute; - right: 0; - margin-right: 65px; -} + .vjs-progress-control { + bottom: 34px; + width: 100%; + position: absolute; + height: 5px; -.vjs-sublime-skin .vjs-volume-menu-button .vjs-menu-content, -.vjs-sublime-skin .vjs-volume-menu-button:hover, -.vjs-sublime-skin .vjs-volume-menu-button:focus, -.vjs-sublime-skin .vjs-volume-menu-button.vjs-slider-active, -.vjs-sublime-skin .vjs-volume-panel .vjs-volume-control, -.vjs-sublime-skin .vjs-volume-panel:hover, -.vjs-sublime-skin .vjs-volume-panel:focus, -.vjs-sublime-skin .vjs-volume-panel.vjs-slider-active { - width: 6em; -} + .vjs-progress-holder { + margin: 0; + border-radius: 0; + } + } -.vjs-sublime-skin .vjs-volume-menu-button .vjs-menu { - left: 23px; -} + .vjs-play-control { + font-size: $font-size; + padding: 0 17px; + margin-right: 5px; + } -.vjs-sublime-skin .vjs-mouse-display:before, -.vjs-sublime-skin .vjs-play-progress:before, -.vjs-sublime-skin .vjs-volume-level:before { - content: ''; /* Remove Circle From Progress Bar */ -} + .vjs-time-control { + &.vjs-current-time { + font-size: $font-size; + display: inline-block; + padding: 0; + + .vjs-current-time-display { + line-height: $control-bar-height; + + &::after { + content: "/"; + margin: 0 1px 0 2px; + } + } + } + + &.vjs-duration { + font-size: $font-size; + display: inline-block; + padding: 0; + + .vjs-duration-display { + line-height: $control-bar-height; + } + } + + &.vjs-remaining-time { + display: none; + } + } -.vjs-sublime-skin .vjs-mouse-display:after, -.vjs-sublime-skin .vjs-play-progress:after, -.vjs-sublime-skin .vjs-time-tooltip { - width: 5.5em; -} + .vjs-webtorrent { + width: 100%; + line-height: $control-bar-height; + text-align: right; + padding-right: 60px; + + .download-speed-number, .upload-speed-number, .peers-number { + font-weight: $font-semibold; + } + + .download-speed-text, .upload-speed-text, .peers-text { + margin-right: 15px; + } + + .icon { + display: inline-block; + width: 15px; + height: 15px; + background-size: contain; + vertical-align: middle; + background-repeat: no-repeat; + margin-right: 6px; + position: relative; + top: -1px; + + &.icon-download { + background-image: url('../assets/player/images/arrow-down.svg'); + } + + &.icon-upload { + background-image: url('../assets/player/images/arrow-up.svg'); + } + } + } -.vjs-sublime-skin .vjs-audio-button { - display: none; -} + .vjs-mute-control { + .vjs-icon-placeholder { + display: inline-block; + width: 22px; + height: 22px; + vertical-align: middle; + background: url('../assets/player/images/volume.svg') no-repeat; + background-size: contain; + + &::before { + content: ''; + } + } + + &.vjs-vol-0 .vjs-icon-placeholder { + background: url('../assets/player/images/volume-mute.svg') no-repeat; + background-size: contain; + } + } -.vjs-sublime-skin .vjs-volume-bar { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAcCAQAAACw95UnAAAAMElEQVRIx2NgoBL4n4YKGUYNHkEG4zJg1OCRYDCpBowaPJwMppbLRg0eNXjUYBLEAXWNUA6QNm1lAAAAAElFTkSuQmCC); - background-size: 22px 14px; - background-repeat: no-repeat; - height: 100%; - width: 100%; - max-width: 22px; - max-height: 14px; - margin: 7px 4px; - border-radius: 0; -} + .vjs-volume-menu-button, + .vjs-volume-panel { + width: 6em; + position: absolute; + right: 0; + margin-right: 65px; + } -.vjs-sublime-skin .vjs-volume-level { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAcAQAAAAAyhWABAAAAAnRSTlMAAHaTzTgAAAAZSURBVHgBYwAB/g9EUv+JokCqiaT+U4MCAPKPS7WUUOc1AAAAAElFTkSuQmCC); - background-size: 22px 14px; - background-repeat: no-repeat; - max-width: 22px; - max-height: 14px; - height: 100%; -} + .vjs-volume-bar { + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAcCAQAAACw95UnAAAAMElEQVRIx2NgoBL4n4YKGUYNHkEG4zJg1OCRYDCpBowaPJwMppbLRg0eNXjUYBLEAXWNUA6QNm1lAAAAAElFTkSuQmCC) no-repeat; + background-size: 22px 14px; + height: 100%; + width: 100%; + max-width: 22px; + max-height: 14px; + margin: 7px 4px; + border-radius: 0; + top: 3px; + + .vjs-volume-level { + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAcAQAAAAAyhWABAAAAAnRSTlMAAHaTzTgAAAAZSURBVHgBYwAB/g9EUv+JokCqiaT+U4MCAPKPS7WUUOc1AAAAAElFTkSuQmCC) no-repeat; + background-size: 22px 14px; + max-width: 22px; + max-height: 14px; + height: 100%; + } + } -/* New for VideoJS v6 */ -.vjs-sublime-skin .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active, -.vjs-sublime-skin .vjs-volume-panel.vjs-volume-panel-horizontal:active, -.vjs-sublime-skin .vjs-volume-panel.vjs-volume-panel-horizontal:focus, -.vjs-sublime-skin .vjs-volume-panel.vjs-volume-panel-horizontal:hover { - width: 6em; - transition-property: none; -} + .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active, + .vjs-volume-panel.vjs-volume-panel-horizontal:active, + .vjs-volume-panel.vjs-volume-panel-horizontal:focus, + .vjs-volume-panel.vjs-volume-panel-horizontal:hover { + width: 6em; + transition-property: none; + } -.vjs-sublime-skin .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control.vjs-volume-horizontal { - width: 3em; - height: auto; -} + .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control.vjs-volume-horizontal { + width: 3em; + height: auto; + } -.vjs-sublime-skin .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control { - transition-property: none; -} + .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control { + transition-property: none; + } -.vjs-sublime-skin .vjs-fullscreen-control .vjs-icon-placeholder { - display: none; /* Remove Duplicate Fullscreen Icon */ -} + .vjs-volume-panel { + .vjs-mute-control { + width: 2em; + z-index: 1; + padding: 0; + } + + .vjs-volume-control { + display: inline-block; + position: relative; + left: 5px; + opacity: 1; + width: 3em; + height: auto; + } + } -.vjs-sublime-skin .vjs-volume-panel .vjs-mute-control { - width: 2em; - z-index: 1; - padding: 0; -} + .vjs-fullscreen-control { + width: 37px; + + .vjs-icon-placeholder { + display: inline-block; + width: 22px; + height: 22px; + vertical-align: middle; + background: url('../assets/player/images/fullscreen.svg') no-repeat; + background-size: contain; + + &::before { + content: ''; + } + } + } -.vjs-sublime-skin .vjs-volume-panel .vjs-volume-control { - display: inline-block; - position: relative; - left: 5px; - opacity: 1; - width: 3em; - height: auto; + .vjs-menu-button-popup { + font-size: 13px; + font-weight: $font-semibold; + width: 42px; + + // Thanks: https://github.com/kmoskwiak/videojs-resolution-switcher/pull/92/files + .vjs-resolution-button-label { + line-height: $control-bar-height; + position: absolute; + top: 0; + left: -1px; + width: 100%; + height: 100%; + text-align: center; + box-sizing: inherit; + } + + .vjs-resolution-button { + outline: 0 !important; + } + + .vjs-menu { + top: 20px; + + .vjs-menu-content { + width: 4em; + left: 50%; /* Center the menu, in it's parent */ + margin-left: -2em; /* half of width, to center */ + } + + li { + text-transform: none; + font-size: 13px; + } + } + } + } } // Thanks: https://projects.lukehaas.me/css-loaders/ .vjs-loading-spinner { margin: 0 !important; - position: absolute; + //position: absolute; // 15px is the nav bar height top: calc(50% - 15px); left: 50%; -- cgit v1.2.3 From 6bafac54bf375cd60f1c06f6afdc648e0e19743d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 5 Dec 2017 15:01:47 +0100 Subject: Fix missing default avatar --- client/config/webpack.common.js | 12 ++++++++++++ client/src/app/shared/users/user.model.ts | 2 +- client/src/assets/images/favicon.png | Bin 2335 -> 539 bytes client/src/index.html | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) (limited to 'client') diff --git a/client/config/webpack.common.js b/client/config/webpack.common.js index acf22dab1..c37516271 100644 --- a/client/config/webpack.common.js +++ b/client/config/webpack.common.js @@ -13,6 +13,7 @@ const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin') const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin') const InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin') const ngcWebpack = require('ngc-webpack') +const CopyWebpackPlugin = require('copy-webpack-plugin') const WebpackNotifierPlugin = require('webpack-notifier') @@ -267,6 +268,17 @@ module.exports = function (options) { inject: 'body' }), + new CopyWebpackPlugin([ + { + from: helpers.root('src/assets/images/favicon.png'), + to: 'assets/images/favicon.png' + }, + { + from: helpers.root('src/assets/images/default-avatar.png'), + to: 'assets/images/default-avatar.png' + } + ]), + /* * Plugin: ScriptExtHtmlWebpackPlugin * Description: Enhances html-webpack-plugin functionality diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts index 220362ef0..9364ae721 100644 --- a/client/src/app/shared/users/user.model.ts +++ b/client/src/app/shared/users/user.model.ts @@ -54,6 +54,6 @@ export class User implements UserServerModel { getAvatarPath () { if (this.account && this.account.avatar) return this.account.avatar.path - return '/assets/images/default-avatar.png' + return '/client/assets/images/default-avatar.png' } } diff --git a/client/src/assets/images/favicon.png b/client/src/assets/images/favicon.png index bb57ee6b0..cef0f0d2e 100644 Binary files a/client/src/assets/images/favicon.png and b/client/src/assets/images/favicon.png differ diff --git a/client/src/index.html b/client/src/index.html index 8e94b903d..4af6b12f6 100644 --- a/client/src/index.html +++ b/client/src/index.html @@ -11,7 +11,7 @@ - + -- cgit v1.2.3 From 35fb2b68ff9cd9b755dae6f073c32a97dc237e35 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 5 Dec 2017 16:30:03 +0100 Subject: Fix favicon ratio --- client/src/assets/images/favicon.png | Bin 539 -> 833 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'client') diff --git a/client/src/assets/images/favicon.png b/client/src/assets/images/favicon.png index cef0f0d2e..2e589cf6c 100644 Binary files a/client/src/assets/images/favicon.png and b/client/src/assets/images/favicon.png differ -- cgit v1.2.3 From d235f6b0d1054a2a3451dacade927caefce8f30c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 5 Dec 2017 16:48:26 +0100 Subject: Design signup and login pages --- client/config/webpack.common.js | 1 + .../account-change-password.component.html | 2 + client/src/app/login/login.component.html | 53 +++++++++++----------- client/src/app/login/login.component.scss | 8 ++++ client/src/app/login/login.component.ts | 3 +- client/src/app/shared/users/user.model.ts | 2 +- client/src/app/signup/signup.component.html | 10 ++-- client/src/app/signup/signup.component.scss | 8 ++++ client/src/app/signup/signup.component.ts | 3 +- client/src/sass/application.scss | 9 ++-- 10 files changed, 58 insertions(+), 41 deletions(-) create mode 100644 client/src/app/login/login.component.scss create mode 100644 client/src/app/signup/signup.component.scss (limited to 'client') diff --git a/client/config/webpack.common.js b/client/config/webpack.common.js index c37516271..f387b44f9 100644 --- a/client/config/webpack.common.js +++ b/client/config/webpack.common.js @@ -302,6 +302,7 @@ module.exports = function (options) { */ new LoaderOptionsPlugin({ options: { + context: '', sassLoader: { precision: 10, includePaths: [ helpers.root('src/sass') ] diff --git a/client/src/app/account/account-settings/account-change-password/account-change-password.component.html b/client/src/app/account/account-settings/account-change-password/account-change-password.component.html index bfb55218f..c57e705f9 100644 --- a/client/src/app/account/account-settings/account-change-password/account-change-password.component.html +++ b/client/src/app/account/account-settings/account-change-password/account-change-password.component.html @@ -1,6 +1,8 @@
{{ error }}
+ + -
- -

Login

+
+
+ Login +
-
{{ error }}
+
{{ error }}
- -
- - -
- {{ formErrors.username }} -
+ +
+ + +
+ {{ formErrors.username }}
+
-
- - -
- {{ formErrors.password }} -
+
+ + +
+ {{ formErrors.password }}
+
- - -
+ +
diff --git a/client/src/app/login/login.component.scss b/client/src/app/login/login.component.scss new file mode 100644 index 000000000..fd6981c59 --- /dev/null +++ b/client/src/app/login/login.component.scss @@ -0,0 +1,8 @@ +input:not([type=submit]) { + @include peertube-input-text(340px); + display: block; +} + +input[type=submit] { + @include peertube-button; +} diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts index 32dc9e36f..dfede5924 100644 --- a/client/src/app/login/login.component.ts +++ b/client/src/app/login/login.component.ts @@ -7,7 +7,8 @@ import { FormReactive } from '../shared' @Component({ selector: 'my-login', - templateUrl: './login.component.html' + templateUrl: './login.component.html', + styleUrls: [ './login.component.scss' ] }) export class LoginComponent extends FormReactive implements OnInit { diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts index 9364ae721..b1c323114 100644 --- a/client/src/app/shared/users/user.model.ts +++ b/client/src/app/shared/users/user.model.ts @@ -54,6 +54,6 @@ export class User implements UserServerModel { getAvatarPath () { if (this.account && this.account.avatar) return this.account.avatar.path - return '/client/assets/images/default-avatar.png' + return API_URL + '/client/assets/images/default-avatar.png' } } diff --git a/client/src/app/signup/signup.component.html b/client/src/app/signup/signup.component.html index b8b7826eb..1e9f7f949 100644 --- a/client/src/app/signup/signup.component.html +++ b/client/src/app/signup/signup.component.html @@ -1,7 +1,8 @@ -
-
+
-

Signup

+
+ Signup +
{{ error }}
@@ -39,8 +40,7 @@
- + -
diff --git a/client/src/app/signup/signup.component.scss b/client/src/app/signup/signup.component.scss new file mode 100644 index 000000000..fd6981c59 --- /dev/null +++ b/client/src/app/signup/signup.component.scss @@ -0,0 +1,8 @@ +input:not([type=submit]) { + @include peertube-input-text(340px); + display: block; +} + +input[type=submit] { + @include peertube-button; +} diff --git a/client/src/app/signup/signup.component.ts b/client/src/app/signup/signup.component.ts index 28e1ed0a8..13390a32a 100644 --- a/client/src/app/signup/signup.component.ts +++ b/client/src/app/signup/signup.component.ts @@ -16,7 +16,8 @@ import { UserCreate } from '../../../../shared' @Component({ selector: 'my-signup', - templateUrl: './signup.component.html' + templateUrl: './signup.component.html', + styleUrls: [ './signup.component.scss' ] }) export class SignupComponent extends FormReactive implements OnInit { error: string = null diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 5e401f93b..4c5c0202c 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -20,12 +20,9 @@ input.readonly { background-color: #fff !important; } -.form-control, .btn { - border-radius: 0; -} - -.dropdown-menu { - border-radius: 0; +label { + font-weight: $font-bold; + font-size: 15px; } .glyphicon-black { -- cgit v1.2.3 From f3aaa9a95cc2b61f1f255472d7014d08faa66561 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 5 Dec 2017 17:46:33 +0100 Subject: Fix client search --- .../account-videos/account-videos.component.ts | 8 +--- client/src/app/app.component.html | 2 +- client/src/app/app.module.ts | 4 +- client/src/app/header/header.component.html | 10 +++++ client/src/app/header/header.component.scss | 39 +++++++++++++++++ client/src/app/header/header.component.ts | 28 ++++++++++++ client/src/app/header/index.ts | 1 + client/src/app/shared/index.ts | 1 - client/src/app/shared/misc/utils.ts | 18 ++++++++ client/src/app/shared/search/index.ts | 4 -- client/src/app/shared/search/search-field.type.ts | 1 - client/src/app/shared/search/search.component.html | 10 ----- client/src/app/shared/search/search.component.scss | 39 ----------------- client/src/app/shared/search/search.component.ts | 42 ------------------ client/src/app/shared/search/search.model.ts | 6 --- client/src/app/shared/search/search.service.ts | 18 -------- client/src/app/shared/shared.module.ts | 4 -- client/src/app/shared/video/abstract-video-list.ts | 24 +++++----- client/src/app/shared/video/video.service.ts | 9 ++-- client/src/app/signup/signup.component.html | 2 +- client/src/app/videos/video-list/index.ts | 1 + .../video-list/video-recently-added.component.ts | 12 +++-- .../videos/video-list/video-search.component.ts | 51 ++++++++++++++++++++++ .../videos/video-list/video-trending.component.ts | 12 +++-- client/src/app/videos/videos-routing.module.ts | 11 +++++ client/src/app/videos/videos.module.ts | 5 ++- 26 files changed, 195 insertions(+), 167 deletions(-) create mode 100644 client/src/app/header/header.component.html create mode 100644 client/src/app/header/header.component.scss create mode 100644 client/src/app/header/header.component.ts create mode 100644 client/src/app/header/index.ts create mode 100644 client/src/app/shared/misc/utils.ts delete mode 100644 client/src/app/shared/search/index.ts delete mode 100644 client/src/app/shared/search/search-field.type.ts delete mode 100644 client/src/app/shared/search/search.component.html delete mode 100644 client/src/app/shared/search/search.component.scss delete mode 100644 client/src/app/shared/search/search.component.ts delete mode 100644 client/src/app/shared/search/search.model.ts delete mode 100644 client/src/app/shared/search/search.service.ts create mode 100644 client/src/app/videos/video-list/video-search.component.ts (limited to 'client') diff --git a/client/src/app/account/account-videos/account-videos.component.ts b/client/src/app/account/account-videos/account-videos.component.ts index cc28f511a..1bc6c0a35 100644 --- a/client/src/app/account/account-videos/account-videos.component.ts +++ b/client/src/app/account/account-videos/account-videos.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' import { AbstractVideoList } from '../../shared/video/abstract-video-list' @@ -9,7 +9,7 @@ import { VideoService } from '../../shared/video/video.service' templateUrl: './account-videos.component.html', styleUrls: [ './account-videos.component.scss' ] }) -export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { +export class AccountVideosComponent extends AbstractVideoList implements OnInit { titlePage = 'My videos' currentRoute = '/account/videos' @@ -24,10 +24,6 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit, super.ngOnInit() } - ngOnDestroy () { - super.ngOnDestroy() - } - getVideosObservable () { return this.videoService.getMyVideos(this.pagination, this.sort) } diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index 640524e23..b095e44d6 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -11,7 +11,7 @@
- +
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 342589003..ee7cb0c8a 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -21,6 +21,7 @@ import { SignupModule } from './signup' import { SharedModule } from './shared' import { VideosModule } from './videos' import { MenuComponent, MenuAdminComponent } from './menu' +import { HeaderComponent } from './header' export function metaFactory (): MetaLoader { return new MetaStaticLoader({ @@ -51,7 +52,8 @@ const APP_PROVIDERS = [ AppComponent, MenuComponent, - MenuAdminComponent + MenuAdminComponent, + HeaderComponent ], imports: [ BrowserModule, diff --git a/client/src/app/header/header.component.html b/client/src/app/header/header.component.html new file mode 100644 index 000000000..aa72fb68a --- /dev/null +++ b/client/src/app/header/header.component.html @@ -0,0 +1,10 @@ + + + + + + Upload + diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss new file mode 100644 index 000000000..7ba8ef26c --- /dev/null +++ b/client/src/app/header/header.component.scss @@ -0,0 +1,39 @@ +#search-video { + @include peertube-input-text($search-input-width); + margin-right: 15px; + padding-right: 25px; // For the search icon + + &::placeholder { + color: #000; + } +} + +.icon.icon-search { + display: inline-block; + background: url('../../../assets/images/header/search.svg') no-repeat; + background-size: contain; + width: 25px; + height: 21px; + vertical-align: middle; + cursor: pointer; + // yolo + position: absolute; + margin-left: -50px; + margin-top: 5px; +} + +.upload-button { + @include peertube-button-link; + + margin-right: 25px; + + .icon.icon-upload { + display: inline-block; + background: url('../../../assets/images/header/upload.svg') no-repeat; + background-size: contain; + width: 22px; + height: 24px; + vertical-align: middle; + margin-right: 6px; + } +} diff --git a/client/src/app/header/header.component.ts b/client/src/app/header/header.component.ts new file mode 100644 index 000000000..a903048f2 --- /dev/null +++ b/client/src/app/header/header.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core' +import { Router } from '@angular/router' +import { getParameterByName } from '../shared/misc/utils' + +@Component({ + selector: 'my-header', + templateUrl: './header.component.html', + styleUrls: [ './header.component.scss' ] +}) + +export class HeaderComponent implements OnInit { + searchValue = '' + + constructor (private router: Router) {} + + ngOnInit () { + const searchQuery = getParameterByName('search', window.location.href) + if (searchQuery) this.searchValue = searchQuery + } + + doSearch () { + if (!this.searchValue) return + + this.router.navigate([ '/videos', 'search' ], { + queryParams: { search: this.searchValue } + }) + } +} diff --git a/client/src/app/header/index.ts b/client/src/app/header/index.ts new file mode 100644 index 000000000..d98d2d00a --- /dev/null +++ b/client/src/app/header/index.ts @@ -0,0 +1 @@ +export * from './header.component' diff --git a/client/src/app/shared/index.ts b/client/src/app/shared/index.ts index 79bf5ef43..413dda16a 100644 --- a/client/src/app/shared/index.ts +++ b/client/src/app/shared/index.ts @@ -1,7 +1,6 @@ export * from './auth' export * from './forms' export * from './rest' -export * from './search' export * from './users' export * from './video-abuse' export * from './video-blacklist' diff --git a/client/src/app/shared/misc/utils.ts b/client/src/app/shared/misc/utils.ts new file mode 100644 index 000000000..2b5c3686e --- /dev/null +++ b/client/src/app/shared/misc/utils.ts @@ -0,0 +1,18 @@ +// Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript + +function getParameterByName (name: string, url: string) { + if (!url) url = window.location.href + name = name.replace(/[\[\]]/g, '\\$&') + + const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)') + const results = regex.exec(url) + + if (!results) return null + if (!results[2]) return '' + + return decodeURIComponent(results[2].replace(/\+/g, ' ')) +} + +export { + getParameterByName +} diff --git a/client/src/app/shared/search/index.ts b/client/src/app/shared/search/index.ts deleted file mode 100644 index d4016cf89..000000000 --- a/client/src/app/shared/search/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './search-field.type' -export * from './search.component' -export * from './search.model' -export * from './search.service' diff --git a/client/src/app/shared/search/search-field.type.ts b/client/src/app/shared/search/search-field.type.ts deleted file mode 100644 index 7323d6cc3..000000000 --- a/client/src/app/shared/search/search-field.type.ts +++ /dev/null @@ -1 +0,0 @@ -export type SearchField = 'name' | 'account' | 'host' | 'tags' diff --git a/client/src/app/shared/search/search.component.html b/client/src/app/shared/search/search.component.html deleted file mode 100644 index 9bc9bafe4..000000000 --- a/client/src/app/shared/search/search.component.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - Upload - diff --git a/client/src/app/shared/search/search.component.scss b/client/src/app/shared/search/search.component.scss deleted file mode 100644 index 7ba8ef26c..000000000 --- a/client/src/app/shared/search/search.component.scss +++ /dev/null @@ -1,39 +0,0 @@ -#search-video { - @include peertube-input-text($search-input-width); - margin-right: 15px; - padding-right: 25px; // For the search icon - - &::placeholder { - color: #000; - } -} - -.icon.icon-search { - display: inline-block; - background: url('../../../assets/images/header/search.svg') no-repeat; - background-size: contain; - width: 25px; - height: 21px; - vertical-align: middle; - cursor: pointer; - // yolo - position: absolute; - margin-left: -50px; - margin-top: 5px; -} - -.upload-button { - @include peertube-button-link; - - margin-right: 25px; - - .icon.icon-upload { - display: inline-block; - background: url('../../../assets/images/header/upload.svg') no-repeat; - background-size: contain; - width: 22px; - height: 24px; - vertical-align: middle; - margin-right: 6px; - } -} diff --git a/client/src/app/shared/search/search.component.ts b/client/src/app/shared/search/search.component.ts deleted file mode 100644 index f49ecc8ad..000000000 --- a/client/src/app/shared/search/search.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, OnInit } from '@angular/core' -import { Router } from '@angular/router' -import { Search } from './search.model' -import { SearchService } from './search.service' - -@Component({ - selector: 'my-search', - templateUrl: './search.component.html', - styleUrls: [ './search.component.scss' ] -}) - -export class SearchComponent implements OnInit { - searchCriteria: Search = { - field: 'name', - value: '' - } - - constructor (private searchService: SearchService, private router: Router) {} - - ngOnInit () { - // Subscribe if the search changed - // Usually changed by videos list component - this.searchService.updateSearch.subscribe( - newSearchCriteria => { - // Put a field by default - if (!newSearchCriteria.field) { - newSearchCriteria.field = 'name' - } - - this.searchCriteria = newSearchCriteria - } - ) - } - - doSearch () { - // if (this.router.url.indexOf('/videos/list') === -1) { - // this.router.navigate([ '/videos/list' ]) - // } - - this.searchService.searchUpdated.next(this.searchCriteria) - } -} diff --git a/client/src/app/shared/search/search.model.ts b/client/src/app/shared/search/search.model.ts deleted file mode 100644 index 174adf2c6..000000000 --- a/client/src/app/shared/search/search.model.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SearchField } from './search-field.type' - -export interface Search { - field: SearchField - value: string -} diff --git a/client/src/app/shared/search/search.service.ts b/client/src/app/shared/search/search.service.ts deleted file mode 100644 index 0480b46bd..000000000 --- a/client/src/app/shared/search/search.service.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Injectable } from '@angular/core' -import { Subject } from 'rxjs/Subject' -import { ReplaySubject } from 'rxjs/ReplaySubject' - -import { Search } from './search.model' - -// This class is needed to communicate between videos/ and search component -// Remove it when we'll be able to subscribe to router changes -@Injectable() -export class SearchService { - searchUpdated: Subject - updateSearch: Subject - - constructor () { - this.updateSearch = new Subject() - this.searchUpdated = new ReplaySubject(1) - } -} diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index f7ced040d..86e1a380e 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -17,7 +17,6 @@ import { FromNowPipe } from './misc/from-now.pipe' import { LoaderComponent } from './misc/loader.component' import { NumberFormatterPipe } from './misc/number-formatter.pipe' import { RestExtractor, RestService } from './rest' -import { SearchComponent, SearchService } from './search' import { UserService } from './users' import { VideoAbuseService } from './video-abuse' import { VideoBlacklistService } from './video-blacklist' @@ -43,7 +42,6 @@ import { VideoService } from './video/video.service' ], declarations: [ - SearchComponent, LoaderComponent, VideoThumbnailComponent, NumberFormatterPipe, @@ -66,7 +64,6 @@ import { VideoService } from './video/video.service' BytesPipe, KeysPipe, - SearchComponent, LoaderComponent, VideoThumbnailComponent, @@ -78,7 +75,6 @@ import { VideoService } from './video/video.service' AUTH_INTERCEPTOR_PROVIDER, RestExtractor, RestService, - SearchService, VideoAbuseService, VideoBlacklistService, UserService, diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts index cf717cf4c..84ca5cbe4 100644 --- a/client/src/app/shared/video/abstract-video-list.ts +++ b/client/src/app/shared/video/abstract-video-list.ts @@ -1,25 +1,25 @@ -import { OnDestroy, OnInit } from '@angular/core' +import { OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' import { Observable } from 'rxjs/Observable' -import { Subscription } from 'rxjs/Subscription' import { SortField } from './sort-field.type' import { VideoPagination } from './video-pagination.model' import { Video } from './video.model' -export abstract class AbstractVideoList implements OnInit, OnDestroy { +export abstract class AbstractVideoList implements OnInit { pagination: VideoPagination = { currentPage: 1, itemsPerPage: 25, totalItems: null } sort: SortField = '-createdAt' + defaultSort: SortField = '-createdAt' videos: Video[] = [] + loadOnInit = true protected notificationsService: NotificationsService protected router: Router protected route: ActivatedRoute - protected subActivatedRoute: Subscription protected abstract currentRoute: string @@ -32,13 +32,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { // Subscribe to route changes const routeParams = this.route.snapshot.params this.loadRouteParams(routeParams) - this.loadMoreVideos('after') - } - - ngOnDestroy () { - if (this.subActivatedRoute) { - this.subActivatedRoute.unsubscribe() - } + if (this.loadOnInit === true) this.loadMoreVideos('after') } onNearOfTop () { @@ -53,6 +47,12 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { } } + reloadVideos () { + this.videos = [] + this.loadedPages = {} + this.loadMoreVideos('before') + } + loadMoreVideos (where: 'before' | 'after') { if (this.loadedPages[this.pagination.currentPage] === true) return @@ -105,7 +105,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { } protected loadRouteParams (routeParams: { [ key: string ]: any }) { - this.sort = routeParams['sort'] as SortField || '-createdAt' + this.sort = routeParams['sort'] as SortField || this.defaultSort if (routeParams['page'] !== undefined) { this.pagination.currentPage = parseInt(routeParams['page'], 10) diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index b2a26417c..3f35b67c4 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts @@ -11,7 +11,7 @@ import { VideoRateType } from '../../../../../shared/models/videos/video-rate.ty import { VideoUpdate } from '../../../../../shared/models/videos/video-update.model' import { RestExtractor } from '../rest/rest-extractor.service' import { RestService } from '../rest/rest.service' -import { Search } from '../search/search.model' +import { Search } from '../header/search.model' import { UserService } from '../users/user.service' import { SortField } from './sort-field.type' import { VideoDetails } from './video-details.model' @@ -91,15 +91,14 @@ export class VideoService { .catch((res) => this.restExtractor.handleError(res)) } - searchVideos (search: Search, videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { - const url = VideoService.BASE_VIDEO_URL + 'search/' + encodeURIComponent(search.value) + searchVideos (search: string, videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { + const url = VideoService.BASE_VIDEO_URL + 'search' const pagination = this.videoPaginationToRestPagination(videoPagination) let params = new HttpParams() params = this.restService.addRestGetParams(params, pagination, sort) - - if (search.field) params.set('field', search.field) + params = params.append('search', search) return this.authHttp .get>(url, { params }) diff --git a/client/src/app/signup/signup.component.html b/client/src/app/signup/signup.component.html index 1e9f7f949..8a30ab512 100644 --- a/client/src/app/signup/signup.component.html +++ b/client/src/app/signup/signup.component.html @@ -1,7 +1,7 @@
- Signup + Create an account
{{ error }}
diff --git a/client/src/app/videos/video-list/index.ts b/client/src/app/videos/video-list/index.ts index 594e31984..13024294e 100644 --- a/client/src/app/videos/video-list/index.ts +++ b/client/src/app/videos/video-list/index.ts @@ -1,3 +1,4 @@ export * from './video-recently-added.component' export * from './video-trending.component' +export * from './video-search.component' export * from './shared' diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts index d48804414..6168fac95 100644 --- a/client/src/app/videos/video-list/video-recently-added.component.ts +++ b/client/src/app/videos/video-list/video-recently-added.component.ts @@ -1,17 +1,19 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from '../../shared/video/video.service' import { AbstractVideoList } from '../../shared/video/abstract-video-list' +import { SortField } from '../../shared/video/sort-field.type' +import { VideoService } from '../../shared/video/video.service' @Component({ selector: 'my-videos-recently-added', styleUrls: [ '../../shared/video/abstract-video-list.scss' ], templateUrl: '../../shared/video/abstract-video-list.html' }) -export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { +export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit { titlePage = 'Recently added' currentRoute = '/videos/recently-added' + sort: SortField = '-createdAt' constructor (protected router: Router, protected route: ActivatedRoute, @@ -24,10 +26,6 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On super.ngOnInit() } - ngOnDestroy () { - super.ngOnDestroy() - } - getVideosObservable () { return this.videoService.getVideos(this.pagination, this.sort) } diff --git a/client/src/app/videos/video-list/video-search.component.ts b/client/src/app/videos/video-list/video-search.component.ts new file mode 100644 index 000000000..ba851d27e --- /dev/null +++ b/client/src/app/videos/video-list/video-search.component.ts @@ -0,0 +1,51 @@ +import { Component, OnDestroy, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { AbstractVideoList } from 'app/shared/video/abstract-video-list' +import { Subscription } from 'rxjs/Subscription' +import { SortField } from '../../shared/video/sort-field.type' +import { VideoService } from '../../shared/video/video.service' + +@Component({ + selector: 'my-videos-search', + styleUrls: [ '../../shared/video/abstract-video-list.scss' ], + templateUrl: '../../shared/video/abstract-video-list.html' +}) +export class VideoSearchComponent extends AbstractVideoList implements OnInit, OnDestroy { + titlePage = 'Search' + currentRoute = '/videos/search' + loadOnInit = false + + private search = '' + private subActivatedRoute: Subscription + + constructor (protected router: Router, + protected route: ActivatedRoute, + protected notificationsService: NotificationsService, + private videoService: VideoService) { + super() + } + + ngOnInit () { + super.ngOnInit() + + this.subActivatedRoute = this.route.queryParams.subscribe( + queryParams => { + this.search = queryParams['search'] + this.reloadVideos() + }, + + err => this.notificationsService.error('Error', err.text) + ) + } + + ngOnDestroy () { + if (this.subActivatedRoute) { + this.subActivatedRoute.unsubscribe() + } + } + + getVideosObservable () { + return this.videoService.searchVideos(this.search, this.pagination, this.sort) + } +} diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts index 9108289c9..e80fd7f2c 100644 --- a/client/src/app/videos/video-list/video-trending.component.ts +++ b/client/src/app/videos/video-list/video-trending.component.ts @@ -1,17 +1,19 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from '../../shared/video/video.service' import { AbstractVideoList } from 'app/shared/video/abstract-video-list' +import { SortField } from '../../shared/video/sort-field.type' +import { VideoService } from '../../shared/video/video.service' @Component({ selector: 'my-videos-trending', styleUrls: [ '../../shared/video/abstract-video-list.scss' ], templateUrl: '../../shared/video/abstract-video-list.html' }) -export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { +export class VideoTrendingComponent extends AbstractVideoList implements OnInit { titlePage = 'Trending' currentRoute = '/videos/trending' + defaultSort: SortField = '-views' constructor (protected router: Router, protected route: ActivatedRoute, @@ -24,10 +26,6 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit, super.ngOnInit() } - ngOnDestroy () { - super.ngOnDestroy() - } - getVideosObservable () { return this.videoService.getVideos(this.pagination, this.sort) } diff --git a/client/src/app/videos/videos-routing.module.ts b/client/src/app/videos/videos-routing.module.ts index 204851c81..6910421b7 100644 --- a/client/src/app/videos/videos-routing.module.ts +++ b/client/src/app/videos/videos-routing.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' import { MetaGuard } from '@ngx-meta/core' +import { VideoSearchComponent } from './video-list' import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' import { VideoTrendingComponent } from './video-list/video-trending.component' import { VideosComponent } from './videos.component' @@ -34,6 +35,15 @@ const videosRoutes: Routes = [ } } }, + { + path: 'search', + component: VideoSearchComponent, + data: { + meta: { + title: 'Search videos' + } + } + }, { path: 'upload', loadChildren: 'app/videos/+video-edit#VideoAddModule', @@ -54,6 +64,7 @@ const videosRoutes: Routes = [ }, { path: ':uuid', + pathMatch: 'full', redirectTo: 'watch/:uuid' }, { diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts index 6d846fd3b..8c8d52ad9 100644 --- a/client/src/app/videos/videos.module.ts +++ b/client/src/app/videos/videos.module.ts @@ -1,6 +1,6 @@ import { NgModule } from '@angular/core' import { SharedModule } from '../shared' -import { VideoMiniatureComponent } from './video-list' +import { VideoMiniatureComponent, VideoSearchComponent } from './video-list' import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' import { VideoTrendingComponent } from './video-list/video-trending.component' import { VideosRoutingModule } from './videos-routing.module' @@ -17,7 +17,8 @@ import { VideosComponent } from './videos.component' VideoTrendingComponent, VideoRecentlyAddedComponent, - VideoMiniatureComponent + VideoMiniatureComponent, + VideoSearchComponent ], exports: [ -- cgit v1.2.3 From a06a31c75c0cd4d337e3e193c670a77cabcd9507 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 5 Dec 2017 18:43:15 +0100 Subject: Fix player control bar when video is not loaded --- client/src/assets/player/peertube-videojs-plugin.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'client') diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts index 977455bff..add4e521e 100644 --- a/client/src/assets/player/peertube-videojs-plugin.ts +++ b/client/src/assets/player/peertube-videojs-plugin.ts @@ -117,10 +117,12 @@ const WebTorrentButton = videojsUntyped.extend(Button, { createEl: function () { const div = document.createElement('div') + const subDiv = document.createElement('div') + div.appendChild(subDiv) const downloadIcon = document.createElement('span') downloadIcon.classList.add('icon', 'icon-download') - div.appendChild(downloadIcon) + subDiv.appendChild(downloadIcon) const downloadSpeedText = document.createElement('span') downloadSpeedText.classList.add('download-speed-text') @@ -129,11 +131,11 @@ const WebTorrentButton = videojsUntyped.extend(Button, { const downloadSpeedUnit = document.createElement('span') downloadSpeedText.appendChild(downloadSpeedNumber) downloadSpeedText.appendChild(downloadSpeedUnit) - div.appendChild(downloadSpeedText) + subDiv.appendChild(downloadSpeedText) const uploadIcon = document.createElement('span') uploadIcon.classList.add('icon', 'icon-upload') - div.appendChild(uploadIcon) + subDiv.appendChild(uploadIcon) const uploadSpeedText = document.createElement('span') uploadSpeedText.classList.add('upload-speed-text') @@ -142,19 +144,19 @@ const WebTorrentButton = videojsUntyped.extend(Button, { const uploadSpeedUnit = document.createElement('span') uploadSpeedText.appendChild(uploadSpeedNumber) uploadSpeedText.appendChild(uploadSpeedUnit) - div.appendChild(uploadSpeedText) + subDiv.appendChild(uploadSpeedText) const peersText = document.createElement('span') peersText.textContent = ' peers' peersText.classList.add('peers-text') const peersNumber = document.createElement('span') peersNumber.classList.add('peers-number') - div.appendChild(peersNumber) - div.appendChild(peersText) + subDiv.appendChild(peersNumber) + subDiv.appendChild(peersText) div.className = 'vjs-webtorrent' // Hide the stats before we get the info - div.style.display = 'none' + subDiv.style.display = 'none' this.player_.on('torrentInfo', (event, data) => { const downloadSpeed = bytes(data.downloadSpeed) @@ -169,7 +171,7 @@ const WebTorrentButton = videojsUntyped.extend(Button, { peersNumber.textContent = numPeers - div.style.display = 'block' + subDiv.style.display = 'block' }) return div -- cgit v1.2.3 From a2b817d322ef4074bdaaf2589ada567f338323f4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 6 Dec 2017 09:19:25 +0100 Subject: Better error messages --- .../account-change-password.component.html | 8 ++++---- .../account-change-password.component.scss | 7 ++++++- .../account-details/account-details.component.html | 2 +- .../account-details/account-details.component.scss | 1 + .../app/account/account-videos/account-videos.component.html | 2 ++ .../app/account/account-videos/account-videos.component.scss | 7 +++++-- client/src/app/login/login.component.html | 8 ++++---- client/src/app/shared/video/abstract-video-list.ts | 8 ++++++++ client/src/app/signup/signup.component.html | 12 ++++++------ client/src/sass/_variables.scss | 1 + client/src/sass/application.scss | 10 ++++++++++ 11 files changed, 48 insertions(+), 18 deletions(-) (limited to 'client') diff --git a/client/src/app/account/account-settings/account-change-password/account-change-password.component.html b/client/src/app/account/account-settings/account-change-password/account-change-password.component.html index c57e705f9..b0e3cada4 100644 --- a/client/src/app/account/account-settings/account-change-password/account-change-password.component.html +++ b/client/src/app/account/account-settings/account-change-password/account-change-password.component.html @@ -4,15 +4,15 @@ -
+
{{ formErrors['new-password'] }}
diff --git a/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss b/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss index 593355b70..75827abbf 100644 --- a/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss +++ b/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss @@ -1,9 +1,14 @@ input[type=password] { @include peertube-input-text(340px); display: block; - margin-bottom: 10px; + + &#new-confirmed-password { + margin-top: 15px; + } } input[type=submit] { @include peertube-button; + margin-top: 15px; } + diff --git a/client/src/app/account/account-settings/account-details/account-details.component.html b/client/src/app/account/account-settings/account-details/account-details.component.html index c3cf6b629..bc18b39b4 100644 --- a/client/src/app/account/account-settings/account-details/account-details.component.html +++ b/client/src/app/account/account-settings/account-details/account-details.component.html @@ -10,5 +10,5 @@ {{ formErrors['displayNSFW'] }}
- + diff --git a/client/src/app/account/account-settings/account-details/account-details.component.scss b/client/src/app/account/account-settings/account-details/account-details.component.scss index b1810d4f9..687166d9e 100644 --- a/client/src/app/account/account-settings/account-details/account-details.component.scss +++ b/client/src/app/account/account-settings/account-details/account-details.component.scss @@ -8,4 +8,5 @@ input[type=submit] { @include peertube-button; display: block; + margin-top: 15px; } diff --git a/client/src/app/account/account-videos/account-videos.component.html b/client/src/app/account/account-videos/account-videos.component.html index eb0a32fd3..81bda9477 100644 --- a/client/src/app/account/account-videos/account-videos.component.html +++ b/client/src/app/account/account-videos/account-videos.component.html @@ -1,7 +1,9 @@
diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index b26933d22..c31497350 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss @@ -2,8 +2,11 @@ display: flex; height: 130px; padding-bottom: 20px; - margin-bottom: 20px; - border-bottom: 1px solid #C6C6C6; + + &:not(:last-child) { + margin-bottom: 20px; + border-bottom: 1px solid #C6C6C6; + } my-video-thumbnail { margin-right: 10px; diff --git a/client/src/app/login/login.component.html b/client/src/app/login/login.component.html index 82b70c98c..24807987c 100644 --- a/client/src/app/login/login.component.html +++ b/client/src/app/login/login.component.html @@ -10,9 +10,9 @@ -
+
{{ formErrors.username }}
@@ -21,9 +21,9 @@ -
+
{{ formErrors.password }}
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts index 84ca5cbe4..ee1ed2cb2 100644 --- a/client/src/app/shared/video/abstract-video-list.ts +++ b/client/src/app/shared/video/abstract-video-list.ts @@ -32,6 +32,7 @@ export abstract class AbstractVideoList implements OnInit { // Subscribe to route changes const routeParams = this.route.snapshot.params this.loadRouteParams(routeParams) + if (this.loadOnInit === true) this.loadMoreVideos('after') } @@ -60,6 +61,13 @@ export abstract class AbstractVideoList implements OnInit { observable.subscribe( ({ videos, totalVideos }) => { + // Paging is too high, return to the first one + if (totalVideos <= ((this.pagination.currentPage - 1) * this.pagination.itemsPerPage)) { + this.pagination.currentPage = 1 + this.setNewRouteParams() + return this.reloadVideos() + } + this.loadedPages[this.pagination.currentPage] = true this.pagination.totalItems = totalVideos diff --git a/client/src/app/signup/signup.component.html b/client/src/app/signup/signup.component.html index 8a30ab512..eb36b29f6 100644 --- a/client/src/app/signup/signup.component.html +++ b/client/src/app/signup/signup.component.html @@ -11,9 +11,9 @@ -
+
{{ formErrors.username }}
@@ -22,9 +22,9 @@ -
+
{{ formErrors.email }}
@@ -33,9 +33,9 @@ -
+
{{ formErrors.password }}
diff --git a/client/src/sass/_variables.scss b/client/src/sass/_variables.scss index 0d655e85c..81dafdc19 100644 --- a/client/src/sass/_variables.scss +++ b/client/src/sass/_variables.scss @@ -7,6 +7,7 @@ $orange-color: #F1680D; $black-background: #000; $grey-background: #f6f2f2; +$red-error: #FF0000; $expanded-horizontal-margins: 150px; $not-expanded-horizontal-margins: 30px; diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 4c5c0202c..b860e1bf2 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -25,6 +25,16 @@ label { font-size: 15px; } +.form-error { + display: block; + color: $red-error; + margin-top: 5px; +} + +.input-error { + border-color: $red-error !important; +} + .glyphicon-black { color: black; } -- cgit v1.2.3 From 332542bc6814bd16c2daf47dc776f9f4b126ec2e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 6 Dec 2017 14:05:38 +0100 Subject: Add delete button to my videos --- .../account-videos/account-videos.component.html | 7 ++++++- .../account-videos/account-videos.component.scss | 18 +++++++++++++++--- .../account-videos/account-videos.component.ts | 22 ++++++++++++++++++++++ .../shared/video-miniature.component.scss | 2 +- client/src/assets/images/account/delete.svg | 14 ++++++++++++++ 5 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 client/src/assets/images/account/delete.svg (limited to 'client') diff --git a/client/src/app/account/account-videos/account-videos.component.html b/client/src/app/account/account-videos/account-videos.component.html index 81bda9477..30db69429 100644 --- a/client/src/app/account/account-videos/account-videos.component.html +++ b/client/src/app/account/account-videos/account-videos.component.html @@ -13,7 +13,12 @@ {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views
- + + + Delete + + + Edit diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index c31497350..7ac25afc3 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss @@ -25,7 +25,7 @@ } } - .edit-button { + .action-button { @include peertube-button-link; font-size: 15px; @@ -33,15 +33,27 @@ color: #585858; background-color: #E5E5E5; - .icon.icon-edit { + &.action-button-delete { + margin-right: 10px; + } + + .icon.icon-edit, .icon.icon-delete { display: inline-block; - background: url('../../../assets/images/account/edit.svg') no-repeat; + background-repeat: no-repeat; background-size: contain; width: 21px; height: 21px; vertical-align: middle; position: relative; top: -2px; + + &.icon-edit { + background-image: url('../../../assets/images/account/edit.svg'); + } + + &.icon-delete { + background-image: url('../../../assets/images/account/delete.svg'); + } } } } diff --git a/client/src/app/account/account-videos/account-videos.component.ts b/client/src/app/account/account-videos/account-videos.component.ts index 1bc6c0a35..9c2cc2404 100644 --- a/client/src/app/account/account-videos/account-videos.component.ts +++ b/client/src/app/account/account-videos/account-videos.component.ts @@ -1,7 +1,9 @@ import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' +import { ConfirmService } from '../../core/confirm' import { AbstractVideoList } from '../../shared/video/abstract-video-list' +import { Video } from '../../shared/video/video.model' import { VideoService } from '../../shared/video/video.service' @Component({ @@ -16,6 +18,7 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit constructor (protected router: Router, protected route: ActivatedRoute, protected notificationsService: NotificationsService, + protected confirmService: ConfirmService, private videoService: VideoService) { super() } @@ -27,4 +30,23 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit getVideosObservable () { return this.videoService.getMyVideos(this.pagination, this.sort) } + + deleteVideo (video: Video) { + this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete').subscribe( + res => { + if (res === false) return + + this.videoService.removeVideo(video.id) + .subscribe( + status => { + this.notificationsService.success('Success', `Video ${video.name} deleted.`) + const index = this.videos.findIndex(v => v.id === video.id) + this.videos.splice(index, 1) + }, + + error => this.notificationsService.error('Error', error.text) + ) + } + ) + } } diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.scss b/client/src/app/videos/video-list/shared/video-miniature.component.scss index 658d7af9d..37e84897b 100644 --- a/client/src/app/videos/video-list/shared/video-miniature.component.scss +++ b/client/src/app/videos/video-list/shared/video-miniature.component.scss @@ -37,7 +37,7 @@ } .video-miniature-account { - font-size: 12px; + font-size: 13px; color: #585858; } } diff --git a/client/src/assets/images/account/delete.svg b/client/src/assets/images/account/delete.svg new file mode 100644 index 000000000..67e9e2ce7 --- /dev/null +++ b/client/src/assets/images/account/delete.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + -- cgit v1.2.3 From 7d763d97497df1bbf7a01f61aa916d99a1338a33 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 6 Dec 2017 14:09:39 +0100 Subject: Add hover effect to buttons --- client/src/app/account/account-videos/account-videos.component.scss | 4 ++++ client/src/sass/_mixins.scss | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'client') diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index 7ac25afc3..e7fe662b1 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss @@ -33,6 +33,10 @@ color: #585858; background-color: #E5E5E5; + &:hover { + background-color: #EFEFEF; + } + &.action-button-delete { margin-right: 10px; } diff --git a/client/src/sass/_mixins.scss b/client/src/sass/_mixins.scss index e44cf064d..7f1063414 100644 --- a/client/src/sass/_mixins.scss +++ b/client/src/sass/_mixins.scss @@ -31,6 +31,10 @@ background-color: $orange-color; padding: 0 17px 0 13px; cursor: pointer; + + &:hover { + background-color: #F97D46; + } } @mixin peertube-button-link { -- cgit v1.2.3 From ce0e281d46a7b574dcccb47958743656532bd312 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 6 Dec 2017 15:07:17 +0100 Subject: Client bulk delete --- .../account-videos/account-videos.component.html | 36 ++++++-- .../account-videos/account-videos.component.scss | 102 +++++++++++++-------- .../account-videos/account-videos.component.ts | 49 +++++++++- client/src/assets/images/account/delete-grey.svg | 14 +++ client/src/assets/images/account/delete-white.svg | 14 +++ client/src/assets/images/account/delete.svg | 14 --- client/src/sass/_mixins.scss | 4 +- client/src/sass/_variables.scss | 1 + 8 files changed, 171 insertions(+), 63 deletions(-) create mode 100644 client/src/assets/images/account/delete-grey.svg create mode 100644 client/src/assets/images/account/delete-white.svg delete mode 100644 client/src/assets/images/account/delete.svg (limited to 'client') diff --git a/client/src/app/account/account-videos/account-videos.component.html b/client/src/app/account/account-videos/account-videos.component.html index 30db69429..030c2f19c 100644 --- a/client/src/app/account/account-videos/account-videos.component.html +++ b/client/src/app/account/account-videos/account-videos.component.html @@ -5,7 +5,9 @@ (scrolled)="onNearOfBottom()" (scrolledUp)="onNearOfTop()" > -
+
+ +
@@ -13,14 +15,30 @@ {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views
- - - Delete - + +
+
+ + Cancel + + + + + Delete + +
+
+ + + + + Delete + - - - Edit - + + + Edit + +
diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index e7fe662b1..e76e3f4e5 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss @@ -1,8 +1,74 @@ +.action-selection-mode { + width: 174px; + + .action-selection-mode-child { + position: fixed; + } +} + +.action-button { + @include peertube-button-link; + + font-size: 15px; + font-weight: $font-semibold; + color: #585858; + background-color: #E5E5E5; + + &:hover { + background-color: #EFEFEF; + } + + &.action-button-delete { + margin-right: 10px; + } + + &.action-button-delete-selection { + background-color: $orange-color; + color: #fff; + + &:hover { + background-color: $orange-hoover-color; + } + } + + .icon { + display: inline-block; + background-repeat: no-repeat; + background-size: contain; + width: 21px; + height: 21px; + vertical-align: middle; + position: relative; + top: -2px; + + &.icon-edit { + background-image: url('../../../assets/images/account/edit.svg'); + } + + &.icon-delete-grey { + background-image: url('../../../assets/images/account/delete-grey.svg'); + } + + &.icon-delete-white { + background-image: url('../../../assets/images/account/delete-white.svg'); + } + } +} + .video { display: flex; height: 130px; padding-bottom: 20px; + input[type=checkbox] { + margin-right: 20px; + outline: 0; + } + + &:first-child { + margin-top: 47px; + } + &:not(:last-child) { margin-bottom: 20px; border-bottom: 1px solid #C6C6C6; @@ -24,40 +90,4 @@ font-size: 13px; } } - - .action-button { - @include peertube-button-link; - - font-size: 15px; - font-weight: $font-semibold; - color: #585858; - background-color: #E5E5E5; - - &:hover { - background-color: #EFEFEF; - } - - &.action-button-delete { - margin-right: 10px; - } - - .icon.icon-edit, .icon.icon-delete { - display: inline-block; - background-repeat: no-repeat; - background-size: contain; - width: 21px; - height: 21px; - vertical-align: middle; - position: relative; - top: -2px; - - &.icon-edit { - background-image: url('../../../assets/images/account/edit.svg'); - } - - &.icon-delete { - background-image: url('../../../assets/images/account/delete.svg'); - } - } - } } diff --git a/client/src/app/account/account-videos/account-videos.component.ts b/client/src/app/account/account-videos/account-videos.component.ts index 9c2cc2404..5f12cfce0 100644 --- a/client/src/app/account/account-videos/account-videos.component.ts +++ b/client/src/app/account/account-videos/account-videos.component.ts @@ -1,6 +1,9 @@ import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' +import 'rxjs/add/observable/from' +import 'rxjs/add/operator/concatAll' +import { Observable } from 'rxjs/Observable' import { ConfirmService } from '../../core/confirm' import { AbstractVideoList } from '../../shared/video/abstract-video-list' import { Video } from '../../shared/video/video.model' @@ -14,6 +17,7 @@ import { VideoService } from '../../shared/video/video.service' export class AccountVideosComponent extends AbstractVideoList implements OnInit { titlePage = 'My videos' currentRoute = '/account/videos' + checkedVideos: { [ id: number ]: boolean } = {} constructor (protected router: Router, protected route: ActivatedRoute, @@ -27,10 +31,47 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit super.ngOnInit() } + abortSelectionMode () { + this.checkedVideos = {} + } + + isInSelectionMode () { + return Object.keys(this.checkedVideos).some(k => this.checkedVideos[k] === true) + } + getVideosObservable () { return this.videoService.getMyVideos(this.pagination, this.sort) } + deleteSelectedVideos () { + const toDeleteVideosIds = Object.keys(this.checkedVideos) + .filter(k => this.checkedVideos[k] === true) + .map(k => parseInt(k, 10)) + + this.confirmService.confirm(`Do you really want to delete ${toDeleteVideosIds.length} videos?`, 'Delete').subscribe( + res => { + if (res === false) return + + const observables: Observable[] = [] + for (const videoId of toDeleteVideosIds) { + const o = this.videoService + .removeVideo(videoId) + .do(() => this.spliceVideosById(videoId)) + + observables.push(o) + } + + Observable.from(observables) + .concatAll() + .subscribe( + res => this.notificationsService.success('Success', `${toDeleteVideosIds.length} videos deleted.`), + + err => this.notificationsService.error('Error', err.text) + ) + } + ) + } + deleteVideo (video: Video) { this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete').subscribe( res => { @@ -40,8 +81,7 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit .subscribe( status => { this.notificationsService.success('Success', `Video ${video.name} deleted.`) - const index = this.videos.findIndex(v => v.id === video.id) - this.videos.splice(index, 1) + this.spliceVideosById(video.id) }, error => this.notificationsService.error('Error', error.text) @@ -49,4 +89,9 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit } ) } + + private spliceVideosById (id: number) { + const index = this.videos.findIndex(v => v.id === id) + this.videos.splice(index, 1) + } } diff --git a/client/src/assets/images/account/delete-grey.svg b/client/src/assets/images/account/delete-grey.svg new file mode 100644 index 000000000..67e9e2ce7 --- /dev/null +++ b/client/src/assets/images/account/delete-grey.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/assets/images/account/delete-white.svg b/client/src/assets/images/account/delete-white.svg new file mode 100644 index 000000000..9c52de557 --- /dev/null +++ b/client/src/assets/images/account/delete-white.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/assets/images/account/delete.svg b/client/src/assets/images/account/delete.svg deleted file mode 100644 index 67e9e2ce7..000000000 --- a/client/src/assets/images/account/delete.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/client/src/sass/_mixins.scss b/client/src/sass/_mixins.scss index 7f1063414..6a18f7a76 100644 --- a/client/src/sass/_mixins.scss +++ b/client/src/sass/_mixins.scss @@ -33,15 +33,15 @@ cursor: pointer; &:hover { - background-color: #F97D46; + background-color: $orange-hoover-color; } } @mixin peertube-button-link { display: inline-block; - @include peertube-button; @include disable-default-a-behaviour; + @include peertube-button; } @mixin avatar ($size) { diff --git a/client/src/sass/_variables.scss b/client/src/sass/_variables.scss index 81dafdc19..cc1cee75b 100644 --- a/client/src/sass/_variables.scss +++ b/client/src/sass/_variables.scss @@ -4,6 +4,7 @@ $font-bold: 700; $grey-color: #555; $orange-color: #F1680D; +$orange-hoover-color: #F97D46; $black-background: #000; $grey-background: #f6f2f2; -- cgit v1.2.3 From b1fa3eba70dbd7d9e5b795ad251e293c88ebeee2 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 6 Dec 2017 17:15:59 +0100 Subject: Begin video watch design --- client/src/app/shared/account/account.model.ts | 20 ++ client/src/app/shared/shared.module.ts | 3 + client/src/app/shared/users/user.model.ts | 6 +- .../src/app/shared/video/abstract-video-list.html | 2 +- client/src/app/shared/video/video-details.model.ts | 9 +- .../shared/video/video-miniature.component.html | 17 ++ .../shared/video/video-miniature.component.scss | 44 +++ .../app/shared/video/video-miniature.component.ts | 17 ++ .../src/app/shared/video/video-pagination.model.ts | 2 +- client/src/app/shared/video/video.model.ts | 8 +- .../videos/+video-watch/video-watch.component.html | 233 +++++++--------- .../videos/+video-watch/video-watch.component.scss | 308 ++++++--------------- .../videos/+video-watch/video-watch.component.ts | 43 ++- client/src/app/videos/video-list/index.ts | 1 - client/src/app/videos/video-list/shared/index.ts | 1 - .../shared/video-miniature.component.html | 17 -- .../shared/video-miniature.component.scss | 44 --- .../video-list/shared/video-miniature.component.ts | 19 -- client/src/app/videos/videos.module.ts | 3 +- client/src/assets/images/video/dislike.svg | 14 + client/src/assets/images/video/like.svg | 15 + client/src/assets/images/video/more.svg | 11 + client/src/assets/images/video/share.svg | 16 ++ 23 files changed, 376 insertions(+), 477 deletions(-) create mode 100644 client/src/app/shared/account/account.model.ts create mode 100644 client/src/app/shared/video/video-miniature.component.html create mode 100644 client/src/app/shared/video/video-miniature.component.scss create mode 100644 client/src/app/shared/video/video-miniature.component.ts delete mode 100644 client/src/app/videos/video-list/shared/index.ts delete mode 100644 client/src/app/videos/video-list/shared/video-miniature.component.html delete mode 100644 client/src/app/videos/video-list/shared/video-miniature.component.scss delete mode 100644 client/src/app/videos/video-list/shared/video-miniature.component.ts create mode 100644 client/src/assets/images/video/dislike.svg create mode 100644 client/src/assets/images/video/like.svg create mode 100644 client/src/assets/images/video/more.svg create mode 100644 client/src/assets/images/video/share.svg (limited to 'client') diff --git a/client/src/app/shared/account/account.model.ts b/client/src/app/shared/account/account.model.ts new file mode 100644 index 000000000..0b008188a --- /dev/null +++ b/client/src/app/shared/account/account.model.ts @@ -0,0 +1,20 @@ +import { Account as ServerAccount } from '../../../../../shared/models/accounts/account.model' +import { Avatar } from '../../../../../shared/models/avatars/avatar.model' + +export class Account implements ServerAccount { + id: number + uuid: string + name: string + host: string + followingCount: number + followersCount: number + createdAt: Date + updatedAt: Date + avatar: Avatar + + static GET_ACCOUNT_AVATAR_PATH (account: Account) { + if (account && account.avatar) return account.avatar.path + + return API_URL + '/client/assets/images/default-avatar.png' + } +} diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 86e1a380e..bd9aee345 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -20,6 +20,7 @@ import { RestExtractor, RestService } from './rest' import { UserService } from './users' import { VideoAbuseService } from './video-abuse' import { VideoBlacklistService } from './video-blacklist' +import { VideoMiniatureComponent } from './video/video-miniature.component' import { VideoThumbnailComponent } from './video/video-thumbnail.component' import { VideoService } from './video/video.service' @@ -44,6 +45,7 @@ import { VideoService } from './video/video.service' declarations: [ LoaderComponent, VideoThumbnailComponent, + VideoMiniatureComponent, NumberFormatterPipe, FromNowPipe ], @@ -66,6 +68,7 @@ import { VideoService } from './video/video.service' LoaderComponent, VideoThumbnailComponent, + VideoMiniatureComponent, NumberFormatterPipe, FromNowPipe diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts index b1c323114..b4d13f37c 100644 --- a/client/src/app/shared/users/user.model.ts +++ b/client/src/app/shared/users/user.model.ts @@ -1,5 +1,5 @@ import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared' -import { Account } from '../../../../../shared/models/accounts' +import { Account } from '../account/account.model' export type UserConstructorHash = { id: number, @@ -52,8 +52,6 @@ export class User implements UserServerModel { } getAvatarPath () { - if (this.account && this.account.avatar) return this.account.avatar.path - - return API_URL + '/client/assets/images/default-avatar.png' + return Account.GET_ACCOUNT_AVATAR_PATH(this.account) } } diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html index bd4f6b1f8..5d07a276b 100644 --- a/client/src/app/shared/video/abstract-video-list.html +++ b/client/src/app/shared/video/abstract-video-list.html @@ -12,7 +12,7 @@ >
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts index 93c380b73..1a956da7c 100644 --- a/client/src/app/shared/video/video-details.model.ts +++ b/client/src/app/shared/video/video-details.model.ts @@ -1,3 +1,4 @@ +import { Account } from '../../../../../shared/models/accounts' import { Video } from '../../shared/video/video.model' import { AuthUser } from '../../core' import { @@ -10,7 +11,7 @@ import { } from '../../../../../shared' export class VideoDetails extends Video implements VideoDetailsServerModel { - account: string + accountName: string by: string createdAt: Date updatedAt: Date @@ -44,6 +45,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { channel: VideoChannel privacy: VideoPrivacy privacyLabel: string + account: Account constructor (hash: VideoDetailsServerModel) { super(hash) @@ -53,6 +55,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { this.descriptionPath = hash.descriptionPath this.files = hash.files this.channel = hash.channel + this.account = hash.account } getAppropriateMagnetUri (actualDownloadSpeed = 0) { @@ -71,7 +74,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { } isRemovableBy (user: AuthUser) { - return user && this.isLocal === true && (this.account === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO)) + return user && this.isLocal === true && (this.accountName === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO)) } isBlackistableBy (user: AuthUser) { @@ -79,6 +82,6 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { } isUpdatableBy (user: AuthUser) { - return user && this.isLocal === true && user.username === this.account + return user && this.isLocal === true && user.username === this.accountName } } diff --git a/client/src/app/shared/video/video-miniature.component.html b/client/src/app/shared/video/video-miniature.component.html new file mode 100644 index 000000000..7ac017235 --- /dev/null +++ b/client/src/app/shared/video/video-miniature.component.html @@ -0,0 +1,17 @@ +
+ + +
+ + + {{ video.name }} + + + + {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views + +
+
diff --git a/client/src/app/shared/video/video-miniature.component.scss b/client/src/app/shared/video/video-miniature.component.scss new file mode 100644 index 000000000..37e84897b --- /dev/null +++ b/client/src/app/shared/video/video-miniature.component.scss @@ -0,0 +1,44 @@ +.video-miniature { + display: inline-block; + padding-right: 15px; + margin-bottom: 30px; + height: 175px; + vertical-align: top; + + .video-miniature-information { + width: 200px; + margin-top: 2px; + line-height: normal; + + .video-miniature-name { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: bold; + transition: color 0.2s; + font-size: 16px; + font-weight: $font-semibold; + color: #000; + + &:hover { + text-decoration: none; + } + + &.blur-filter { + filter: blur(3px); + padding-left: 4px; + } + } + + .video-miniature-created-at-views { + display: block; + font-size: 13px; + } + + .video-miniature-account { + font-size: 13px; + color: #585858; + } + } +} diff --git a/client/src/app/shared/video/video-miniature.component.ts b/client/src/app/shared/video/video-miniature.component.ts new file mode 100644 index 000000000..4d79a74bb --- /dev/null +++ b/client/src/app/shared/video/video-miniature.component.ts @@ -0,0 +1,17 @@ +import { Component, Input } from '@angular/core' +import { User } from '../users' +import { Video } from './video.model' + +@Component({ + selector: 'my-video-miniature', + styleUrls: [ './video-miniature.component.scss' ], + templateUrl: './video-miniature.component.html' +}) +export class VideoMiniatureComponent { + @Input() user: User + @Input() video: Video + + isVideoNSFWForThisUser () { + return this.video.isVideoNSFWForUser(this.user) + } +} diff --git a/client/src/app/shared/video/video-pagination.model.ts b/client/src/app/shared/video/video-pagination.model.ts index 9e71769cb..e9db61596 100644 --- a/client/src/app/shared/video/video-pagination.model.ts +++ b/client/src/app/shared/video/video-pagination.model.ts @@ -1,5 +1,5 @@ export interface VideoPagination { currentPage: number itemsPerPage: number - totalItems: number + totalItems?: number } diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index 6929c8755..d86ef8f92 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts @@ -1,8 +1,9 @@ import { Video as VideoServerModel } from '../../../../../shared' import { User } from '../' +import { Account } from '../../../../../shared/models/accounts' export class Video implements VideoServerModel { - account: string + accountName: string by: string createdAt: Date updatedAt: Date @@ -31,6 +32,7 @@ export class Video implements VideoServerModel { likes: number dislikes: number nsfw: boolean + account: Account private static createByString (account: string, serverHost: string) { return account + '@' + serverHost @@ -52,7 +54,7 @@ export class Video implements VideoServerModel { absoluteAPIUrl = window.location.origin } - this.account = hash.account + this.accountName = hash.accountName this.createdAt = new Date(hash.createdAt.toString()) this.categoryLabel = hash.categoryLabel this.category = hash.category @@ -80,7 +82,7 @@ export class Video implements VideoServerModel { this.dislikes = hash.dislikes this.nsfw = hash.nsfw - this.by = Video.createByString(hash.account, hash.serverHost) + this.by = Video.createByString(hash.accountName, hash.serverHost) } isVideoNSFWForUser (user: User) { diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index aa1f2f77e..f31e82bff 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -1,18 +1,3 @@ -
-
- The video load seems to be abnormally long. - -
-
-
@@ -23,167 +8,153 @@
-
-
-
- {{ video.name }} -
- -
- {{ video.views}} views -
-
+
+
+
+
{{ video.name }}
+ +
+
+ +
-
- +
+ +
- +
+ + Share +
- -
-
- - - - {{ video.likes }} - -
- -
- +
+ {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views +
- - {{ video.dislikes }} - -
+
+ {{ video.channel.name }} +
-
-
-
-
- Published on {{ video.createdAt | date:'short' }} -
+
+ By {{ video.by }} + Account avatar +
-
+
+
-
+
Show more
-
+
Show less
-
-
- - Privacy: +
+
+ + Privacy - + {{ video.privacyLabel }}
-
- - Category: +
+ + Category - + {{ video.categoryLabel }}
-
- - Licence: +
+ + Licence - + {{ video.licenceLabel }}
-
- - Language: +
+ + Language - + {{ video.languageLabel }}
-
- - Tags: +
+ + Tags - + + {{ getVideoTags() }} +
+
+ +
+
+
+
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index 06c2de7c6..7bcfeb7c3 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -17,167 +17,108 @@ font-weight: bold; } -#torrent-info { - font-size: 10px; - margin-top: 10px; - text-align: center; - - div { - min-width: 60px; - } -} - -#video-info { - .video-name-views { - font-weight: bold; - font-size: 18px; - min-height: $video-watch-title-height; - display: flex; - align-items: center; - - .video-name { - padding-left: $video-watch-info-padding-left; - } +.video-bottom { + margin-top: 40px; + display: flex; - .video-views { - text-align: right; - // Keep a symmetry with the video name - padding-right: $video-watch-info-padding-left - } + .video-info { + flex-grow: 1; + margin-right: 28px; - } + .video-info-name-actions { + display: flex; + align-items: center; - .video-small-blocks { - height: $video-watch-info-height; - color: $video-watch-info-color; - border-color: $video-watch-border-color; - border-width: 1px 0px; - border-style: solid; + .video-info-name { + font-size: 27px; + font-weight: $font-semibold; + flex-grow: 1; + } - .video-small-block { - height: $video-watch-info-height; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; + .video-info-actions { + .action-button { + @include peertube-button; - a { - cursor: pointer; - transition: color 0.3s; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + font-size: 15px; + font-weight: $font-semibold; + color: #585858; + background-color: #E5E5E5; + display: inline-block; + padding: 0 10px 0 10px; - &, &:hover { - color: inherit; - text-decoration:none; + &:hover { + background-color: #EFEFEF; + } } - &:hover { - color: #000 !important; + .action-more { + display: inline-block; } - &:hover > .glyphicon { - opacity: 1 !important; - } - } + .icon { + display: inline-block; + background-repeat: no-repeat; + background-size: contain; + width: 21px; + height: 21px; + vertical-align: middle; + position: relative; + top: -2px; - .option .glyphicon { - font-size: 22px; - color: inherit; - opacity: 0.15; - margin-bottom: 10px; - transition: opacity 0.3s; - } + &.icon-like { + background-image: url('../../../assets/images/video/like.svg'); + } - .video-small-block-text { - font-size: 15px; - font-weight: bold; - } - } + &.icon-dislike { + background-image: url('../../../assets/images/video/dislike.svg'); + } - .video-small-block:not(:last-child) { - border-width: 0 1px 0 0; - border-color: $video-watch-border-color; - border-style: solid; - } + &.icon-share { + background-image: url('../../../assets/images/video/share.svg'); + } - .video-small-block-account, .video-small-block-more { - a.option { - display: block; - - .glyphicon { - display: block; + &.icon-more { + background-image: url('../../../assets/images/video/more.svg'); + } } } } - .video-small-block-share, .video-small-block-more { - a.option { - display: block; - - .glyphicon { - display: block; - } - } + .video-info-date-views { + font-size: 16px; + margin-bottom: 10px; } - .video-small-block-more .video-small-block-dropdown { - position: relative; - - .dropdown-item .glyphicon { - margin-right: 5px; - } + .video-info-channel { + font-weight: $font-semibold; + font-size: 15px; } - .video-small-block-rating { - - .video-small-block-like { - margin-bottom: 10px; - } - - .video-small-block-text { - vertical-align: top; - } - - .glyphicon { - font-size: 18px; - margin: 0 10px 0 0; - opacity: 0.3; - } - - .interactive { - cursor: pointer; - transition: opacity, color 0.3s; + .video-info-by { + display: flex; + align-items: center; + font-size: 13px; - &.activated, &:hover { - opacity: 1; - color: #000; - } + img { + width: 16px; + height: 16px; + margin-left: 3px; } } - } - - .video-details { - margin-top: 30px; - .video-details-date-description { - padding-left: $video-watch-info-padding-left; + .video-info-description { + margin: 20px 0; + font-size: 15px; .description-loading { display: inline-block; } - .video-details-date { - font-weight: bold; - margin-bottom: 30px; - } - - .video-details-description-more { + .video-info-description-more { cursor: pointer; - margin-top: 15px; - font-weight: bold; - color: #acaeb7; + font-weight: $font-semibold; + color: #585858; + font-size: 14px; .glyphicon { position: relative; @@ -186,109 +127,20 @@ } } - .video-details-attributes { - font-weight: bold; - font-size: 12px; - - .video-details-attribute { - display: flex; - - .video-details-attribute-label { - color: $video-watch-info-color; - flex-basis: 60px; - flex-grow: 0; - flex-shrink: 0; - margin-right: 5px; - } - } - } - - .video-details-tags { - display: flex; - flex-wrap: wrap; - - a { - margin: 0 3px 3px 0; - font-size: 11px; - } - } - } - - @media screen and (max-width: 800px) { - .video-name-views { - .video-name { - padding-left: 5px; - padding-right: 0px; - } - - .video-views { - padding-left: 0px; - padding-right: 5px; - } - } - - .video-small-blocks { - a, .video-small-block-text { - font-size: 13px !important; - } - - .glyphicon { - font-size: 18px !important; - } - - .video-small-block-account { - padding-left: 10px; - padding-right: 10px; - } - } - - .video-details { - .video-details-date-description { - padding-left: 10px; - font-size: 13px !important; - } - - .video-details-attributes { - font-size: 11px !important; + .video-attributes { + .video-attribute { + font-size: 13px; + display: block; + margin-bottom: 12px; - .video-details-attribute-label { - width: 50px; + .video-attribute-label { + width: 86px; + display: inline-block; + color: #585858; + font-weight: $font-bold; } } } - } - @media screen and (max-width: 500px) { - .video-name-views { - font-size: 16px !important; - } - - // Keep the same hierarchy than max-width: 800px - .video-small-blocks { - a, .video-small-block-text { - font-size: 10px !important; - } - - .video-small-block-account { - padding-left: 5px; - padding-right: 5px; - } - } - - .video-details { - .video-details-date-description { - margin-bottom: 30px; - width: 100%; - - .video-details-date { - margin-bottom: 15px; - } - } - - .video-details-attributes { - padding-left: 10px; - padding-right: 10px; - } - } } } 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 48842602e..3c6951403 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -10,6 +10,8 @@ import { UserVideoRateType, VideoRateType } from '../../../../../shared' import '../../../assets/player/peertube-videojs-plugin' import { AuthService, ConfirmService } from '../../core' import { VideoBlacklistService } from '../../shared' +import { Account } from '../../shared/account/account.model' +import { Video } from '../../shared/video/video.model' import { MarkdownService } from '../shared' import { VideoDownloadComponent } from './video-download.component' import { VideoReportComponent } from './video-report.component' @@ -26,6 +28,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { @ViewChild('videoShareModal') videoShareModal: VideoShareComponent @ViewChild('videoReportModal') videoReportModal: VideoReportComponent + otherVideos: Video[] = [] + error = false loading = false player: videojs.Player @@ -57,6 +61,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy { ) {} ngOnInit () { + this.videoService.getVideos({ currentPage: 1, itemsPerPage: 5 }, '-createdAt') + .subscribe( + data => this.otherVideos = data.videos, + + err => console.error(err) + ) + this.paramsSub = this.route.params.subscribe(routeParams => { let uuid = routeParams['uuid'] this.videoService.getVideo(uuid).subscribe( @@ -114,27 +125,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { ) } - removeVideo (event: Event) { - event.preventDefault() - - this.confirmService.confirm('Do you really want to delete this video?', 'Delete').subscribe( - res => { - if (res === false) return - - this.videoService.removeVideo(this.video.id) - .subscribe( - status => { - this.notificationsService.success('Success', `Video ${this.video.name} deleted.`) - // Go back to the video-list. - this.router.navigate(['/videos/list']) - }, - - error => this.notificationsService.error('Error', error.text) - ) - } - ) - } - blacklistVideo (event: Event) { event.preventDefault() @@ -165,7 +155,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { } showLessDescription () { - this.updateVideoDescription(this.shortVideoDescription) this.completeDescriptionShown = false } @@ -222,6 +211,16 @@ export class VideoWatchComponent implements OnInit, OnDestroy { return this.video.isBlackistableBy(this.authService.getUser()) } + getAvatarPath () { + return Account.GET_ACCOUNT_AVATAR_PATH(this.video.account) + } + + getVideoTags () { + if (!this.video || Array.isArray(this.video.tags) === false) return [] + + return this.video.tags.join(', ') + } + private updateVideoDescription (description: string) { this.video.description = description this.setVideoDescriptionHTML() diff --git a/client/src/app/videos/video-list/index.ts b/client/src/app/videos/video-list/index.ts index 13024294e..5e7c7886c 100644 --- a/client/src/app/videos/video-list/index.ts +++ b/client/src/app/videos/video-list/index.ts @@ -1,4 +1,3 @@ export * from './video-recently-added.component' export * from './video-trending.component' export * from './video-search.component' -export * from './shared' diff --git a/client/src/app/videos/video-list/shared/index.ts b/client/src/app/videos/video-list/shared/index.ts deleted file mode 100644 index 2778f2d9e..000000000 --- a/client/src/app/videos/video-list/shared/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './video-miniature.component' diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.html b/client/src/app/videos/video-list/shared/video-miniature.component.html deleted file mode 100644 index 7ac017235..000000000 --- a/client/src/app/videos/video-list/shared/video-miniature.component.html +++ /dev/null @@ -1,17 +0,0 @@ -
- - -
- - - {{ video.name }} - - - - {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views - -
-
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.scss b/client/src/app/videos/video-list/shared/video-miniature.component.scss deleted file mode 100644 index 37e84897b..000000000 --- a/client/src/app/videos/video-list/shared/video-miniature.component.scss +++ /dev/null @@ -1,44 +0,0 @@ -.video-miniature { - display: inline-block; - padding-right: 15px; - margin-bottom: 30px; - height: 175px; - vertical-align: top; - - .video-miniature-information { - width: 200px; - margin-top: 2px; - line-height: normal; - - .video-miniature-name { - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-weight: bold; - transition: color 0.2s; - font-size: 16px; - font-weight: $font-semibold; - color: #000; - - &:hover { - text-decoration: none; - } - - &.blur-filter { - filter: blur(3px); - padding-left: 4px; - } - } - - .video-miniature-created-at-views { - display: block; - font-size: 13px; - } - - .video-miniature-account { - font-size: 13px; - color: #585858; - } - } -} diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.ts b/client/src/app/videos/video-list/shared/video-miniature.component.ts deleted file mode 100644 index e8fc8e911..000000000 --- a/client/src/app/videos/video-list/shared/video-miniature.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Component, Input } from '@angular/core' -import { User } from '../../../shared' -import { SortField } from '../../../shared/video/sort-field.type' -import { Video } from '../../../shared/video/video.model' - -@Component({ - selector: 'my-video-miniature', - styleUrls: [ './video-miniature.component.scss' ], - templateUrl: './video-miniature.component.html' -}) -export class VideoMiniatureComponent { - @Input() currentSort: SortField - @Input() user: User - @Input() video: Video - - isVideoNSFWForThisUser () { - return this.video.isVideoNSFWForUser(this.user) - } -} diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts index 8c8d52ad9..4b14d1da8 100644 --- a/client/src/app/videos/videos.module.ts +++ b/client/src/app/videos/videos.module.ts @@ -1,6 +1,6 @@ import { NgModule } from '@angular/core' import { SharedModule } from '../shared' -import { VideoMiniatureComponent, VideoSearchComponent } from './video-list' +import { VideoSearchComponent } from './video-list' import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' import { VideoTrendingComponent } from './video-list/video-trending.component' import { VideosRoutingModule } from './videos-routing.module' @@ -17,7 +17,6 @@ import { VideosComponent } from './videos.component' VideoTrendingComponent, VideoRecentlyAddedComponent, - VideoMiniatureComponent, VideoSearchComponent ], diff --git a/client/src/assets/images/video/dislike.svg b/client/src/assets/images/video/dislike.svg new file mode 100644 index 000000000..56a7908fb --- /dev/null +++ b/client/src/assets/images/video/dislike.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/assets/images/video/like.svg b/client/src/assets/images/video/like.svg new file mode 100644 index 000000000..5ef6c7b31 --- /dev/null +++ b/client/src/assets/images/video/like.svg @@ -0,0 +1,15 @@ + + + + thumbs-up + Created with Sketch. + + + + + + + + + + diff --git a/client/src/assets/images/video/more.svg b/client/src/assets/images/video/more.svg new file mode 100644 index 000000000..dea392136 --- /dev/null +++ b/client/src/assets/images/video/more.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/client/src/assets/images/video/share.svg b/client/src/assets/images/video/share.svg new file mode 100644 index 000000000..da0f43e81 --- /dev/null +++ b/client/src/assets/images/video/share.svg @@ -0,0 +1,16 @@ + + + + share + Created with Sketch. + + + + + + + + + + + -- cgit v1.2.3 From 6a9e1d42f878c55ac5e2af8a1c98e6fe28a04f36 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 6 Dec 2017 18:04:40 +0100 Subject: Add likes/dislikes bar --- client/src/app/shared/video/video-details.model.ts | 5 +++++ .../videos/+video-watch/video-watch.component.html | 10 ++++++++-- .../videos/+video-watch/video-watch.component.scss | 23 +++++++++++++++++++--- 3 files changed, 33 insertions(+), 5 deletions(-) (limited to 'client') diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts index 1a956da7c..b96f8f6c8 100644 --- a/client/src/app/shared/video/video-details.model.ts +++ b/client/src/app/shared/video/video-details.model.ts @@ -46,6 +46,8 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { privacy: VideoPrivacy privacyLabel: string account: Account + likesPercent: number + dislikesPercent: number constructor (hash: VideoDetailsServerModel) { super(hash) @@ -56,6 +58,9 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { this.files = hash.files this.channel = hash.channel this.account = hash.account + + this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100 + this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100 } getAppropriateMagnetUri (actualDownloadSpeed = 0) { diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index f31e82bff..b17392ff1 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -73,8 +73,14 @@
-
- {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views +
+
+ {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views +
+ +
+ +
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index 7bcfeb7c3..5064ceb95 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -84,9 +84,26 @@ } } - .video-info-date-views { - font-size: 16px; - margin-bottom: 10px; + .video-info-date-views-bar { + display: flex; + + .video-info-date-views { + font-size: 16px; + margin-bottom: 10px; + flex-grow: 1; + } + + .video-info-likes-dislikes-bar { + height: 5px; + width: 186px; + background-color: #E5E5E5; + margin-top: 25px; + + .likes-bar { + height: 100%; + background-color: #39CC0B; + } + } } .video-info-channel { -- cgit v1.2.3 From 41c3dfac99c41d2daec2a55554bb517ed5d59fc4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 6 Dec 2017 18:10:57 +0100 Subject: Design other videos in watch video page --- .../app/videos/+video-watch/video-watch.component.html | 4 ++++ .../app/videos/+video-watch/video-watch.component.scss | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'client') diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index b17392ff1..88d89f9e4 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -159,6 +159,10 @@
+
+ Other videos +
+
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index 5064ceb95..d7f47ed75 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -158,6 +158,22 @@ } } } + } + + .other-videos { + .title-page { + margin-top: 0; + } + + /deep/ .video-miniature { + display: flex; + height: 100%; + margin-bottom: 20px; + .video-miniature-information { + margin-left: 10px; + } + } } } + -- cgit v1.2.3 From 7b272fd73f1ea67e83c1924f2cc33503b8759811 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 7 Dec 2017 10:02:01 +0100 Subject: Fix dropdown menu in video watch --- .../account-change-password.component.scss | 2 + .../account-details/account-details.component.scss | 1 + .../account-videos/account-videos.component.scss | 2 +- client/src/app/header/header.component.scss | 5 +- client/src/app/login/login.component.scss | 1 + client/src/app/signup/signup.component.scss | 1 + .../videos/+video-watch/video-watch.component.html | 34 ++------ .../videos/+video-watch/video-watch.component.scss | 92 +++++++++++++++------- client/src/assets/images/account/edit.svg | 15 ---- client/src/assets/images/global/edit.svg | 15 ++++ client/src/assets/images/video/alert.svg | 16 ++++ client/src/assets/images/video/dislike-grey.svg | 14 ++++ client/src/assets/images/video/dislike-white.svg | 14 ++++ client/src/assets/images/video/dislike.svg | 14 ---- client/src/assets/images/video/download.svg | 16 ++++ client/src/assets/images/video/eye-closed.svg | 18 +++++ client/src/assets/images/video/like-grey.svg | 15 ++++ client/src/assets/images/video/like-white.svg | 15 ++++ client/src/assets/images/video/like.svg | 15 ---- client/src/sass/_mixins.scss | 24 ++++-- client/src/sass/_variables.scss | 12 +-- client/src/sass/application.scss | 14 ++++ 22 files changed, 238 insertions(+), 117 deletions(-) delete mode 100644 client/src/assets/images/account/edit.svg create mode 100644 client/src/assets/images/global/edit.svg create mode 100644 client/src/assets/images/video/alert.svg create mode 100644 client/src/assets/images/video/dislike-grey.svg create mode 100644 client/src/assets/images/video/dislike-white.svg delete mode 100644 client/src/assets/images/video/dislike.svg create mode 100644 client/src/assets/images/video/download.svg create mode 100644 client/src/assets/images/video/eye-closed.svg create mode 100644 client/src/assets/images/video/like-grey.svg create mode 100644 client/src/assets/images/video/like-white.svg delete mode 100644 client/src/assets/images/video/like.svg (limited to 'client') diff --git a/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss b/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss index 75827abbf..7a4fdb34d 100644 --- a/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss +++ b/client/src/app/account/account-settings/account-change-password/account-change-password.component.scss @@ -9,6 +9,8 @@ input[type=password] { input[type=submit] { @include peertube-button; + @include orange-button; + margin-top: 15px; } diff --git a/client/src/app/account/account-settings/account-details/account-details.component.scss b/client/src/app/account/account-settings/account-details/account-details.component.scss index 687166d9e..5c369f968 100644 --- a/client/src/app/account/account-settings/account-details/account-details.component.scss +++ b/client/src/app/account/account-settings/account-details/account-details.component.scss @@ -6,6 +6,7 @@ label { input[type=submit] { @include peertube-button; + @include orange-button; display: block; margin-top: 15px; diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index e76e3f4e5..04aaa8e89 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss @@ -42,7 +42,7 @@ top: -2px; &.icon-edit { - background-image: url('../../../assets/images/account/edit.svg'); + background-image: url('../../../assets/images/global/edit.svg'); } &.icon-delete-grey { diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss index 7ba8ef26c..e7761a9df 100644 --- a/client/src/app/header/header.component.scss +++ b/client/src/app/header/header.component.scss @@ -10,7 +10,7 @@ .icon.icon-search { display: inline-block; - background: url('../../../assets/images/header/search.svg') no-repeat; + background: url('../../assets/images/header/search.svg') no-repeat; background-size: contain; width: 25px; height: 21px; @@ -24,12 +24,13 @@ .upload-button { @include peertube-button-link; + @include orange-button; margin-right: 25px; .icon.icon-upload { display: inline-block; - background: url('../../../assets/images/header/upload.svg') no-repeat; + background: url('../../assets/images/header/upload.svg') no-repeat; background-size: contain; width: 22px; height: 24px; diff --git a/client/src/app/login/login.component.scss b/client/src/app/login/login.component.scss index fd6981c59..3b4326de4 100644 --- a/client/src/app/login/login.component.scss +++ b/client/src/app/login/login.component.scss @@ -5,4 +5,5 @@ input:not([type=submit]) { input[type=submit] { @include peertube-button; + @include orange-button; } diff --git a/client/src/app/signup/signup.component.scss b/client/src/app/signup/signup.component.scss index fd6981c59..3b4326de4 100644 --- a/client/src/app/signup/signup.component.scss +++ b/client/src/app/signup/signup.component.scss @@ -5,4 +5,5 @@ input:not([type=submit]) { input[type=submit] { @include peertube-button; + @include orange-button; } diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index 88d89f9e4..583da4685 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -14,18 +14,12 @@
{{ video.name }}
-
- +
+
-
- +
+
@@ -39,33 +33,21 @@
@@ -78,7 +60,7 @@ {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views
-
+
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index d7f47ed75..3f36410f4 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -6,6 +6,11 @@ #video-element { width: 888px; height: 500px; + + // VideoJS create an inner video player + video { + outline: 0; + } } } @@ -38,47 +43,78 @@ .video-info-actions { .action-button { @include peertube-button; + @include grey-button; font-size: 15px; font-weight: $font-semibold; - color: #585858; - background-color: #E5E5E5; display: inline-block; padding: 0 10px 0 10px; - &:hover { - background-color: #EFEFEF; + .icon { + display: inline-block; + background-repeat: no-repeat; + background-size: contain; + width: 21px; + height: 21px; + vertical-align: middle; + position: relative; + top: -2px; + + &.icon-like { + background-image: url('../../../assets/images/video/like-grey.svg'); + } + + &.icon-dislike { + background-image: url('../../../assets/images/video/dislike-grey.svg'); + } + + &.icon-share { + background-image: url('../../../assets/images/video/share.svg'); + } + + &.icon-more { + background-image: url('../../../assets/images/video/more.svg'); + } } - } - .action-more { - display: inline-block; - } + &.activated { + @include orange-button; - .icon { - display: inline-block; - background-repeat: no-repeat; - background-size: contain; - width: 21px; - height: 21px; - vertical-align: middle; - position: relative; - top: -2px; - - &.icon-like { - background-image: url('../../../assets/images/video/like.svg'); - } + .icon-like { + background-image: url('../../../assets/images/video/like-white.svg'); + } - &.icon-dislike { - background-image: url('../../../assets/images/video/dislike.svg'); + .icon-dislike { + background-image: url('../../../assets/images/video/dislike-white.svg'); + } } + } - &.icon-share { - background-image: url('../../../assets/images/video/share.svg'); - } + .action-more { + display: inline-block; - &.icon-more { - background-image: url('../../../assets/images/video/more.svg'); + .dropdown-menu .icon { + display: inline-block; + background-repeat: no-repeat; + background-size: contain; + width: 21px; + height: 21px; + vertical-align: middle; + margin-right: 5px; + position: relative; + top: -1px; + + &.icon-download { + background-image: url('../../../assets/images/video/download.svg'); + } + + &.icon-alert { + background-image: url('../../../assets/images/video/alert.svg'); + } + + &.icon-blacklist { + background-image: url('../../../assets/images/video/eye-closed.svg'); + } } } } diff --git a/client/src/assets/images/account/edit.svg b/client/src/assets/images/account/edit.svg deleted file mode 100644 index 23ece68f1..000000000 --- a/client/src/assets/images/account/edit.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - edit - Created with Sketch. - - - - - - - - - - diff --git a/client/src/assets/images/global/edit.svg b/client/src/assets/images/global/edit.svg new file mode 100644 index 000000000..23ece68f1 --- /dev/null +++ b/client/src/assets/images/global/edit.svg @@ -0,0 +1,15 @@ + + + + edit + Created with Sketch. + + + + + + + + + + diff --git a/client/src/assets/images/video/alert.svg b/client/src/assets/images/video/alert.svg new file mode 100644 index 000000000..6d3af029f --- /dev/null +++ b/client/src/assets/images/video/alert.svg @@ -0,0 +1,16 @@ + + + + alert + Created with Sketch. + + + + + + + + + + + diff --git a/client/src/assets/images/video/dislike-grey.svg b/client/src/assets/images/video/dislike-grey.svg new file mode 100644 index 000000000..56a7908fb --- /dev/null +++ b/client/src/assets/images/video/dislike-grey.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/assets/images/video/dislike-white.svg b/client/src/assets/images/video/dislike-white.svg new file mode 100644 index 000000000..cfc6eaa1f --- /dev/null +++ b/client/src/assets/images/video/dislike-white.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/assets/images/video/dislike.svg b/client/src/assets/images/video/dislike.svg deleted file mode 100644 index 56a7908fb..000000000 --- a/client/src/assets/images/video/dislike.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/client/src/assets/images/video/download.svg b/client/src/assets/images/video/download.svg new file mode 100644 index 000000000..5b0cca5ef --- /dev/null +++ b/client/src/assets/images/video/download.svg @@ -0,0 +1,16 @@ + + + + download + Created with Sketch. + + + + + + + + + + + diff --git a/client/src/assets/images/video/eye-closed.svg b/client/src/assets/images/video/eye-closed.svg new file mode 100644 index 000000000..c5b739659 --- /dev/null +++ b/client/src/assets/images/video/eye-closed.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/client/src/assets/images/video/like-grey.svg b/client/src/assets/images/video/like-grey.svg new file mode 100644 index 000000000..5ef6c7b31 --- /dev/null +++ b/client/src/assets/images/video/like-grey.svg @@ -0,0 +1,15 @@ + + + + thumbs-up + Created with Sketch. + + + + + + + + + + diff --git a/client/src/assets/images/video/like-white.svg b/client/src/assets/images/video/like-white.svg new file mode 100644 index 000000000..88e5f6a9a --- /dev/null +++ b/client/src/assets/images/video/like-white.svg @@ -0,0 +1,15 @@ + + + + thumbs-up + Created with Sketch. + + + + + + + + + + diff --git a/client/src/assets/images/video/like.svg b/client/src/assets/images/video/like.svg deleted file mode 100644 index 5ef6c7b31..000000000 --- a/client/src/assets/images/video/like.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - thumbs-up - Created with Sketch. - - - - - - - - - - diff --git a/client/src/sass/_mixins.scss b/client/src/sass/_mixins.scss index 6a18f7a76..ddc9c6766 100644 --- a/client/src/sass/_mixins.scss +++ b/client/src/sass/_mixins.scss @@ -19,22 +19,34 @@ } } +@mixin orange-button { + color: #fff; + background-color: $orange-color; + + &:hover { + background-color: $orange-hoover-color; + } +} + +@mixin grey-button { + background-color: $grey-color; + color: #585858; + + &:hover { + background-color: $grey-hoover-color; + } +} + @mixin peertube-button { border: none; - color: #fff; font-weight: $font-semibold; font-size: 15px; height: $button-height; line-height: $button-height; border-radius: 3px; text-align: center; - background-color: $orange-color; padding: 0 17px 0 13px; cursor: pointer; - - &:hover { - background-color: $orange-hoover-color; - } } @mixin peertube-button-link { diff --git a/client/src/sass/_variables.scss b/client/src/sass/_variables.scss index cc1cee75b..0d310409b 100644 --- a/client/src/sass/_variables.scss +++ b/client/src/sass/_variables.scss @@ -2,7 +2,8 @@ $font-regular: 400; $font-semibold: 600; $font-bold: 700; -$grey-color: #555; +$grey-color: #E5E5E5; +$grey-hoover-color: #EFEFEF;; $orange-color: #F1680D; $orange-hoover-color: #F97D46; @@ -17,7 +18,6 @@ $button-height: 30px; $header-height: 50px; $header-border-color: #e9eff6; - $search-input-width: 375px; $menu-color: #fff; @@ -27,11 +27,3 @@ $footer-height: 30px; $footer-margin: 30px; $footer-border-color: $header-border-color; - -$video-miniature-other-infos: #686767; - -$video-watch-border-color: #eceef4; -$video-watch-title-height: 90px; -$video-watch-info-color: #9da0ae; -$video-watch-info-height: 120px; -$video-watch-info-padding-left: 40px; diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index b860e1bf2..c81031021 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -138,3 +138,17 @@ p-datatable { } } } + +.dropdown-menu { + border-radius: 3px; + box-shadow: 0 3px 6px; + font-size: 15px; + + .dropdown-item { + padding: 3px 15px; + } + + a { + color: #000 !important; + } +} -- cgit v1.2.3 From 0727cab0dfd3d53e5e9c88bfbda6bc0e090d4f12 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 7 Dec 2017 10:27:33 +0100 Subject: Design video watch modals --- .../account-videos/account-videos.component.scss | 8 ++---- client/src/app/app.component.scss | 8 ++---- client/src/app/header/header.component.scss | 10 +++----- client/src/app/menu/menu.component.scss | 6 ++--- .../shared/forms/form-validators/video-abuse.ts | 6 ++--- .../+video-watch/video-download.component.html | 11 ++++---- .../+video-watch/video-download.component.scss | 23 +++++++++++++++++ .../+video-watch/video-download.component.ts | 2 +- .../+video-watch/video-report.component.html | 12 ++++----- .../videos/+video-watch/video-share.component.html | 2 +- .../videos/+video-watch/video-watch.component.scss | 10 +++----- client/src/assets/images/video/download-grey.svg | 16 ++++++++++++ client/src/assets/images/video/download-white.svg | 16 ++++++++++++ client/src/assets/images/video/download.svg | 16 ------------ client/src/sass/_mixins.scss | 16 ++++++++++-- client/src/sass/application.scss | 30 ++++++++++++++++++++++ 16 files changed, 129 insertions(+), 63 deletions(-) create mode 100644 client/src/app/videos/+video-watch/video-download.component.scss create mode 100644 client/src/assets/images/video/download-grey.svg create mode 100644 client/src/assets/images/video/download-white.svg delete mode 100644 client/src/assets/images/video/download.svg (limited to 'client') diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index 04aaa8e89..083918e29 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss @@ -32,12 +32,8 @@ } .icon { - display: inline-block; - background-repeat: no-repeat; - background-size: contain; - width: 21px; - height: 21px; - vertical-align: middle; + @include icon(21px); + position: relative; top: -2px; diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss index 97c5d461a..10af9debe 100644 --- a/client/src/app/app.component.scss +++ b/client/src/app/app.component.scss @@ -35,11 +35,7 @@ align-items: center; .icon { - cursor: pointer; - width: 22px; - height: 22px; - display: inline-block; - background-size: contain; + @include icon(22px); &.icon-menu { background-image: url('../assets/images/header/menu.svg'); @@ -59,7 +55,7 @@ .icon.icon-logo { display: inline-block; background: url('../assets/images/logo.svg') no-repeat; - width: 20px; + width: 23px; height: 24px; } } diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss index e7761a9df..d1c59e8d1 100644 --- a/client/src/app/header/header.component.scss +++ b/client/src/app/header/header.component.scss @@ -9,13 +9,11 @@ } .icon.icon-search { - display: inline-block; - background: url('../../assets/images/header/search.svg') no-repeat; - background-size: contain; - width: 25px; + @include icon(25px); height: 21px; - vertical-align: middle; - cursor: pointer; + + background-image: url('../../assets/images/header/search.svg'); + // yolo position: absolute; margin-left: -50px; diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index c93c59622..eda3e1a85 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss @@ -107,11 +107,9 @@ menu { @include disable-default-a-behaviour; .icon { - width: 22px; - height: 22px; - display: inline-block; + @include icon(22px); + margin-right: 18px; - background-size: contain; &.icon-videos-trending { position: relative; diff --git a/client/src/app/shared/forms/form-validators/video-abuse.ts b/client/src/app/shared/forms/form-validators/video-abuse.ts index 3c7f26205..4b2a2b789 100644 --- a/client/src/app/shared/forms/form-validators/video-abuse.ts +++ b/client/src/app/shared/forms/form-validators/video-abuse.ts @@ -3,8 +3,8 @@ import { Validators } from '@angular/forms' export const VIDEO_ABUSE_REASON = { VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(300) ], MESSAGES: { - 'required': 'Report reason name is required.', - 'minlength': 'Report reson must be at least 2 characters long.', - 'maxlength': 'Report reson cannot be more than 300 characters long.' + 'required': 'Report reason is required.', + 'minlength': 'Report reason must be at least 2 characters long.', + 'maxlength': 'Report reason cannot be more than 300 characters long.' } } diff --git a/client/src/app/videos/+video-watch/video-download.component.html b/client/src/app/videos/+video-watch/video-download.component.html index ddc57e999..7efc79e93 100644 --- a/client/src/app/videos/+video-watch/video-download.component.html +++ b/client/src/app/videos/+video-watch/video-download.component.html @@ -6,18 +6,19 @@ - +

Download

diff --git a/client/src/app/core/confirm/confirm.component.ts b/client/src/app/core/confirm/confirm.component.ts index c8e41e233..0515d969a 100644 --- a/client/src/app/core/confirm/confirm.component.ts +++ b/client/src/app/core/confirm/confirm.component.ts @@ -11,7 +11,8 @@ export interface ConfigChangedEvent { @Component({ selector: 'my-confirm', - templateUrl: './confirm.component.html' + templateUrl: './confirm.component.html', + styles: [ '.button { padding: 0 13px; }' ] }) export class ConfirmComponent implements OnInit { @ViewChild('confirmModal') confirmModal: ModalDirective diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index dc1f4dba0..0c999d659 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -42,22 +42,6 @@ label { .main-col { margin-left: $menu-width; - .title-page { - color: #000; - font-size: 16px; - display: inline-block; - margin-right: 55px; - font-weight: $font-semibold; - @include disable-default-a-behaviour; - - &.active, &.title-page-single { - border-bottom: 2px solid $orange-color; - font-weight: $font-bold; - margin-top: 30px; - margin-bottom: 25px; - } - } - .margin-content { margin-left: $not-expanded-horizontal-margins; margin-right: $not-expanded-horizontal-margins; @@ -88,6 +72,22 @@ label { } } +.title-page { + color: #000; + font-size: 16px; + display: inline-block; + margin-right: 55px; + font-weight: $font-semibold; + @include disable-default-a-behaviour; + + &.active, &.title-page-single { + border-bottom: 2px solid $orange-color; + font-weight: $font-bold; + margin-top: 30px; + margin-bottom: 25px; + } +} + // On small screen, menu is absolute and displayed over the page @media screen and (max-width: 500px) { .title-menu-left { -- cgit v1.2.3 From ff249f499ccca2e37757f338384e7ba44c906a69 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 7 Dec 2017 11:15:19 +0100 Subject: Move video form inside a component --- .../src/app/shared/forms/form-validators/video.ts | 6 ++ .../+video-edit/shared/video-edit.component.html | 85 +++++++++++++++++++++ .../+video-edit/shared/video-edit.component.ts | 83 ++++++++++++++++++++ .../videos/+video-edit/shared/video-edit.module.ts | 7 +- .../videos/+video-edit/video-update.component.html | 88 +--------------------- .../videos/+video-edit/video-update.component.ts | 46 ++--------- .../videos/+video-watch/video-watch.component.ts | 10 +-- 7 files changed, 190 insertions(+), 135 deletions(-) create mode 100644 client/src/app/videos/+video-edit/shared/video-edit.component.html create mode 100644 client/src/app/videos/+video-edit/shared/video-edit.component.ts (limited to 'client') diff --git a/client/src/app/shared/forms/form-validators/video.ts b/client/src/app/shared/forms/form-validators/video.ts index 65f11f5da..8e512e8c8 100644 --- a/client/src/app/shared/forms/form-validators/video.ts +++ b/client/src/app/shared/forms/form-validators/video.ts @@ -1,5 +1,11 @@ import { Validators } from '@angular/forms' +export type ValidatorMessage = { + [ id: string ]: { + [ error: string ]: string + } +} + export const VIDEO_NAME = { VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(120) ], MESSAGES: { diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html new file mode 100644 index 000000000..e087b71a4 --- /dev/null +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html @@ -0,0 +1,85 @@ +
+
+ + +
+ {{ formErrors.name }} +
+
+ +
+ + + +
+ {{ formErrors.privacy }} +
+
+ +
+ + +
+ +
+ + + +
+ {{ formErrors.category }} +
+
+ +
+ + + +
+ {{ formErrors.licence }} +
+
+ +
+ + + +
+ {{ formErrors.language }} +
+
+ +
+ (press enter to add the tag) + +
+ +
+ + + +
+ {{ formErrors.description }} +
+
+
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.ts b/client/src/app/videos/+video-edit/shared/video-edit.component.ts new file mode 100644 index 000000000..5b1cc3f9c --- /dev/null +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.ts @@ -0,0 +1,83 @@ +import { Component, Input, OnInit } from '@angular/core' +import { FormBuilder, FormControl, FormGroup } from '@angular/forms' +import { ActivatedRoute, Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { ServerService } from 'app/core' +import { VideoEdit } from 'app/shared/video/video-edit.model' +import 'rxjs/add/observable/forkJoin' +import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' +import { + ValidatorMessage, + VIDEO_CATEGORY, + VIDEO_DESCRIPTION, + VIDEO_LANGUAGE, + VIDEO_LICENCE, + VIDEO_NAME, + VIDEO_PRIVACY, + VIDEO_TAGS +} from '../../../shared/forms/form-validators' + +@Component({ + selector: 'my-video-edit', + styleUrls: [ './video-edit.component.scss' ], + templateUrl: './video-edit.component.html' +}) + +export class VideoEditComponent implements OnInit { + @Input() form: FormGroup + @Input() formErrors: { [ id: string ]: string } = {} + @Input() validationMessages: ValidatorMessage = {} + @Input() videoPrivacies = [] + + tags: string[] = [] + videoCategories = [] + videoLicences = [] + videoLanguages = [] + video: VideoEdit + + tagValidators = VIDEO_TAGS.VALIDATORS + tagValidatorsMessages = VIDEO_TAGS.MESSAGES + + error: string = null + + constructor ( + private formBuilder: FormBuilder, + private route: ActivatedRoute, + private router: Router, + private notificationsService: NotificationsService, + private serverService: ServerService + ) { } + + updateForm () { + this.formErrors['name'] = '' + this.formErrors['privacy'] = '' + this.formErrors['category'] = '' + this.formErrors['licence'] = '' + this.formErrors['language'] = '' + this.formErrors['description'] = '' + + this.validationMessages['name'] = VIDEO_NAME.MESSAGES + this.validationMessages['privacy'] = VIDEO_PRIVACY.MESSAGES + this.validationMessages['category'] = VIDEO_CATEGORY.MESSAGES + this.validationMessages['licence'] = VIDEO_LICENCE.MESSAGES + this.validationMessages['language'] = VIDEO_LANGUAGE.MESSAGES + this.validationMessages['description'] = VIDEO_DESCRIPTION.MESSAGES + + this.form.addControl('name', new FormControl('', VIDEO_NAME.VALIDATORS)) + this.form.addControl('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS)) + this.form.addControl('nsfw', new FormControl(false)) + this.form.addControl('category', new FormControl('', VIDEO_CATEGORY.VALIDATORS)) + this.form.addControl('licence', new FormControl('', VIDEO_LICENCE.VALIDATORS)) + this.form.addControl('language', new FormControl('', VIDEO_LANGUAGE.VALIDATORS)) + this.form.addControl('description', new FormControl('', VIDEO_DESCRIPTION.VALIDATORS)) + this.form.addControl('tags', new FormControl('')) + } + + ngOnInit () { + this.updateForm() + + this.videoCategories = this.serverService.getVideoCategories() + this.videoLicences = this.serverService.getVideoLicences() + this.videoLanguages = this.serverService.getVideoLanguages() + } +} diff --git a/client/src/app/videos/+video-edit/shared/video-edit.module.ts b/client/src/app/videos/+video-edit/shared/video-edit.module.ts index cdab694f9..c7ed8c351 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.module.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.module.ts @@ -5,6 +5,7 @@ import { TabsModule } from 'ngx-bootstrap/tabs' import { MarkdownService, VideoDescriptionComponent } from '../../shared' import { SharedModule } from '../../../shared' +import { VideoEditComponent } from './video-edit.component' @NgModule({ imports: [ @@ -15,14 +16,16 @@ import { SharedModule } from '../../../shared' ], declarations: [ - VideoDescriptionComponent + VideoDescriptionComponent, + VideoEditComponent ], exports: [ TagInputModule, TabsModule, - VideoDescriptionComponent + VideoDescriptionComponent, + VideoEditComponent ], providers: [ diff --git a/client/src/app/videos/+video-edit/video-update.component.html b/client/src/app/videos/+video-edit/video-update.component.html index b9c6139b2..c57f35da0 100644 --- a/client/src/app/videos/+video-edit/video-update.component.html +++ b/client/src/app/videos/+video-edit/video-update.component.html @@ -3,92 +3,12 @@

Update {{ video?.name }}

-
{{ error }}
-
-
- - -
- {{ formErrors.name }} -
-
- -
- - - -
- {{ formErrors.privacy }} -
-
- -
- - -
- -
- - -
- {{ formErrors.category }} -
-
- -
- - - -
- {{ formErrors.licence }} -
-
- -
- - - -
- {{ formErrors.language }} -
-
- -
- (press enter to add the tag) - -
- -
- - - -
- {{ formErrors.description }} -
-
+
this.onValueChanged(data)) } ngOnInit () { this.buildForm() - this.videoCategories = this.serverService.getVideoCategories() - this.videoLicences = this.serverService.getVideoLicences() - this.videoLanguages = this.serverService.getVideoLanguages() this.videoPrivacies = this.serverService.getVideoPrivacies() const uuid: string = this.route.snapshot.params['uuid'] - this.videoService.getVideo(uuid) .switchMap(video => { return this.videoService @@ -103,7 +69,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { video => { this.video = new VideoEdit(video) - // We cannot set private a video that was not private anymore + // We cannot set private a video that was not private if (video.privacy !== VideoPrivacy.PRIVATE) { const newVideoPrivacies = [] for (const p of this.videoPrivacies) { 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 3c6951403..87db023bf 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -11,12 +11,12 @@ import '../../../assets/player/peertube-videojs-plugin' import { AuthService, ConfirmService } from '../../core' import { VideoBlacklistService } from '../../shared' import { Account } from '../../shared/account/account.model' +import { VideoDetails } from '../../shared/video/video-details.model' import { Video } from '../../shared/video/video.model' import { MarkdownService } from '../shared' import { VideoDownloadComponent } from './video-download.component' import { VideoReportComponent } from './video-report.component' import { VideoShareComponent } from './video-share.component' -import { VideoDetails } from '../../shared/video/video-details.model' @Component({ selector: 'my-video-watch', @@ -199,14 +199,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { return this.authService.isLoggedIn() } - canUserUpdateVideo () { - return this.video.isUpdatableBy(this.authService.getUser()) - } - - isVideoRemovable () { - return this.video.isRemovableBy(this.authService.getUser()) - } - isVideoBlacklistable () { return this.video.isBlackistableBy(this.authService.getUser()) } -- cgit v1.2.3 From 4cc66133abf1e37873316999b660c93ab92eb4a0 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 7 Dec 2017 14:48:47 +0100 Subject: Design video update --- .../+video-edit/shared/video-edit.component.html | 130 ++++++++++---------- .../+video-edit/shared/video-edit.component.scss | 136 ++++++++++++++++++--- .../videos/+video-edit/video-update.component.html | 19 ++- .../videos/+video-edit/video-update.component.ts | 13 +- .../videos/+video-watch/video-watch.component.scss | 1 + .../videos/shared/video-description.component.html | 4 +- .../videos/shared/video-description.component.scss | 6 +- client/src/assets/images/global/validate.svg | 14 +++ client/src/sass/_mixins.scss | 22 +++- 9 files changed, 236 insertions(+), 109 deletions(-) create mode 100644 client/src/assets/images/global/validate.svg (limited to 'client') diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html index e087b71a4..a6b753166 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html @@ -1,85 +1,85 @@ -
-
- - -
- {{ formErrors.name }} -
-
+
-
- - +
+
+ + +
+ {{ formErrors.name }} +
+
-
- {{ formErrors.privacy }} +
+ (press enter to add the tag) +
-
-
- - +
+ + + +
+ {{ formErrors.description }} +
+
-
- - +
+
+ + -
- {{ formErrors.category }} +
+ {{ formErrors.category }} +
-
-
- - +
+ + -
- {{ formErrors.licence }} +
+ {{ formErrors.licence }} +
-
-
- - +
+ + -
- {{ formErrors.language }} +
+ {{ formErrors.language }} +
-
-
- (press enter to add the tag) - -
+
+ + -
- - +
+ {{ formErrors.privacy }} +
+
-
- {{ formErrors.description }} +
+ +
+
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.scss b/client/src/app/videos/+video-edit/shared/video-edit.component.scss index 9ee0c520c..0af48fabe 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.scss @@ -1,3 +1,121 @@ +.video-edit { + height: 100%; + + .form-group { + margin-bottom: 25px; + } + + input { + @include peertube-input-text(100%); + display: block; + + &[type=checkbox] { + outline: 0; + } + } + + select { + @include peertube-select(100%); + } + + input, select { + font-size: 15px + } + + .form-group-checkbox { + display: flex; + align-items: center; + + label { + font-weight: $font-regular; + margin: 0; + } + + input { + width: 10px; + margin-right: 10px; + } + } +} + +.submit-container { + text-align: right; + position: relative; + bottom: $button-height; + + .submit-button { + @include peertube-button; + @include orange-button; + + display: inline-block; + + input { + cursor: inherit; + background-color: inherit; + border: none; + padding: 0; + } + + .icon.icon-validate { + @include icon(20px); + + cursor: inherit; + position: relative; + top: -1px; + margin-right: 4px; + background-image: url('../../../../assets/images/global/validate.svg'); + } + } +} + +/deep/ { + .ng2-tag-input { + border: none !important; + } + + .ng2-tags-container { + display: flex; + align-items: center; + border: 1px solid #C6C6C6; + border-radius: 3px; + padding: 5px !important; + } + + tag { + background-color: #E5E5E5 !important; + border-radius: 3px !important; + font-size: 15px !important; + color: #000 !important; + height: 30px !important; + line-height: 30px !important; + margin: 0 5px 0 0 !important; + cursor: default !important; + padding: 0 8px 0 10px !important; + + div { + height: 100% !important; + } + } + + delete-icon { + cursor: pointer !important; + height: auto !important; + vertical-align: middle !important; + padding-left: 6px !important; + + svg { + height: auto !important; + vertical-align: middle !important; + fill: #585858 !important; + } + + &:hover { + transform: none !important; + } + } +} + + .btn-file { position: relative; overflow: hidden; @@ -20,24 +138,6 @@ display: block; } -.form-group { - margin-bottom: 10px; -} - -div.tags { - height: 40px; - font-size: 20px; - margin-top: 20px; - - .tag { - margin-right: 10px; - - .remove { - cursor: pointer; - } - } -} - div.file-to-upload { height: 40px; diff --git a/client/src/app/videos/+video-edit/video-update.component.html b/client/src/app/videos/+video-edit/video-update.component.html index c57f35da0..3163495bf 100644 --- a/client/src/app/videos/+video-edit/video-update.component.html +++ b/client/src/app/videos/+video-edit/video-update.component.html @@ -1,7 +1,7 @@ -
-
- -

Update {{ video?.name }}

+
+
+ Update {{ video?.name }} +
@@ -10,12 +10,11 @@ [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" > -
- +
+
+ + +
-
diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts index 01ab0a716..d1da8b6d8 100644 --- a/client/src/app/videos/+video-edit/video-update.component.ts +++ b/client/src/app/videos/+video-edit/video-update.component.ts @@ -5,19 +5,10 @@ import { NotificationsService } from 'angular2-notifications' import 'rxjs/add/observable/forkJoin' import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' import { ServerService } from '../../core' -import { - FormReactive, - VIDEO_CATEGORY, - VIDEO_DESCRIPTION, - VIDEO_LANGUAGE, - VIDEO_LICENCE, - VIDEO_NAME, - VIDEO_PRIVACY, - VIDEO_TAGS -} from '../../shared' +import { FormReactive } from '../../shared' import { ValidatorMessage } from '../../shared/forms/form-validators' -import { VideoService } from '../../shared/video/video.service' import { VideoEdit } from '../../shared/video/video-edit.model' +import { VideoService } from '../../shared/video/video.service' @Component({ selector: 'my-videos-update', diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index 6973619b2..2ccfd2749 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -70,6 +70,7 @@ &.icon-more { background-image: url('../../../assets/images/video/more.svg'); + top: -1px; } } diff --git a/client/src/app/videos/shared/video-description.component.html b/client/src/app/videos/shared/video-description.component.html index 7a228857c..da66a9753 100644 --- a/client/src/app/videos/shared/video-description.component.html +++ b/client/src/app/videos/shared/video-description.component.html @@ -1,6 +1,6 @@ diff --git a/client/src/app/videos/shared/video-description.component.scss b/client/src/app/videos/shared/video-description.component.scss index d8d73e846..6ef81ae58 100644 --- a/client/src/app/videos/shared/video-description.component.scss +++ b/client/src/app/videos/shared/video-description.component.scss @@ -1,11 +1,15 @@ textarea { + @include peertube-input-text(100%); + + font-size: 15px; height: 150px; } .previews /deep/ { + font-size: 15px !important; + .nav { margin-top: 10px; - font-size: 0.9em; } .tab-content { diff --git a/client/src/assets/images/global/validate.svg b/client/src/assets/images/global/validate.svg new file mode 100644 index 000000000..5c7ee9d14 --- /dev/null +++ b/client/src/assets/images/global/validate.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/sass/_mixins.scss b/client/src/sass/_mixins.scss index 14d9b5044..121e16e10 100644 --- a/client/src/sass/_mixins.scss +++ b/client/src/sass/_mixins.scss @@ -23,20 +23,28 @@ color: #fff; background-color: $orange-color; - &:hover, &:active, &:focus, &[disabled] { + &:hover, &:active, &:focus, &[disabled], &.disabled { color: #fff; background-color: $orange-hoover-color; } + + &[disabled], &.disabled { + cursor: default; + } } @mixin grey-button { background-color: $grey-color; color: #585858; - &:hover, &:active, &:focus, &[disabled] { + &:hover, &:active, &:focus, &[disabled], &.disabled { color: #585858; background-color: $grey-hoover-color; } + + &[disabled], &.disabled { + cursor: default; + } } @mixin peertube-button { @@ -72,3 +80,13 @@ vertical-align: middle; cursor: pointer; } + + +@mixin peertube-select ($width) { + background-color: #fff; + border: 1px solid #C6C6C6; + height: $button-height; + width: $width; + border-radius: 3px; + padding-left: 15px; +} -- cgit v1.2.3 From 27e1a06c331278e5d37bc5172ee7e4fc968e4b5e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 7 Dec 2017 16:32:06 +0100 Subject: First step upload with new design --- .../+video-edit/shared/video-edit.component.html | 1 + .../+video-edit/shared/video-edit.component.scss | 35 ----- .../videos/+video-edit/video-add.component.html | 142 ++++----------------- .../videos/+video-edit/video-add.component.scss | 60 +++++++++ .../app/videos/+video-edit/video-add.component.ts | 126 +++++++----------- client/src/assets/images/video/upload.svg | 16 +++ 6 files changed, 145 insertions(+), 235 deletions(-) create mode 100644 client/src/app/videos/+video-edit/video-add.component.scss create mode 100644 client/src/assets/images/video/upload.svg (limited to 'client') diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html index a6b753166..8c071ce12 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html @@ -67,6 +67,7 @@
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.scss b/client/src/app/videos/+video-edit/shared/video-edit.component.scss index 0af48fabe..2d0bfc36e 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.scss @@ -115,42 +115,7 @@ } } - -.btn-file { - position: relative; - overflow: hidden; - display: block; -} - -.btn-file input[type=file] { - position: absolute; - top: 0; - right: 0; - min-width: 100%; - min-height: 100%; - font-size: 100px; - text-align: right; - filter: alpha(opacity=0); - opacity: 0; - outline: none; - background: white; - cursor: inherit; - display: block; -} - -div.file-to-upload { - height: 40px; - - .glyphicon-remove { - cursor: pointer; - } -} - .little-information { font-size: 0.8em; font-style: italic; } - -.label-tags { - margin-bottom: 0; -} diff --git a/client/src/app/videos/+video-edit/video-add.component.html b/client/src/app/videos/+video-edit/video-add.component.html index b4e0f9f7c..f8355f3db 100644 --- a/client/src/app/videos/+video-edit/video-add.component.html +++ b/client/src/app/videos/+video-edit/video-add.component.html @@ -1,141 +1,45 @@ -
-
+
+
+ Upload your video +
-

Upload a video

+
{{ error }}
-
{{ error }}
+
+
+
-
-
- - -
- {{ formErrors.name }} -
+
+ Select the file to upload +
- - - -
- {{ formErrors.privacy }} -
-
- -
- -
- - - -
- {{ formErrors.channelId }} -
+
-
- - -
- {{ formErrors.category }} -
-
- -
- - - -
- {{ formErrors.licence }} -
-
- -
- - + + -
- {{ formErrors.language }} +
+
+ +
- -
- (press enter to add the tag) - -
- -
- -
- Select the video... - - -
-
- -
-
- {{ filename }} - -
-
- -
- {{ formErrors.videofile }} -
- -
- - - -
- {{ formErrors.description }} -
-
- -
- - - - Server is processing the video - - -
- -
- -
diff --git a/client/src/app/videos/+video-edit/video-add.component.scss b/client/src/app/videos/+video-edit/video-add.component.scss new file mode 100644 index 000000000..25dfd40d2 --- /dev/null +++ b/client/src/app/videos/+video-edit/video-add.component.scss @@ -0,0 +1,60 @@ +.upload-video-container { + border-radius: 3px; + background-color: #F7F7F7; + border: 3px solid #EAEAEA; + width: 100%; + height: 440px; + text-align: center; + margin-top: 40px; + display: flex; + justify-content: center; + align-items: center; + + .upload-video { + display: flex; + flex-direction: column; + align-items: center; + + .icon.icon-upload { + @include icon(90px); + margin-bottom: 25px; + + background-image: url('../../../assets/images/video/upload.svg'); + } + + .button-file { + position: relative; + overflow: hidden; + display: inline-block; + margin-bottom: 70px; + + @include peertube-button; + @include orange-button; + + input[type=file] { + position: absolute; + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + font-size: 100px; + text-align: right; + filter: alpha(opacity=0); + opacity: 0; + outline: none; + background: white; + cursor: inherit; + display: block; + } + } + + select { + @include peertube-select(auto); + + display: inline-block; + font-size: 15px + } + } +} + + diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts index 989addbd7..071f9a28b 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts @@ -6,61 +6,33 @@ import { NotificationsService } from 'angular2-notifications' import { VideoService } from 'app/shared/video/video.service' import { VideoCreate } from '../../../../../shared' import { AuthService, ServerService } from '../../core' -import { - FormReactive, - VIDEO_CATEGORY, - VIDEO_CHANNEL, - VIDEO_DESCRIPTION, - VIDEO_FILE, - VIDEO_LANGUAGE, - VIDEO_LICENCE, - VIDEO_NAME, - VIDEO_PRIVACY, - VIDEO_TAGS -} from '../../shared' +import { FormReactive } from '../../shared' +import { ValidatorMessage } from '../../shared/forms/form-validators' +import { VideoEdit } from '../../shared/video/video-edit.model' @Component({ selector: 'my-videos-add', - styleUrls: [ './shared/video-edit.component.scss' ], - templateUrl: './video-add.component.html' + templateUrl: './video-add.component.html', + styleUrls: [ + './shared/video-edit.component.scss', + './video-add.component.scss' + ] }) export class VideoAddComponent extends FormReactive implements OnInit { @ViewChild('videofileInput') videofileInput + isUploadingVideo = false progressPercent = 0 - tags: string[] = [] - videoCategories = [] - videoLicences = [] - videoLanguages = [] - videoPrivacies = [] - userVideoChannels = [] - - tagValidators = VIDEO_TAGS.VALIDATORS - tagValidatorsMessages = VIDEO_TAGS.MESSAGES - error: string + error: string = null form: FormGroup - formErrors = { - name: '', - privacy: '', - category: '', - licence: '', - language: '', - channelId: '', - description: '', - videofile: '' - } - validationMessages = { - name: VIDEO_NAME.MESSAGES, - privacy: VIDEO_PRIVACY.MESSAGES, - category: VIDEO_CATEGORY.MESSAGES, - licence: VIDEO_LICENCE.MESSAGES, - language: VIDEO_LANGUAGE.MESSAGES, - channelId: VIDEO_CHANNEL.MESSAGES, - description: VIDEO_DESCRIPTION.MESSAGES, - videofile: VIDEO_FILE.MESSAGES - } + formErrors: { [ id: string ]: string } = {} + validationMessages: ValidatorMessage = {} + userVideoChannels = [] + videoPrivacies = [] + firstStepPrivacy = 0 + firstStepChannel = 0 constructor ( private formBuilder: FormBuilder, @@ -73,35 +45,17 @@ export class VideoAddComponent extends FormReactive implements OnInit { super() } - get filename () { - return this.form.value['videofile'] - } - buildForm () { - this.form = this.formBuilder.group({ - name: [ '', VIDEO_NAME.VALIDATORS ], - nsfw: [ false ], - privacy: [ '', VIDEO_PRIVACY.VALIDATORS ], - category: [ '', VIDEO_CATEGORY.VALIDATORS ], - licence: [ '', VIDEO_LICENCE.VALIDATORS ], - language: [ '', VIDEO_LANGUAGE.VALIDATORS ], - channelId: [ '', VIDEO_CHANNEL.VALIDATORS ], - description: [ '', VIDEO_DESCRIPTION.VALIDATORS ], - videofile: [ '', VIDEO_FILE.VALIDATORS ], - tags: [ '' ] - }) - + this.form = this.formBuilder.group({}) this.form.valueChanges.subscribe(data => this.onValueChanged(data)) } ngOnInit () { - this.videoCategories = this.serverService.getVideoCategories() - this.videoLicences = this.serverService.getVideoLicences() - this.videoLanguages = this.serverService.getVideoLanguages() - this.videoPrivacies = this.serverService.getVideoPrivacies() - this.buildForm() + this.videoPrivacies = this.serverService.getVideoPrivacies() + this.firstStepPrivacy = this.videoPrivacies[0].id + this.authService.userInformationLoaded .subscribe( () => { @@ -112,21 +66,13 @@ export class VideoAddComponent extends FormReactive implements OnInit { if (Array.isArray(videoChannels) === false) return this.userVideoChannels = videoChannels.map(v => ({ id: v.id, label: v.name })) - - this.form.patchValue({ channelId: this.userVideoChannels[0].id }) + this.firstStepChannel = this.userVideoChannels[0].id } ) } - // The goal is to keep reactive form validation (required field) - // https://stackoverflow.com/a/44238894 fileChange ($event) { - this.form.controls['videofile'].setValue($event.target.files[0].name) - } - - removeFile () { - this.videofileInput.nativeElement.value = '' - this.form.controls['videofile'].setValue('') + console.log('uploading file ?') } checkForm () { @@ -135,11 +81,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { return this.form.valid } - upload () { - if (this.checkForm() === false) { - return - } - + uploadFirstStep () { const formValue: VideoCreate = this.form.value const name = formValue.name @@ -193,4 +135,26 @@ export class VideoAddComponent extends FormReactive implements OnInit { } ) } + + updateSecondStep () { + if (this.checkForm() === false) { + return + } + + const video = new VideoEdit(this.form.value) + + this.videoService.updateVideo(video) + .subscribe( + () => { + this.notificationsService.success('Success', 'Video published.') + this.router.navigate([ '/videos/watch', video.uuid ]) + }, + + err => { + this.error = 'Cannot update the video.' + console.error(err) + } + ) + + } } diff --git a/client/src/assets/images/video/upload.svg b/client/src/assets/images/video/upload.svg new file mode 100644 index 000000000..c5b7cb443 --- /dev/null +++ b/client/src/assets/images/video/upload.svg @@ -0,0 +1,16 @@ + + + + cloud-upload + Created with Sketch. + + + + + + + + + + + -- cgit v1.2.3 From baeefe22caf8ae6cb58dc40754e5db14b50168bf Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 7 Dec 2017 17:22:44 +0100 Subject: First upload step is ok --- client/src/app/core/server/server.service.ts | 37 +++++++++------ .../videos/+video-edit/video-add.component.html | 32 ++++++------- .../app/videos/+video-edit/video-add.component.ts | 52 +++++++++------------- 3 files changed, 61 insertions(+), 60 deletions(-) (limited to 'client') diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index cbc4074c9..43a836c5a 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts @@ -1,5 +1,7 @@ -import { Injectable } from '@angular/core' import { HttpClient } from '@angular/common/http' +import { Injectable } from '@angular/core' +import 'rxjs/add/operator/do' +import { ReplaySubject } from 'rxjs/ReplaySubject' import { ServerConfig } from '../../../../../shared' @@ -8,6 +10,11 @@ export class ServerService { private static BASE_CONFIG_URL = API_URL + '/api/v1/config/' private static BASE_VIDEO_URL = API_URL + '/api/v1/videos/' + videoPrivaciesLoaded = new ReplaySubject(1) + videoCategoriesLoaded = new ReplaySubject(1) + videoLicencesLoaded = new ReplaySubject(1) + videoLanguagesLoaded = new ReplaySubject(1) + private config: ServerConfig = { signup: { allowed: false @@ -29,19 +36,19 @@ export class ServerService { } loadVideoCategories () { - return this.loadVideoAttributeEnum('categories', this.videoCategories) + return this.loadVideoAttributeEnum('categories', this.videoCategories, this.videoCategoriesLoaded) } loadVideoLicences () { - return this.loadVideoAttributeEnum('licences', this.videoLicences) + return this.loadVideoAttributeEnum('licences', this.videoLicences, this.videoLicencesLoaded) } loadVideoLanguages () { - return this.loadVideoAttributeEnum('languages', this.videoLanguages) + return this.loadVideoAttributeEnum('languages', this.videoLanguages, this.videoLanguagesLoaded) } loadVideoPrivacies () { - return this.loadVideoAttributeEnum('privacies', this.videoPrivacies) + return this.loadVideoAttributeEnum('privacies', this.videoPrivacies, this.videoPrivaciesLoaded) } getConfig () { @@ -66,17 +73,19 @@ export class ServerService { private loadVideoAttributeEnum ( attributeName: 'categories' | 'licences' | 'languages' | 'privacies', - hashToPopulate: { id: number, label: string }[] + hashToPopulate: { id: number, label: string }[], + notifier: ReplaySubject ) { return this.http.get(ServerService.BASE_VIDEO_URL + attributeName) - .subscribe(data => { - Object.keys(data) - .forEach(dataKey => { - hashToPopulate.push({ - id: parseInt(dataKey, 10), - label: data[dataKey] - }) - }) + .do(() => notifier.next(true)) + .subscribe(data => { + Object.keys(data) + .forEach(dataKey => { + hashToPopulate.push({ + id: parseInt(dataKey, 10), + label: data[dataKey] + }) }) + }) } } diff --git a/client/src/app/videos/+video-edit/video-add.component.html b/client/src/app/videos/+video-edit/video-add.component.html index f8355f3db..78e5bb70e 100644 --- a/client/src/app/videos/+video-edit/video-add.component.html +++ b/client/src/app/videos/+video-edit/video-add.component.html @@ -5,13 +5,13 @@
{{ error }}
-
+
Select the file to upload - +
@@ -26,20 +26,20 @@
+
- -
- - -
-
- - -
+ + + + +
+
+ +
- -
+
+
diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts index 071f9a28b..c2ee4ae2e 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts @@ -23,12 +23,14 @@ export class VideoAddComponent extends FormReactive implements OnInit { @ViewChild('videofileInput') videofileInput isUploadingVideo = false + videoUploaded = false progressPercent = 0 error: string = null form: FormGroup formErrors: { [ id: string ]: string } = {} validationMessages: ValidatorMessage = {} + userVideoChannels = [] videoPrivacies = [] firstStepPrivacy = 0 @@ -53,8 +55,12 @@ export class VideoAddComponent extends FormReactive implements OnInit { ngOnInit () { this.buildForm() - this.videoPrivacies = this.serverService.getVideoPrivacies() - this.firstStepPrivacy = this.videoPrivacies[0].id + this.serverService.videoCategoriesLoaded + .subscribe( + () => { + this.videoPrivacies = this.serverService.getVideoPrivacies() + this.firstStepPrivacy = this.videoPrivacies[0].id + }) this.authService.userInformationLoaded .subscribe( @@ -71,8 +77,8 @@ export class VideoAddComponent extends FormReactive implements OnInit { ) } - fileChange ($event) { - console.log('uploading file ?') + fileChange () { + this.uploadFirstStep() } checkForm () { @@ -82,38 +88,26 @@ export class VideoAddComponent extends FormReactive implements OnInit { } uploadFirstStep () { - const formValue: VideoCreate = this.form.value - - const name = formValue.name - const privacy = formValue.privacy - const nsfw = formValue.nsfw - const category = formValue.category - const licence = formValue.licence - const language = formValue.language - const channelId = formValue.channelId - const description = formValue.description - const tags = formValue.tags const videofile = this.videofileInput.nativeElement.files[0] + const name = videofile.name + const privacy = this.firstStepPrivacy.toString() + const nsfw = false + const channelId = this.firstStepChannel.toString() const formData = new FormData() formData.append('name', name) formData.append('privacy', privacy.toString()) - formData.append('category', '' + category) formData.append('nsfw', '' + nsfw) - formData.append('licence', '' + licence) formData.append('channelId', '' + channelId) formData.append('videofile', videofile) - // Language is optional - if (language) { - formData.append('language', '' + language) - } - - formData.append('description', description) - - for (let i = 0; i < tags.length; i++) { - formData.append(`tags[${i}]`, tags[i]) - } + this.isUploadingVideo = true + this.form.patchValue({ + name, + privacy, + nsfw, + channelId + }) this.videoService.uploadVideo(formData).subscribe( event => { @@ -121,10 +115,8 @@ export class VideoAddComponent extends FormReactive implements OnInit { this.progressPercent = Math.round(100 * event.loaded / event.total) } else if (event instanceof HttpResponse) { console.log('Video uploaded.') - this.notificationsService.success('Success', 'Video uploaded.') - // Display all the videos once it's finished - this.router.navigate([ '/videos/trending' ]) + this.videoUploaded = true } }, -- cgit v1.2.3 From c182778e26b8478fae9d7dd0bf0687baf7b72fd1 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 7 Dec 2017 17:56:59 +0100 Subject: Add progress bar for video upload --- client/.bootstraprc | 10 +++++----- client/src/app/shared/shared.module.ts | 3 --- .../videos/+video-edit/video-add.component.html | 2 ++ .../videos/+video-edit/video-add.component.scss | 23 ++++++++++++++++++++++ .../app/videos/+video-edit/video-add.component.ts | 8 ++++---- .../src/app/videos/+video-edit/video-add.module.ts | 4 +++- 6 files changed, 37 insertions(+), 13 deletions(-) (limited to 'client') diff --git a/client/.bootstraprc b/client/.bootstraprc index 6ceef4fe9..cc6768d43 100644 --- a/client/.bootstraprc +++ b/client/.bootstraprc @@ -84,19 +84,19 @@ styles: navs: true navbar: false breadcrumbs: false - pagination: true + pagination: false pager: false - labels: true + labels: false badges: false jumbotron: false - thumbnails: true + thumbnails: false alerts: true - progress-bars: true + progress-bars: false media: true list-group: false panels: true wells: false - responsive-embed: true + responsive-embed: false close: true # Components w/ JavaScript diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index bd9aee345..74f6f579d 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -6,7 +6,6 @@ import { RouterModule } from '@angular/router' import { BsDropdownModule } from 'ngx-bootstrap/dropdown' import { ModalModule } from 'ngx-bootstrap/modal' -import { ProgressbarModule } from 'ngx-bootstrap/progressbar' import { InfiniteScrollModule } from 'ngx-infinite-scroll' import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' @@ -34,7 +33,6 @@ import { VideoService } from './video/video.service' BsDropdownModule.forRoot(), ModalModule.forRoot(), - ProgressbarModule.forRoot(), DataTableModule, PrimeSharedModule, @@ -59,7 +57,6 @@ import { VideoService } from './video/video.service' BsDropdownModule, ModalModule, - ProgressbarModule, DataTableModule, PrimeSharedModule, InfiniteScrollModule, diff --git a/client/src/app/videos/+video-edit/video-add.component.html b/client/src/app/videos/+video-edit/video-add.component.html index 78e5bb70e..6883f8280 100644 --- a/client/src/app/videos/+video-edit/video-add.component.html +++ b/client/src/app/videos/+video-edit/video-add.component.html @@ -28,6 +28,8 @@
+ +
{ this.videoPrivacies = this.serverService.getVideoPrivacies() @@ -112,7 +112,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { this.videoService.uploadVideo(formData).subscribe( event => { if (event.type === HttpEventType.UploadProgress) { - this.progressPercent = Math.round(100 * event.loaded / event.total) + this.videoUploadPercents = Math.round(100 * event.loaded / event.total) } else if (event instanceof HttpResponse) { console.log('Video uploaded.') @@ -122,7 +122,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { err => { // Reset progress - this.progressPercent = 0 + this.videoUploadPercents = 0 this.error = err.message } ) diff --git a/client/src/app/videos/+video-edit/video-add.module.ts b/client/src/app/videos/+video-edit/video-add.module.ts index f58d12dac..1efecdf4d 100644 --- a/client/src/app/videos/+video-edit/video-add.module.ts +++ b/client/src/app/videos/+video-edit/video-add.module.ts @@ -1,4 +1,5 @@ import { NgModule } from '@angular/core' +import { ProgressBarModule } from 'primeng/primeng' import { SharedModule } from '../../shared' import { VideoEditModule } from './shared/video-edit.module' import { VideoAddRoutingModule } from './video-add-routing.module' @@ -8,7 +9,8 @@ import { VideoAddComponent } from './video-add.component' imports: [ VideoAddRoutingModule, VideoEditModule, - SharedModule + SharedModule, + ProgressBarModule ], declarations: [ -- cgit v1.2.3 From cadb46d832724ea1a17b085b992142aa32e212be Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 8 Dec 2017 08:39:15 +0100 Subject: Design second video upload step --- client/src/app/core/auth/auth.service.ts | 3 +- client/src/app/core/server/server.service.ts | 3 +- client/src/app/menu/menu.component.scss | 4 ++ .../src/app/shared/forms/form-validators/video.ts | 22 ++----- client/src/app/shared/video/video-edit.model.ts | 26 +++++---- client/src/app/shared/video/video.service.ts | 11 ++-- .../shared/video-description.component.html | 9 +++ .../shared/video-description.component.scss | 41 +++++++++++++ .../shared/video-description.component.ts | 66 +++++++++++++++++++++ .../+video-edit/shared/video-edit.component.scss | 9 +++ .../videos/+video-edit/shared/video-edit.module.ts | 3 +- .../videos/+video-edit/video-add.component.html | 16 +++-- .../videos/+video-edit/video-add.component.scss | 19 +++++- .../app/videos/+video-edit/video-add.component.ts | 30 ++++++---- .../videos/+video-edit/video-update.component.html | 4 +- .../videos/+video-watch/video-watch.component.html | 2 +- .../videos/+video-watch/video-watch.component.ts | 5 ++ client/src/app/videos/shared/index.ts | 1 - .../videos/shared/video-description.component.html | 9 --- .../videos/shared/video-description.component.scss | 19 ------ .../videos/shared/video-description.component.ts | 68 ---------------------- client/src/sass/_mixins.scss | 6 +- client/src/sass/application.scss | 4 ++ 23 files changed, 224 insertions(+), 156 deletions(-) create mode 100644 client/src/app/videos/+video-edit/shared/video-description.component.html create mode 100644 client/src/app/videos/+video-edit/shared/video-description.component.scss create mode 100644 client/src/app/videos/+video-edit/shared/video-description.component.ts delete mode 100644 client/src/app/videos/shared/video-description.component.html delete mode 100644 client/src/app/videos/shared/video-description.component.scss delete mode 100644 client/src/app/videos/shared/video-description.component.ts (limited to 'client') diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index fd2708c11..0db197f02 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts @@ -194,7 +194,6 @@ export class AuthService { } this.mergeUserInformation(obj) - .do(() => this.userInformationLoaded.next(true)) .subscribe( res => { this.user.displayNSFW = res.displayNSFW @@ -203,6 +202,8 @@ export class AuthService { this.user.account = res.account this.user.save() + + this.userInformationLoaded.next(true) } ) } diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index 43a836c5a..16e0595b6 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts @@ -77,7 +77,6 @@ export class ServerService { notifier: ReplaySubject ) { return this.http.get(ServerService.BASE_VIDEO_URL + attributeName) - .do(() => notifier.next(true)) .subscribe(data => { Object.keys(data) .forEach(dataKey => { @@ -86,6 +85,8 @@ export class ServerService { label: data[dataKey] }) }) + + notifier.next(true) }) } } diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index eda3e1a85..63d63d287 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss @@ -43,6 +43,10 @@ menu { .logged-in-email { font-size: 13px; color: #C6C6C6; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 140px; } } diff --git a/client/src/app/shared/forms/form-validators/video.ts b/client/src/app/shared/forms/form-validators/video.ts index 8e512e8c8..45da7df4a 100644 --- a/client/src/app/shared/forms/form-validators/video.ts +++ b/client/src/app/shared/forms/form-validators/video.ts @@ -23,17 +23,13 @@ export const VIDEO_PRIVACY = { } export const VIDEO_CATEGORY = { - VALIDATORS: [ Validators.required ], - MESSAGES: { - 'required': 'Video category is required.' - } + VALIDATORS: [ ], + MESSAGES: {} } export const VIDEO_LICENCE = { - VALIDATORS: [ Validators.required ], - MESSAGES: { - 'required': 'Video licence is required.' - } + VALIDATORS: [ ], + MESSAGES: {} } export const VIDEO_LANGUAGE = { @@ -49,9 +45,8 @@ export const VIDEO_CHANNEL = { } export const VIDEO_DESCRIPTION = { - VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(3000) ], + VALIDATORS: [ Validators.minLength(3), Validators.maxLength(3000) ], MESSAGES: { - 'required': 'Video description is required.', 'minlength': 'Video description must be at least 3 characters long.', 'maxlength': 'Video description cannot be more than 3000 characters long.' } @@ -64,10 +59,3 @@ export const VIDEO_TAGS = { 'maxlength': 'A tag should be less than 30 characters long.' } } - -export const VIDEO_FILE = { - VALIDATORS: [ Validators.required ], - MESSAGES: { - 'required': 'Video file is required.' - } -} diff --git a/client/src/app/shared/video/video-edit.model.ts b/client/src/app/shared/video/video-edit.model.ts index 88d23a59f..955255bfa 100644 --- a/client/src/app/shared/video/video-edit.model.ts +++ b/client/src/app/shared/video/video-edit.model.ts @@ -14,18 +14,20 @@ export class VideoEdit { uuid?: string id?: number - constructor (videoDetails: VideoDetails) { - this.id = videoDetails.id - this.uuid = videoDetails.uuid - this.category = videoDetails.category - this.licence = videoDetails.licence - this.language = videoDetails.language - this.description = videoDetails.description - this.name = videoDetails.name - this.tags = videoDetails.tags - this.nsfw = videoDetails.nsfw - this.channel = videoDetails.channel.id - this.privacy = videoDetails.privacy + constructor (videoDetails?: VideoDetails) { + if (videoDetails) { + this.id = videoDetails.id + this.uuid = videoDetails.uuid + this.category = videoDetails.category + this.licence = videoDetails.licence + this.language = videoDetails.language + this.description = videoDetails.description + this.name = videoDetails.name + this.tags = videoDetails.tags + this.nsfw = videoDetails.nsfw + this.channel = videoDetails.channel.id + this.privacy = videoDetails.privacy + } } patch (values: Object) { diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index 3f35b67c4..1a0644c3d 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts @@ -42,14 +42,17 @@ export class VideoService { } updateVideo (video: VideoEdit) { - const language = video.language ? video.language : null + const language = video.language || undefined + const licence = video.licence || undefined + const category = video.category || undefined + const description = video.description || undefined const body: VideoUpdate = { name: video.name, - category: video.category, - licence: video.licence, + category, + licence, language, - description: video.description, + description, privacy: video.privacy, tags: video.tags, nsfw: video.nsfw diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.html b/client/src/app/videos/+video-edit/shared/video-description.component.html new file mode 100644 index 000000000..da66a9753 --- /dev/null +++ b/client/src/app/videos/+video-edit/shared/video-description.component.html @@ -0,0 +1,9 @@ + + + + + + diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.scss b/client/src/app/videos/+video-edit/shared/video-description.component.scss new file mode 100644 index 000000000..38506bb46 --- /dev/null +++ b/client/src/app/videos/+video-edit/shared/video-description.component.scss @@ -0,0 +1,41 @@ +textarea { + @include peertube-input-text(100%); + + padding: 5px 15px; + font-size: 15px; + height: 150px; +} + +.previews /deep/ { + font-size: 15px !important; + + .nav { + margin-top: 10px; + font-size: 16px !important; + border: none !important; + + .nav-item .nav-link { + color: #000 !important; + height: 30px !important; + margin-right: 30px; + padding: 0 15px; + display: flex; + align-items: center; + border-radius: 3px; + border: none !important; + + &.active, &:hover { + background-color: #F0F0F0; + } + + &.active { + font-weight: $font-semibold !important; + } + } + } + + .tab-content { + min-height: 75px; + padding: 15px; + } +} diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.ts b/client/src/app/videos/+video-edit/shared/video-description.component.ts new file mode 100644 index 000000000..8dfb74b2a --- /dev/null +++ b/client/src/app/videos/+video-edit/shared/video-description.component.ts @@ -0,0 +1,66 @@ +import { Component, forwardRef, Input, OnInit } from '@angular/core' +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' +import { truncate } from 'lodash' +import 'rxjs/add/operator/debounceTime' +import 'rxjs/add/operator/distinctUntilChanged' +import { Subject } from 'rxjs/Subject' +import { MarkdownService } from '../../shared' + +@Component({ + selector: 'my-video-description', + templateUrl: './video-description.component.html', + styleUrls: [ './video-description.component.scss' ], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => VideoDescriptionComponent), + multi: true + } + ] +}) + +export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { + @Input() description = '' + truncatedDescriptionHTML = '' + descriptionHTML = '' + + private descriptionChanged = new Subject() + + constructor (private markdownService: MarkdownService) {} + + ngOnInit () { + this.descriptionChanged + .debounceTime(150) + .distinctUntilChanged() + .subscribe(() => this.updateDescriptionPreviews()) + + this.descriptionChanged.next(this.description) + } + + propagateChange = (_: any) => { /* empty */ } + + writeValue (description: string) { + this.description = description + + this.descriptionChanged.next(this.description) + } + + registerOnChange (fn: (_: any) => void) { + this.propagateChange = fn + } + + registerOnTouched () { + // Unused + } + + onModelChange () { + this.propagateChange(this.description) + + this.descriptionChanged.next(this.description) + } + + private updateDescriptionPreviews () { + this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 })) + this.descriptionHTML = this.markdownService.markdownToHTML(this.description) + } +} diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.scss b/client/src/app/videos/+video-edit/shared/video-edit.component.scss index 2d0bfc36e..d363499ce 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.scss @@ -43,6 +43,14 @@ position: relative; bottom: $button-height; + .message-submit { + display: inline-block; + margin-right: 25px; + + color: #585858; + font-size: 15px; + } + .submit-button { @include peertube-button; @include orange-button; @@ -54,6 +62,7 @@ background-color: inherit; border: none; padding: 0; + outline: 0; } .icon.icon-validate { diff --git a/client/src/app/videos/+video-edit/shared/video-edit.module.ts b/client/src/app/videos/+video-edit/shared/video-edit.module.ts index c7ed8c351..ce106d82f 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.module.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.module.ts @@ -3,8 +3,9 @@ import { NgModule } from '@angular/core' import { TagInputModule } from 'ngx-chips' import { TabsModule } from 'ngx-bootstrap/tabs' -import { MarkdownService, VideoDescriptionComponent } from '../../shared' +import { MarkdownService } from '../../shared' import { SharedModule } from '../../../shared' +import { VideoDescriptionComponent } from './video-description.component' import { VideoEditComponent } from './video-edit.component' @NgModule({ diff --git a/client/src/app/videos/+video-edit/video-add.component.html b/client/src/app/videos/+video-edit/video-add.component.html index 6883f8280..a6f2bf6f2 100644 --- a/client/src/app/videos/+video-edit/video-add.component.html +++ b/client/src/app/videos/+video-edit/video-add.component.html @@ -15,20 +15,23 @@
-
-
- + @@ -37,10 +40,13 @@ [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" > +
-
+
Publish will be available when upload is finished
+ +
- +
diff --git a/client/src/app/videos/+video-edit/video-add.component.scss b/client/src/app/videos/+video-edit/video-add.component.scss index dfdf7ff73..39673b4b7 100644 --- a/client/src/app/videos/+video-edit/video-add.component.scss +++ b/client/src/app/videos/+video-edit/video-add.component.scss @@ -18,6 +18,7 @@ .icon.icon-upload { @include icon(90px); margin-bottom: 25px; + cursor: default; background-image: url('../../../assets/images/video/upload.svg'); } @@ -58,10 +59,9 @@ } p-progressBar { - margin-top: 50px; - margin-bottom: 40px; - /deep/ .ui-progressbar { + margin-top: 25px !important; + margin-bottom: 40px !important; font-size: 15px !important; color: #fff !important; height: 30px !important; @@ -76,6 +76,19 @@ p-progressBar { .ui-progressbar-label { text-align: left; padding-left: 18px; + margin-top: 0 !important; + } + } + + &.processing { + /deep/ .ui-progressbar-label { + // Same color as background to hide "100%" + color: rgba(11, 204, 41, 0.16) !important; + + &::before { + content: 'Processing...'; + color: #fff; + } } } } diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts index 2409acfda..2bbc3de17 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts @@ -5,6 +5,7 @@ import { Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' import { VideoService } from 'app/shared/video/video.service' import { VideoCreate } from '../../../../../shared' +import { VideoPrivacy } from '../../../../../shared/models/videos' import { AuthService, ServerService } from '../../core' import { FormReactive } from '../../shared' import { ValidatorMessage } from '../../shared/forms/form-validators' @@ -25,6 +26,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { isUploadingVideo = false videoUploaded = false videoUploadPercents = 0 + videoUploadedId = 0 error: string = null form: FormGroup @@ -33,8 +35,8 @@ export class VideoAddComponent extends FormReactive implements OnInit { userVideoChannels = [] videoPrivacies = [] - firstStepPrivacy = 0 - firstStepChannel = 0 + firstStepPrivacyId = 0 + firstStepChannelId = 0 constructor ( private formBuilder: FormBuilder, @@ -59,7 +61,9 @@ export class VideoAddComponent extends FormReactive implements OnInit { .subscribe( () => { this.videoPrivacies = this.serverService.getVideoPrivacies() - this.firstStepPrivacy = this.videoPrivacies[0].id + + // Public by default + this.firstStepPrivacyId = VideoPrivacy.PUBLIC }) this.authService.userInformationLoaded @@ -72,7 +76,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { if (Array.isArray(videoChannels) === false) return this.userVideoChannels = videoChannels.map(v => ({ id: v.id, label: v.name })) - this.firstStepChannel = this.userVideoChannels[0].id + this.firstStepChannelId = this.userVideoChannels[0].id } ) } @@ -89,14 +93,15 @@ export class VideoAddComponent extends FormReactive implements OnInit { uploadFirstStep () { const videofile = this.videofileInput.nativeElement.files[0] - const name = videofile.name - const privacy = this.firstStepPrivacy.toString() + const name = videofile.name.replace(/\.[^/.]+$/, '') + const privacy = this.firstStepPrivacyId.toString() const nsfw = false - const channelId = this.firstStepChannel.toString() + const channelId = this.firstStepChannelId.toString() const formData = new FormData() formData.append('name', name) - formData.append('privacy', privacy.toString()) + // Put the video "private" -> we wait he validates the second step + formData.append('privacy', VideoPrivacy.PRIVATE.toString()) formData.append('nsfw', '' + nsfw) formData.append('channelId', '' + channelId) formData.append('videofile', videofile) @@ -117,6 +122,8 @@ export class VideoAddComponent extends FormReactive implements OnInit { console.log('Video uploaded.') this.videoUploaded = true + + this.videoUploadedId = event.body.video.id } }, @@ -133,13 +140,16 @@ export class VideoAddComponent extends FormReactive implements OnInit { return } - const video = new VideoEdit(this.form.value) + const video = new VideoEdit() + video.patch(this.form.value) + video.channel = this.firstStepChannelId + video.id = this.videoUploadedId this.videoService.updateVideo(video) .subscribe( () => { this.notificationsService.success('Success', 'Video published.') - this.router.navigate([ '/videos/watch', video.uuid ]) + this.router.navigate([ '/videos/watch', video.id ]) }, err => { diff --git a/client/src/app/videos/+video-edit/video-update.component.html b/client/src/app/videos/+video-edit/video-update.component.html index 3163495bf..261b8a130 100644 --- a/client/src/app/videos/+video-edit/video-update.component.html +++ b/client/src/app/videos/+video-edit/video-update.component.html @@ -11,9 +11,9 @@ >
-
+
- +
diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index 583da4685..dfed4768c 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -78,7 +78,7 @@
-
+
Show more 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 87db023bf..d4e3ec014 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -219,6 +219,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy { } private setVideoDescriptionHTML () { + if (!this.video.description) { + this.videoHTMLDescription = '' + return + } + this.videoHTMLDescription = this.markdownService.markdownToHTML(this.video.description) } diff --git a/client/src/app/videos/shared/index.ts b/client/src/app/videos/shared/index.ts index 3c72ed895..7a66944b9 100644 --- a/client/src/app/videos/shared/index.ts +++ b/client/src/app/videos/shared/index.ts @@ -1,2 +1 @@ export * from './markdown.service' -export * from './video-description.component' diff --git a/client/src/app/videos/shared/video-description.component.html b/client/src/app/videos/shared/video-description.component.html deleted file mode 100644 index da66a9753..000000000 --- a/client/src/app/videos/shared/video-description.component.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/client/src/app/videos/shared/video-description.component.scss b/client/src/app/videos/shared/video-description.component.scss deleted file mode 100644 index 6ef81ae58..000000000 --- a/client/src/app/videos/shared/video-description.component.scss +++ /dev/null @@ -1,19 +0,0 @@ -textarea { - @include peertube-input-text(100%); - - font-size: 15px; - height: 150px; -} - -.previews /deep/ { - font-size: 15px !important; - - .nav { - margin-top: 10px; - } - - .tab-content { - min-height: 75px; - padding: 5px; - } -} diff --git a/client/src/app/videos/shared/video-description.component.ts b/client/src/app/videos/shared/video-description.component.ts deleted file mode 100644 index d9ffb7800..000000000 --- a/client/src/app/videos/shared/video-description.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Component, forwardRef, Input, OnInit } from '@angular/core' -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' -import { Subject } from 'rxjs/Subject' -import 'rxjs/add/operator/debounceTime' -import 'rxjs/add/operator/distinctUntilChanged' - -import { truncate } from 'lodash' - -import { MarkdownService } from './markdown.service' - -@Component({ - selector: 'my-video-description', - templateUrl: './video-description.component.html', - styleUrls: [ './video-description.component.scss' ], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => VideoDescriptionComponent), - multi: true - } - ] -}) - -export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { - @Input() description = '' - truncatedDescriptionHTML = '' - descriptionHTML = '' - - private descriptionChanged = new Subject() - - constructor (private markdownService: MarkdownService) {} - - ngOnInit () { - this.descriptionChanged - .debounceTime(150) - .distinctUntilChanged() - .subscribe(() => this.updateDescriptionPreviews()) - - this.descriptionChanged.next(this.description) - } - - propagateChange = (_: any) => { /* empty */ } - - writeValue (description: string) { - this.description = description - - this.descriptionChanged.next(this.description) - } - - registerOnChange (fn: (_: any) => void) { - this.propagateChange = fn - } - - registerOnTouched () { - // Unused - } - - onModelChange () { - this.propagateChange(this.description) - - this.descriptionChanged.next(this.description) - } - - private updateDescriptionPreviews () { - this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 })) - this.descriptionHTML = this.markdownService.markdownToHTML(this.description) - } -} diff --git a/client/src/sass/_mixins.scss b/client/src/sass/_mixins.scss index 121e16e10..d9c9e45ec 100644 --- a/client/src/sass/_mixins.scss +++ b/client/src/sass/_mixins.scss @@ -1,5 +1,5 @@ @mixin disable-default-a-behaviour { - &:hover, &:focus { + &:hover, &:focus, &:active { text-decoration: none !important; outline: none !important; } @@ -23,13 +23,15 @@ color: #fff; background-color: $orange-color; - &:hover, &:active, &:focus, &[disabled], &.disabled { + &:hover, &:active, &:focus { color: #fff; background-color: $orange-hoover-color; } &[disabled], &.disabled { cursor: default; + color: #fff; + background-color: #C6C6C6; } } diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 0c999d659..3c5a00309 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -86,6 +86,10 @@ label { margin-top: 30px; margin-bottom: 25px; } + + &:hover, &:active, &:focus { + color: #000; +} } // On small screen, menu is absolute and displayed over the page -- cgit v1.2.3 From 04e0fc488826f505a8de3ce99113f3cb2fcec147 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 8 Dec 2017 10:41:49 +0100 Subject: Begin admin design --- client/src/app/+admin/admin.component.html | 27 +++++ client/src/app/+admin/admin.component.scss | 0 client/src/app/+admin/admin.component.ts | 26 ++++- .../followers-list/followers-list.component.html | 26 ++--- .../following-add/following-add.component.html | 53 ++++----- .../following-list/following-list.component.html | 34 +++--- .../src/app/+admin/follows/follows.component.scss | 18 --- .../users/user-edit/user-edit.component.html | 123 ++++++++++----------- .../users/user-edit/user-edit.component.scss | 18 +++ .../users/user-list/user-list.component.html | 63 +++++------ .../users/user-list/user-list.component.scss | 22 +++- client/src/app/app.component.html | 3 +- client/src/app/app.module.ts | 3 +- client/src/app/header/header.component.scss | 7 +- client/src/app/menu/index.ts | 1 - client/src/app/menu/menu-admin.component.html | 35 ------ client/src/app/menu/menu-admin.component.ts | 33 ------ .../shared/video-description.component.scss | 34 ------ client/src/assets/images/admin/add.svg | 13 +++ client/src/sass/application.scss | 42 ++++++- 20 files changed, 288 insertions(+), 293 deletions(-) create mode 100644 client/src/app/+admin/admin.component.html create mode 100644 client/src/app/+admin/admin.component.scss delete mode 100644 client/src/app/menu/menu-admin.component.html delete mode 100644 client/src/app/menu/menu-admin.component.ts create mode 100644 client/src/assets/images/admin/add.svg (limited to 'client') diff --git a/client/src/app/+admin/admin.component.html b/client/src/app/+admin/admin.component.html new file mode 100644 index 000000000..0bf4c8aac --- /dev/null +++ b/client/src/app/+admin/admin.component.html @@ -0,0 +1,27 @@ + diff --git a/client/src/app/+admin/admin.component.scss b/client/src/app/+admin/admin.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts index ecd62ee61..75cd50cc7 100644 --- a/client/src/app/+admin/admin.component.ts +++ b/client/src/app/+admin/admin.component.ts @@ -1,7 +1,31 @@ import { Component } from '@angular/core' +import { UserRight } from '../../../../shared' +import { AuthService } from '../core/auth/auth.service' @Component({ - template: '' + templateUrl: './admin.component.html', + styleUrls: [ './admin.component.scss' ] }) export class AdminComponent { + constructor (private auth: AuthService) {} + + hasUsersRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_USERS) + } + + hasServerFollowRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_SERVER_FOLLOW) + } + + hasVideoAbusesRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) + } + + hasVideoBlacklistRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) + } + + hasJobsRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS) + } } diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html index 473801822..ea5380ff7 100644 --- a/client/src/app/+admin/follows/followers-list/followers-list.component.html +++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html @@ -1,16 +1,12 @@ -
-
-

Followers list

+

Followers list

- - - - - - - -
-
+ + + + + + + diff --git a/client/src/app/+admin/follows/following-add/following-add.component.html b/client/src/app/+admin/follows/following-add/following-add.component.html index 8e7dddc11..65c1eda0c 100644 --- a/client/src/app/+admin/follows/following-add/following-add.component.html +++ b/client/src/app/+admin/follows/following-add/following-add.component.html @@ -1,35 +1,30 @@ -
-
+

Add following

-

Add following

+
{{ error }}
-
{{ error }}
+
+
+ - -
- +
+ + + + + +
-
- - - - - -
- -
- It should be a valid host. -
-
- -
- It seems that you are not on a HTTPS server. Your webserver need to have TLS activated in order to follow servers. -
+
+ It should be a valid host. +
+
- -
+
+ It seems that you are not on a HTTPS server. Your webserver need to have TLS activated in order to follow servers.
-
+ + + diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html index a73084312..85c7c3af1 100644 --- a/client/src/app/+admin/follows/following-list/following-list.component.html +++ b/client/src/app/+admin/follows/following-list/following-list.component.html @@ -1,20 +1,16 @@ -
-
-

Following list

+

Following list

- - - - - - - - - - - -
-
+ + + + + + + + + + + diff --git a/client/src/app/+admin/follows/follows.component.scss b/client/src/app/+admin/follows/follows.component.scss index d8ab41975..242effd85 100644 --- a/client/src/app/+admin/follows/follows.component.scss +++ b/client/src/app/+admin/follows/follows.component.scss @@ -1,21 +1,3 @@ .follows-menu { margin-top: 20px; } - -tabset /deep/ { - .nav-link { - padding: 0; - } - - .tab-link { - display: block; - text-align: center; - height: 40px; - width: 120px; - line-height: 40px; - - &:hover, &:active, &:focus { - text-decoration: none !important; - } - } -} diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html index 349be13c1..ed27ea745 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.html +++ b/client/src/app/+admin/users/user-edit/user-edit.component.html @@ -1,73 +1,68 @@ -
-
+
Add user
+
Edit user {{ username }}
-

Add user

-

Edit user {{ username }}

+
{{ error }}
-
{{ error }}
- -
-
- - -
- {{ formErrors.username }} -
-
- -
- - -
- {{ formErrors.email }} -
-
+ +
+ + +
+ {{ formErrors.username }} +
+
-
- - -
- {{ formErrors.password }} -
-
+
+ + +
+ {{ formErrors.email }} +
+
-
- - +
+ + +
+ {{ formErrors.password }} +
+
-
- {{ formErrors.role }} -
-
+
+ + -
- - +
+ {{ formErrors.role }} +
+
-
- Transcoding is enabled on server. The video quota only take in account original video.
- In maximum, this user could use ~ {{ computeQuotaWithTranscoding() | bytes }}. -
-
+
+ + - - +
+ Transcoding is enabled on server. The video quota only take in account original video.
+ In maximum, this user could use ~ {{ computeQuotaWithTranscoding() | bytes }}. +
-
+ + + diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.scss b/client/src/app/+admin/users/user-edit/user-edit.component.scss index 401caa0c6..68d270c19 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.scss +++ b/client/src/app/+admin/users/user-edit/user-edit.component.scss @@ -1,3 +1,21 @@ +.admin-sub-title { + margin-bottom: 30px; +} + +input:not([type=submit]) { + @include peertube-input-text(340px); + display: block; +} + +select { + @include peertube-select(340px); +} + +input[type=submit] { + @include peertube-button; + @include orange-button; +} + .transcoding-information { margin-top: 5px; font-size: 11px; 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 16a8a8033..a100ddfaa 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,35 +1,32 @@ -
-
+
+
Users list
-

Users list

- - - - - - - - - - - - - - - - - - - - - - - - - Add user - -
+ + + 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 index 71adef653..54ecb61b4 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.scss +++ b/client/src/app/+admin/users/user-list/user-list.component.scss @@ -1,3 +1,21 @@ -.add-user { - margin-top: 10px; +.sub-header { + display: flex; + align-items: center; + margin-bottom: 30px; + + .admin-sub-title { + flex-grow: 1; + } + + .add-button { + @include peertube-button-link; + @include orange-button; + + .icon.icon-add { + @include icon(22px); + + margin-right: 3px; + background-image: url('../../../../assets/images/admin/add.svg'); + } + } } diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index b095e44d6..cb1f4e4ef 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -19,8 +19,7 @@
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index ee7cb0c8a..1326e3411 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -20,7 +20,7 @@ import { LoginModule } from './login' import { SignupModule } from './signup' import { SharedModule } from './shared' import { VideosModule } from './videos' -import { MenuComponent, MenuAdminComponent } from './menu' +import { MenuComponent } from './menu' import { HeaderComponent } from './header' export function metaFactory (): MetaLoader { @@ -52,7 +52,6 @@ const APP_PROVIDERS = [ AppComponent, MenuComponent, - MenuAdminComponent, HeaderComponent ], imports: [ diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss index d1c59e8d1..ed8695eab 100644 --- a/client/src/app/header/header.component.scss +++ b/client/src/app/header/header.component.scss @@ -27,10 +27,9 @@ margin-right: 25px; .icon.icon-upload { - display: inline-block; - background: url('../../assets/images/header/upload.svg') no-repeat; - background-size: contain; - width: 22px; + @include icon(22px); + + background-image: url('../../assets/images/header/upload.svg'); height: 24px; vertical-align: middle; margin-right: 6px; diff --git a/client/src/app/menu/index.ts b/client/src/app/menu/index.ts index c905ed20a..421271c12 100644 --- a/client/src/app/menu/index.ts +++ b/client/src/app/menu/index.ts @@ -1,2 +1 @@ export * from './menu.component' -export * from './menu-admin.component' diff --git a/client/src/app/menu/menu-admin.component.html b/client/src/app/menu/menu-admin.component.html deleted file mode 100644 index 9857b2e3e..000000000 --- a/client/src/app/menu/menu-admin.component.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - diff --git a/client/src/app/menu/menu-admin.component.ts b/client/src/app/menu/menu-admin.component.ts deleted file mode 100644 index 1babf5eb6..000000000 --- a/client/src/app/menu/menu-admin.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Component } from '@angular/core' - -import { AuthService } from '../core/auth/auth.service' -import { UserRight } from '../../../../shared' - -@Component({ - selector: 'my-menu-admin', - templateUrl: './menu-admin.component.html', - styleUrls: [ './menu.component.scss' ] -}) -export class MenuAdminComponent { - constructor (private auth: AuthService) {} - - hasUsersRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_USERS) - } - - hasServerFollowRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_SERVER_FOLLOW) - } - - hasVideoAbusesRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) - } - - hasVideoBlacklistRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) - } - - hasJobsRight () { - return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS) - } -} diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.scss b/client/src/app/videos/+video-edit/shared/video-description.component.scss index 38506bb46..8155cbca7 100644 --- a/client/src/app/videos/+video-edit/shared/video-description.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-description.component.scss @@ -5,37 +5,3 @@ textarea { font-size: 15px; height: 150px; } - -.previews /deep/ { - font-size: 15px !important; - - .nav { - margin-top: 10px; - font-size: 16px !important; - border: none !important; - - .nav-item .nav-link { - color: #000 !important; - height: 30px !important; - margin-right: 30px; - padding: 0 15px; - display: flex; - align-items: center; - border-radius: 3px; - border: none !important; - - &.active, &:hover { - background-color: #F0F0F0; - } - - &.active { - font-weight: $font-semibold !important; - } - } - } - - .tab-content { - min-height: 75px; - padding: 15px; - } -} diff --git a/client/src/assets/images/admin/add.svg b/client/src/assets/images/admin/add.svg new file mode 100644 index 000000000..42b269c43 --- /dev/null +++ b/client/src/assets/images/admin/add.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 3c5a00309..e7b4024a7 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -89,7 +89,12 @@ label { &:hover, &:active, &:focus { color: #000; + } } + +.admin-sub-title { + font-size: 20px; + font-weight: bold; } // On small screen, menu is absolute and displayed over the page @@ -132,7 +137,7 @@ label { to { -moz-transform: rotate(360deg);} } -/* ngprime data table customizations */ +// ngprime data table customizations p-datatable { .action-cell { text-align: center; @@ -143,6 +148,7 @@ p-datatable { } } +// Bootstrap customizations .dropdown-menu { border-radius: 3px; box-shadow: 0 3px 6px; @@ -167,6 +173,40 @@ p-datatable { } } +.nav { + margin-top: 10px; + font-size: 16px !important; + border: none !important; + + .nav-item .nav-link { + height: 30px !important; + margin-right: 30px; + padding: 0 15px; + display: flex; + align-items: center; + border-radius: 3px; + border: none !important; + + &, & a { + color: #000 !important; + } + + &.active, &:hover { + background-color: #F0F0F0; + } + + &.active { + font-weight: $font-semibold !important; + } + } + + .tab-content { + min-height: 75px; + padding: 15px; + } +} + + .orange-button { @include peertube-button; @include orange-button; -- cgit v1.2.3 From cd83ea1b908efe594c1e03f886c0dc4742b91360 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 8 Dec 2017 14:34:17 +0100 Subject: Design admin data tables --- .../+admin/jobs/jobs-list/jobs-list.component.html | 36 ++++---- .../+admin/jobs/jobs-list/jobs-list.component.scss | 3 + .../+admin/jobs/jobs-list/jobs-list.component.ts | 24 +++--- client/src/app/+admin/jobs/shared/job.service.ts | 7 ++ .../users/user-list/user-list.component.html | 16 ++-- .../users/user-list/user-list.component.scss | 10 --- .../account-videos/account-videos.component.html | 12 +-- .../account-videos/account-videos.component.scss | 27 +----- client/src/app/shared/misc/button.component.scss | 27 ++++++ .../app/shared/misc/delete-button.component.html | 4 + .../src/app/shared/misc/delete-button.component.ts | 10 +++ .../src/app/shared/misc/edit-button.component.html | 4 + .../src/app/shared/misc/edit-button.component.ts | 11 +++ client/src/app/shared/misc/utils.ts | 5 ++ client/src/app/shared/shared.module.ts | 6 ++ client/src/assets/images/account/delete-grey.svg | 14 --- client/src/assets/images/account/delete-white.svg | 14 --- client/src/assets/images/global/delete-grey.svg | 14 +++ client/src/assets/images/global/delete-white.svg | 14 +++ client/src/sass/application.scss | 99 +++++++++++++++++++++- 20 files changed, 242 insertions(+), 115 deletions(-) create mode 100644 client/src/app/+admin/jobs/jobs-list/jobs-list.component.scss create mode 100644 client/src/app/shared/misc/button.component.scss create mode 100644 client/src/app/shared/misc/delete-button.component.html create mode 100644 client/src/app/shared/misc/delete-button.component.ts create mode 100644 client/src/app/shared/misc/edit-button.component.html create mode 100644 client/src/app/shared/misc/edit-button.component.ts delete mode 100644 client/src/assets/images/account/delete-grey.svg delete mode 100644 client/src/assets/images/account/delete-white.svg create mode 100644 client/src/assets/images/global/delete-grey.svg create mode 100644 client/src/assets/images/global/delete-white.svg (limited to 'client') diff --git a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html index a90267172..29103c06b 100644 --- a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html +++ b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html @@ -1,18 +1,20 @@ -
-
-

Jobs list

- - - - - - - - - - -
+
+
Jobs list
+ + + + + + + +
{{ job.handlerInputData }}
+
+
+ + + +
diff --git a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.scss b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.scss new file mode 100644 index 000000000..9dde13216 --- /dev/null +++ b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.scss @@ -0,0 +1,3 @@ +pre { + font-size: 13px; +} diff --git a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts index 88fe259fb..f93847f29 100644 --- a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts +++ b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts @@ -1,22 +1,24 @@ -import { Component } from '@angular/core' +import { Component, OnInit } from '@angular/core' import { NotificationsService } from 'angular2-notifications' import { SortMeta } from 'primeng/primeng' import { Job } from '../../../../../../shared/index' import { RestPagination, RestTable } from '../../../shared' +import { viewportHeight } from '../../../shared/misc/utils' import { JobService } from '../shared' import { RestExtractor } from '../../../shared/rest/rest-extractor.service' @Component({ selector: 'my-jobs-list', templateUrl: './jobs-list.component.html', - styleUrls: [ ] + styleUrls: [ './jobs-list.component.scss' ] }) -export class JobsListComponent extends RestTable { +export class JobsListComponent extends RestTable implements OnInit { jobs: Job[] = [] totalRecords = 0 - rowsPerPage = 10 + rowsPerPage = 20 sort: SortMeta = { field: 'createdAt', order: 1 } pagination: RestPagination = { count: this.rowsPerPage, start: 0 } + scrollHeight = '' constructor ( private notificationsService: NotificationsService, @@ -26,10 +28,14 @@ export class JobsListComponent extends RestTable { super() } + ngOnInit () { + // 270 -> headers + footer... + this.scrollHeight = (viewportHeight() - 380) + 'px' + } + protected loadData () { this.jobsService .getJobs(this.pagination, this.sort) - .map(res => this.restExtractor.applyToResultListData(res, this.formatJob.bind(this))) .subscribe( resultList => { this.jobs = resultList.data @@ -39,12 +45,4 @@ export class JobsListComponent extends RestTable { err => this.notificationsService.error('Error', err.message) ) } - - private formatJob (job: Job) { - const handlerInputData = JSON.stringify(job.handlerInputData) - - return Object.assign(job, { - handlerInputData - }) - } } diff --git a/client/src/app/+admin/jobs/shared/job.service.ts b/client/src/app/+admin/jobs/shared/job.service.ts index 49f1ab6f5..0cfbdbbea 100644 --- a/client/src/app/+admin/jobs/shared/job.service.ts +++ b/client/src/app/+admin/jobs/shared/job.service.ts @@ -25,6 +25,13 @@ export class JobService { return this.authHttp.get>(JobService.BASE_JOB_URL, { params }) .map(res => this.restExtractor.convertResultListDateToHuman(res)) + .map(res => this.restExtractor.applyToResultListData(res, this.prettyPrintData)) .catch(err => this.restExtractor.handleError(err)) } + + private prettyPrintData (obj: Job) { + const handlerInputData = JSON.stringify(obj.handlerInputData, null, 2) + + return Object.assign(obj, { handlerInputData }) + } } 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 a100ddfaa..5a19edfde 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,4 +1,4 @@ -
+
Users list
@@ -17,16 +17,10 @@ - - - - - - - - - - + + + + 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 index 54ecb61b4..8b22f67ff 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.scss +++ b/client/src/app/+admin/users/user-list/user-list.component.scss @@ -1,12 +1,3 @@ -.sub-header { - display: flex; - align-items: center; - margin-bottom: 30px; - - .admin-sub-title { - flex-grow: 1; - } - .add-button { @include peertube-button-link; @include orange-button; @@ -18,4 +9,3 @@ background-image: url('../../../../assets/images/admin/add.svg'); } } -} diff --git a/client/src/app/account/account-videos/account-videos.component.html b/client/src/app/account/account-videos/account-videos.component.html index 030c2f19c..641fcb38a 100644 --- a/client/src/app/account/account-videos/account-videos.component.html +++ b/client/src/app/account/account-videos/account-videos.component.html @@ -22,7 +22,7 @@ Cancel - + Delete @@ -30,15 +30,9 @@
- - - Delete - + - - - Edit - +
diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index 083918e29..670fe992c 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss @@ -6,17 +6,7 @@ } } -.action-button { - @include peertube-button-link; - - font-size: 15px; - font-weight: $font-semibold; - color: #585858; - background-color: #E5E5E5; - - &:hover { - background-color: #EFEFEF; - } +/deep/ .action-button { &.action-button-delete { margin-right: 10px; @@ -32,21 +22,8 @@ } .icon { - @include icon(21px); - - position: relative; - top: -2px; - - &.icon-edit { - background-image: url('../../../assets/images/global/edit.svg'); - } - - &.icon-delete-grey { - background-image: url('../../../assets/images/account/delete-grey.svg'); - } - &.icon-delete-white { - background-image: url('../../../assets/images/account/delete-white.svg'); + background-image: url('../../../assets/images/global/delete-white.svg'); } } } diff --git a/client/src/app/shared/misc/button.component.scss b/client/src/app/shared/misc/button.component.scss new file mode 100644 index 000000000..5fcae4f10 --- /dev/null +++ b/client/src/app/shared/misc/button.component.scss @@ -0,0 +1,27 @@ +.action-button { + @include peertube-button-link; + + font-size: 15px; + font-weight: $font-semibold; + color: #585858; + background-color: #E5E5E5; + + &:hover { + background-color: #EFEFEF; + } + + .icon { + @include icon(21px); + + position: relative; + top: -2px; + + &.icon-edit { + background-image: url('../../../assets/images/global/edit.svg'); + } + + &.icon-delete-grey { + background-image: url('../../../assets/images/global/delete-grey.svg'); + } + } +} diff --git a/client/src/app/shared/misc/delete-button.component.html b/client/src/app/shared/misc/delete-button.component.html new file mode 100644 index 000000000..3db483882 --- /dev/null +++ b/client/src/app/shared/misc/delete-button.component.html @@ -0,0 +1,4 @@ + + + Delete + diff --git a/client/src/app/shared/misc/delete-button.component.ts b/client/src/app/shared/misc/delete-button.component.ts new file mode 100644 index 000000000..e04039f69 --- /dev/null +++ b/client/src/app/shared/misc/delete-button.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core' + +@Component({ + selector: 'my-delete-button', + styleUrls: [ './button.component.scss' ], + templateUrl: './delete-button.component.html' +}) + +export class DeleteButtonComponent { +} diff --git a/client/src/app/shared/misc/edit-button.component.html b/client/src/app/shared/misc/edit-button.component.html new file mode 100644 index 000000000..6e9564bd7 --- /dev/null +++ b/client/src/app/shared/misc/edit-button.component.html @@ -0,0 +1,4 @@ + + + Edit + diff --git a/client/src/app/shared/misc/edit-button.component.ts b/client/src/app/shared/misc/edit-button.component.ts new file mode 100644 index 000000000..201a618ec --- /dev/null +++ b/client/src/app/shared/misc/edit-button.component.ts @@ -0,0 +1,11 @@ +import { Component, Input } from '@angular/core' + +@Component({ + selector: 'my-edit-button', + styleUrls: [ './button.component.scss' ], + templateUrl: './edit-button.component.html' +}) + +export class EditButtonComponent { + @Input() routerLink = [] +} diff --git a/client/src/app/shared/misc/utils.ts b/client/src/app/shared/misc/utils.ts index 2b5c3686e..df9e0381a 100644 --- a/client/src/app/shared/misc/utils.ts +++ b/client/src/app/shared/misc/utils.ts @@ -13,6 +13,11 @@ function getParameterByName (name: string, url: string) { return decodeURIComponent(results[2].replace(/\+/g, ' ')) } +function viewportHeight () { + return Math.max(document.documentElement.clientHeight, window.innerHeight || 0) +} + export { + viewportHeight, getParameterByName } diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 74f6f579d..d0e163f69 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -12,6 +12,8 @@ import { SharedModule as PrimeSharedModule } from 'primeng/components/common/sha import { DataTableModule } from 'primeng/components/datatable/datatable' import { AUTH_INTERCEPTOR_PROVIDER } from './auth' +import { DeleteButtonComponent } from './misc/delete-button.component' +import { EditButtonComponent } from './misc/edit-button.component' import { FromNowPipe } from './misc/from-now.pipe' import { LoaderComponent } from './misc/loader.component' import { NumberFormatterPipe } from './misc/number-formatter.pipe' @@ -44,6 +46,8 @@ import { VideoService } from './video/video.service' LoaderComponent, VideoThumbnailComponent, VideoMiniatureComponent, + DeleteButtonComponent, + EditButtonComponent, NumberFormatterPipe, FromNowPipe ], @@ -66,6 +70,8 @@ import { VideoService } from './video/video.service' LoaderComponent, VideoThumbnailComponent, VideoMiniatureComponent, + DeleteButtonComponent, + EditButtonComponent, NumberFormatterPipe, FromNowPipe diff --git a/client/src/assets/images/account/delete-grey.svg b/client/src/assets/images/account/delete-grey.svg deleted file mode 100644 index 67e9e2ce7..000000000 --- a/client/src/assets/images/account/delete-grey.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/client/src/assets/images/account/delete-white.svg b/client/src/assets/images/account/delete-white.svg deleted file mode 100644 index 9c52de557..000000000 --- a/client/src/assets/images/account/delete-white.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/client/src/assets/images/global/delete-grey.svg b/client/src/assets/images/global/delete-grey.svg new file mode 100644 index 000000000..67e9e2ce7 --- /dev/null +++ b/client/src/assets/images/global/delete-grey.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/assets/images/global/delete-white.svg b/client/src/assets/images/global/delete-white.svg new file mode 100644 index 000000000..9c52de557 --- /dev/null +++ b/client/src/assets/images/global/delete-white.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index e7b4024a7..5277e2070 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -92,6 +92,16 @@ label { } } +.admin-sub-header { + display: flex; + align-items: center; + margin-bottom: 30px; + + .admin-sub-title { + flex-grow: 1; + } +} + .admin-sub-title { font-size: 20px; font-weight: bold; @@ -139,11 +149,96 @@ label { // ngprime data table customizations p-datatable { + font-size: 15px !important; + + .ui-datatable-scrollable-header { + background-color: #fff !important; + } + + .ui-widget-content { + border: none !important; + } + + .ui-datatable-virtual-table { + border-top: none !important; + } + + td { + border: 1px solid #E5E5E5 !important; + padding: 15px; + } + + tr { + background-color: #fff !important; + height: 46px; + + &:hover { + background-color: #f0f0f0 !important; + } + + &:not(:hover) { + .action-cell * { + display: none !important; + } + } + + &:first-child td { + border-top: none !important; + } + } + + th { + border: none !important; + border-bottom: 1px solid #f0f0f0 !important; + text-align: left !important; + padding: 5px 0 5px 15px !important; + font-weight: $font-semibold !important; + color: #000 !important; + + &.ui-state-active, &.ui-sortable-column:hover { + background-color: #f0f0f0 !important; + border: 1px solid #f0f0f0 !important; + } + } + .action-cell { + width: 250px !important; + padding: 0 !important; text-align: center; + } - .glyphicon { - cursor: pointer; + p-paginator { + overflow: hidden; + display: block; + padding-top: 2px; + border: 1px solid #f0f0f0 !important; + border-top: none !important; + + .ui-paginator-bottom { + position: relative; + border: none !important; + border-top: 1px solid #f0f0f0 !important; + box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.16); + height: 40px; + display: flex; + justify-content: center; + align-items: center; + + a { + color: #000 !important; + font-weight: $font-semibold !important; + margin-right: 20px !important; + outline: 0 !important; + border-radius: 3px !important; + padding: 5px 2px !important; + + &.ui-state-active { + &, &:hover, &:active, &:focus { + color: #fff !important; + background-color: $orange-color !important; + } + } + } } } } -- cgit v1.2.3 From e600e1fea275c12f4420e23624804617e61a082c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 8 Dec 2017 15:22:57 +0100 Subject: Design follow admin page --- .../followers-list/followers-list.component.html | 2 - .../followers-list/followers-list.component.scss | 3 - .../following-add/following-add.component.html | 32 +++----- .../following-add/following-add.component.scss | 10 ++- .../following-add/following-add.component.ts | 94 +++++++--------------- .../following-list/following-list.component.html | 4 +- .../src/app/+admin/follows/follows.component.html | 6 +- .../src/app/+admin/follows/follows.component.scss | 5 +- client/src/app/+admin/follows/follows.component.ts | 2 +- .../users/user-edit/user-edit.component.html | 2 +- .../users/user-list/user-list.component.html | 2 +- .../shared/forms/form-validators/host.validator.ts | 10 +-- client/src/sass/_mixins.scss | 1 + client/src/sass/application.scss | 14 ++-- 14 files changed, 73 insertions(+), 114 deletions(-) (limited to 'client') diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html index ea5380ff7..a24039fc6 100644 --- a/client/src/app/+admin/follows/followers-list/followers-list.component.html +++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html @@ -1,5 +1,3 @@ -

Followers list

- Add following -
{{ error }}
-
-
- + +
+ -
- - - - - -
+ -
- It should be a valid host. +
+ {{ hostsError }}
-
- It seems that you are not on a HTTPS server. Your webserver need to have TLS activated in order to follow servers. +
+ It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
- + diff --git a/client/src/app/+admin/follows/following-add/following-add.component.scss b/client/src/app/+admin/follows/following-add/following-add.component.scss index 5fde51636..2cb3efe28 100644 --- a/client/src/app/+admin/follows/following-add/following-add.component.scss +++ b/client/src/app/+admin/follows/following-add/following-add.component.scss @@ -1,7 +1,9 @@ -table { - margin-bottom: 40px; +textarea { + height: 250px; } -.input-group-btn button { - width: 35px; +input[type=submit] { + @include peertube-button; + @include orange-button; } + diff --git a/client/src/app/+admin/follows/following-add/following-add.component.ts b/client/src/app/+admin/follows/following-add/following-add.component.ts index 814c6f1a1..bf842129d 100644 --- a/client/src/app/+admin/follows/following-add/following-add.component.ts +++ b/client/src/app/+admin/follows/following-add/following-add.component.ts @@ -1,9 +1,6 @@ -import { Component, OnInit } from '@angular/core' -import { FormControl, FormGroup } from '@angular/forms' +import { Component } from '@angular/core' import { Router } from '@angular/router' - import { NotificationsService } from 'angular2-notifications' - import { ConfirmService } from '../../../core' import { validateHost } from '../../../shared' import { FollowService } from '../shared' @@ -13,9 +10,9 @@ import { FollowService } from '../shared' templateUrl: './following-add.component.html', styleUrls: [ './following-add.component.scss' ] }) -export class FollowingAddComponent implements OnInit { - form: FormGroup - hosts: string[] = [ ] +export class FollowingAddComponent { + hostsString = '' + hostsError: string = null error: string = null constructor ( @@ -25,76 +22,50 @@ export class FollowingAddComponent implements OnInit { private followService: FollowService ) {} - ngOnInit () { - this.form = new FormGroup({}) - this.addField() - } - - addField () { - this.form.addControl(`host-${this.hosts.length}`, new FormControl('', [ validateHost ])) - this.hosts.push('') - } - - canMakeFriends () { + httpEnabled () { return window.location.protocol === 'https:' } - customTrackBy (index: number, obj: any): any { - return index - } - - displayAddField (index: number) { - return index === (this.hosts.length - 1) - } + onHostsChanged () { + this.hostsError = null - displayRemoveField (index: number) { - return (index !== 0 || this.hosts.length > 1) && index !== (this.hosts.length - 1) - } + const newHostsErrors = [] + const hosts = this.getNotEmptyHosts() - isFormValid () { - // Do not check the last input - for (let i = 0; i < this.hosts.length - 1; i++) { - if (!this.form.controls[`host-${i}`].valid) return false + for (const host of hosts) { + if (validateHost(host) === false) { + newHostsErrors.push(`${host} is not valid`) + } } - const lastIndex = this.hosts.length - 1 - // If the last input (which is not the first) is empty, it's ok - if (this.hosts[lastIndex] === '' && lastIndex !== 0) { - return true - } else { - return this.form.controls[`host-${lastIndex}`].valid + if (newHostsErrors.length !== 0) { + this.hostsError = newHostsErrors.join('. ') } } - removeField (index: number) { - // Remove the last control - this.form.removeControl(`host-${this.hosts.length - 1}`) - this.hosts.splice(index, 1) - } - addFollowing () { this.error = '' - const notEmptyHosts = this.getNotEmptyHosts() - if (notEmptyHosts.length === 0) { - this.error = 'You need to specify at least 1 host.' - return + const hosts = this.getNotEmptyHosts() + if (hosts.length === 0) { + this.error = 'You need to specify hosts to follow.' } - if (!this.isHostsUnique(notEmptyHosts)) { + if (!this.isHostsUnique(hosts)) { this.error = 'Hosts need to be unique.' return } - const confirmMessage = 'Are you sure to make friends with:
- ' + notEmptyHosts.join('
- ') + const confirmMessage = 'If you confirm, you will send a follow request to:
- ' + hosts.join('
- ') this.confirmService.confirm(confirmMessage, 'Follow new server(s)').subscribe( res => { if (res === false) return - this.followService.follow(notEmptyHosts).subscribe( + this.followService.follow(hosts).subscribe( status => { this.notificationsService.success('Success', 'Follow request(s) sent!') - this.router.navigate([ '/admin/follows/following-list' ]) + + setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500) }, err => this.notificationsService.error('Error', err.message) @@ -103,18 +74,15 @@ export class FollowingAddComponent implements OnInit { ) } - private getNotEmptyHosts () { - const notEmptyHosts = [] - - Object.keys(this.form.value).forEach((hostKey) => { - const host = this.form.value[hostKey] - if (host !== '') notEmptyHosts.push(host) - }) - - return notEmptyHosts - } - private isHostsUnique (hosts: string[]) { return hosts.every(host => hosts.indexOf(host) === hosts.lastIndexOf(host)) } + + private getNotEmptyHosts () { + const hosts = this.hostsString + .split('\n') + .filter(host => host && host.length !== 0) // Eject empty hosts + + return hosts + } } diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html index 85c7c3af1..3e70b418c 100644 --- a/client/src/app/+admin/follows/following-list/following-list.component.html +++ b/client/src/app/+admin/follows/following-list/following-list.component.html @@ -1,5 +1,3 @@ -

Following list

- - + diff --git a/client/src/app/+admin/follows/follows.component.html b/client/src/app/+admin/follows/follows.component.html index b67bc9736..1baba5a4d 100644 --- a/client/src/app/+admin/follows/follows.component.html +++ b/client/src/app/+admin/follows/follows.component.html @@ -1,4 +1,6 @@ -
+
+
Manage follows
+ @@ -8,4 +10,6 @@
+ + diff --git a/client/src/app/+admin/follows/follows.component.scss b/client/src/app/+admin/follows/follows.component.scss index 242effd85..835fa3b78 100644 --- a/client/src/app/+admin/follows/follows.component.scss +++ b/client/src/app/+admin/follows/follows.component.scss @@ -1,3 +1,4 @@ -.follows-menu { - margin-top: 20px; +.admin-sub-title { + flex-grow: 0; + margin-right: 30px; } diff --git a/client/src/app/+admin/follows/follows.component.ts b/client/src/app/+admin/follows/follows.component.ts index a1be82585..f29ad384f 100644 --- a/client/src/app/+admin/follows/follows.component.ts +++ b/client/src/app/+admin/follows/follows.component.ts @@ -47,7 +47,7 @@ export class FollowsComponent implements OnInit, AfterViewInit { for (let i = 0; i < this.links.length; i++) { const path = this.links[i].path - if (url.endsWith(path) === true) { + if (url.endsWith(path) === true && this.followsMenuTabs.tabs[i]) { this.followsMenuTabs.tabs[i].active = true return } diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html index ed27ea745..963e2f39a 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.html +++ b/client/src/app/+admin/users/user-edit/user-edit.component.html @@ -64,5 +64,5 @@
- + 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 5a19edfde..b3d90ba1e 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 @@ -18,7 +18,7 @@ - + diff --git a/client/src/app/shared/forms/form-validators/host.validator.ts b/client/src/app/shared/forms/form-validators/host.validator.ts index 03e810fdb..c18a35f9b 100644 --- a/client/src/app/shared/forms/form-validators/host.validator.ts +++ b/client/src/app/shared/forms/form-validators/host.validator.ts @@ -1,14 +1,8 @@ -import { FormControl } from '@angular/forms' - -export function validateHost (c: FormControl) { +export function validateHost (value: string) { // Thanks to http://stackoverflow.com/a/106223 const HOST_REGEXP = new RegExp( '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' ) - return HOST_REGEXP.test(c.value) ? null : { - validateHost: { - valid: false - } - } + return HOST_REGEXP.test(value) } diff --git a/client/src/sass/_mixins.scss b/client/src/sass/_mixins.scss index d9c9e45ec..2a7192fb2 100644 --- a/client/src/sass/_mixins.scss +++ b/client/src/sass/_mixins.scss @@ -59,6 +59,7 @@ text-align: center; padding: 0 17px 0 13px; cursor: pointer; + outline: 0; } @mixin peertube-button-link { diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 5277e2070..ecbb8dac5 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -269,21 +269,25 @@ p-datatable { } .nav { - margin-top: 10px; font-size: 16px !important; border: none !important; .nav-item .nav-link { - height: 30px !important; margin-right: 30px; - padding: 0 15px; - display: flex; - align-items: center; + padding: 0; border-radius: 3px; border: none !important; + .tab-link { + display: flex !important; + align-items: center; + height: 30px !important; + padding: 0 15px; + } + &, & a { color: #000 !important; + @include disable-default-a-behaviour; } &.active, &:hover { -- cgit v1.2.3 From f595d3947708114deeed4312cc5ffd285745b090 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 8 Dec 2017 17:31:21 +0100 Subject: Finish admin design --- .../following-list/following-list.component.html | 2 +- .../+admin/jobs/jobs-list/jobs-list.component.html | 2 +- .../video-abuse-list.component.html | 41 ++++++++++------------ .../video-abuse-list.component.scss | 6 ++++ .../video-abuse-list/video-abuse-list.component.ts | 3 +- .../video-blacklist-list.component.html | 2 +- client/src/app/core/auth/auth.service.ts | 22 +++++------- client/src/app/shared/video/abstract-video-list.ts | 6 +++- .../shared/video-description.component.html | 2 +- .../shared/video-description.component.scss | 17 +++++++++ .../shared/video-description.component.ts | 2 ++ client/src/sass/application.scss | 21 ++++------- 12 files changed, 70 insertions(+), 56 deletions(-) create mode 100644 client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss (limited to 'client') diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html index 3e70b418c..2b6cc9113 100644 --- a/client/src/app/+admin/follows/following-list/following-list.component.html +++ b/client/src/app/+admin/follows/following-list/following-list.component.html @@ -6,7 +6,7 @@ - + diff --git a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html index 29103c06b..7aa5f4254 100644 --- a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html +++ b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html @@ -8,7 +8,7 @@ > - +
{{ job.handlerInputData }}
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html index ab0a9d99f..d655a5e9b 100644 --- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html +++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html @@ -1,24 +1,19 @@ -
-
- -

Video abuses list

- - - - - - - - - - {{ videoAbuse.videoId }} - - - - - -
+
+
Video abuses list
+ + + + + + + + + + {{ videoAbuse.videoName }} + + + diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss new file mode 100644 index 000000000..6a4762650 --- /dev/null +++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss @@ -0,0 +1,6 @@ +/deep/ a { + + &, &:hover, &:active, &:focus { + color: #000; + } +} diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts index 654603d01..b4d3bbd24 100644 --- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts +++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts @@ -8,7 +8,8 @@ import { VideoAbuse } from '../../../../../../shared' @Component({ selector: 'my-video-abuse-list', - templateUrl: './video-abuse-list.component.html' + templateUrl: './video-abuse-list.component.html', + styleUrls: [ './video-abuse-list.component.scss'] }) export class VideoAbuseListComponent extends RestTable implements OnInit { videoAbuses: VideoAbuse[] = [] diff --git a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html index 05d116798..1d813fa07 100644 --- a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html +++ b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html @@ -18,7 +18,7 @@ - + diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 0db197f02..e887dde1f 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts @@ -169,19 +169,15 @@ export class AuthService { return this.http.post(AuthService.BASE_TOKEN_URL, body, { headers }) .map(res => this.handleRefreshToken(res)) - .catch(res => { - // The refresh token is invalid? - if (res.status === 400 && res.error.error === 'invalid_grant') { - console.error('Cannot refresh token -> logout...') - this.logout() - this.router.navigate(['/login']) - - return Observable.throw({ - error: 'You need to reconnect.' - }) - } - - return this.restExtractor.handleError(res) + .catch(err => { + console.error(err) + console.log('Cannot refresh token -> logout...') + this.logout() + this.router.navigate(['/login']) + + return Observable.throw({ + error: 'You need to reconnect.' + }) }) } diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts index ee1ed2cb2..ba1635a18 100644 --- a/client/src/app/shared/video/abstract-video-list.ts +++ b/client/src/app/shared/video/abstract-video-list.ts @@ -62,7 +62,7 @@ export abstract class AbstractVideoList implements OnInit { observable.subscribe( ({ videos, totalVideos }) => { // Paging is too high, return to the first one - if (totalVideos <= ((this.pagination.currentPage - 1) * this.pagination.itemsPerPage)) { + if (this.pagination.currentPage > 1 && totalVideos <= ((this.pagination.currentPage - 1) * this.pagination.itemsPerPage)) { this.pagination.currentPage = 1 this.setNewRouteParams() return this.reloadVideos() @@ -82,6 +82,10 @@ export abstract class AbstractVideoList implements OnInit { } protected hasMoreVideos () { + // No results + if (this.pagination.totalItems === 0) return false + + // Not loaded yet if (!this.pagination.totalItems) return true const maxPage = this.pagination.totalItems / this.pagination.itemsPerPage diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.html b/client/src/app/videos/+video-edit/shared/video-description.component.html index da66a9753..5d05467be 100644 --- a/client/src/app/videos/+video-edit/shared/video-description.component.html +++ b/client/src/app/videos/+video-edit/shared/video-description.component.html @@ -1,6 +1,6 @@ diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.scss b/client/src/app/videos/+video-edit/shared/video-description.component.scss index 8155cbca7..2a4c8d189 100644 --- a/client/src/app/videos/+video-edit/shared/video-description.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-description.component.scss @@ -4,4 +4,21 @@ textarea { padding: 5px 15px; font-size: 15px; height: 150px; + margin-bottom: 15px; } + +/deep/ { + .nav-link { + display: flex !important; + align-items: center; + height: 30px !important; + padding: 0 15px !important; + } + + .tab-content { + min-height: 75px; + padding: 15px; + font-size: 15px; + } +} + diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.ts b/client/src/app/videos/+video-edit/shared/video-description.component.ts index 8dfb74b2a..9b77a27e6 100644 --- a/client/src/app/videos/+video-edit/shared/video-description.component.ts +++ b/client/src/app/videos/+video-edit/shared/video-description.component.ts @@ -60,6 +60,8 @@ export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { } private updateDescriptionPreviews () { + if (!this.description) return + this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 })) this.descriptionHTML = this.markdownService.markdownToHTML(this.description) } diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index ecbb8dac5..5a4aa4cd9 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -165,7 +165,7 @@ p-datatable { td { border: 1px solid #E5E5E5 !important; - padding: 15px; + padding-left: 15px !important; } tr { @@ -185,6 +185,10 @@ p-datatable { &:first-child td { border-top: none !important; } + + &:last-child td { + border-bottom: none !important; + } } th { @@ -198,6 +202,7 @@ p-datatable { &.ui-state-active, &.ui-sortable-column:hover { background-color: #f0f0f0 !important; border: 1px solid #f0f0f0 !important; + border-width: 0 1px !important; } } @@ -208,17 +213,10 @@ p-datatable { } p-paginator { - overflow: hidden; - display: block; - padding-top: 2px; - border: 1px solid #f0f0f0 !important; - border-top: none !important; - .ui-paginator-bottom { position: relative; border: none !important; - border-top: 1px solid #f0f0f0 !important; - box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.16); + border: 1px solid #f0f0f0 !important; height: 40px; display: flex; justify-content: center; @@ -298,11 +296,6 @@ p-datatable { font-weight: $font-semibold !important; } } - - .tab-content { - min-height: 75px; - padding: 15px; - } } -- cgit v1.2.3 From 3daf400219fe8cc94025886ba14876bc59a45244 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 11 Dec 2017 08:50:43 +0100 Subject: Responsive homepage --- client/src/app/app.component.html | 7 ++----- client/src/app/app.component.scss | 2 ++ client/src/app/header/header.component.html | 2 +- client/src/app/header/header.component.scss | 21 +++++++++++++++++++++ .../src/app/shared/video/abstract-video-list.html | 1 + .../src/app/shared/video/abstract-video-list.scss | 7 +++++++ client/src/sass/application.scss | 19 +++++++++++++------ 7 files changed, 47 insertions(+), 12 deletions(-) (limited to 'client') diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index cb1f4e4ef..da4273dda 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -16,11 +16,8 @@
-
- - +
+
diff --git a/client/src/app/app.component.scss b/client/src/app/app.component.scss index 10af9debe..008c6d1f0 100644 --- a/client/src/app/app.component.scss +++ b/client/src/app/app.component.scss @@ -61,6 +61,8 @@ } @media screen and (max-width: 500px) { + width: 70px; + #peertube-title { display: none; } diff --git a/client/src/app/header/header.component.html b/client/src/app/header/header.component.html index aa72fb68a..c853d2b1b 100644 --- a/client/src/app/header/header.component.html +++ b/client/src/app/header/header.component.html @@ -6,5 +6,5 @@ - Upload + Upload diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss index ed8695eab..5f64ede98 100644 --- a/client/src/app/header/header.component.scss +++ b/client/src/app/header/header.component.scss @@ -6,6 +6,14 @@ &::placeholder { color: #000; } + + @media screen and (max-width: 800px) { + width: calc(100% - 150px); + } + + @media screen and (max-width: 400px) { + width: calc(100% - 70px); + } } .icon.icon-search { @@ -34,4 +42,17 @@ vertical-align: middle; margin-right: 6px; } + + @media screen and (max-width: 400px) { + margin-right: 10px; + padding: 0 10px; + + .icon.icon-upload { + margin-right: 0; + } + + .upload-button-label { + display: none; + } + } } diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html index 5d07a276b..5761f2c81 100644 --- a/client/src/app/shared/video/abstract-video-list.html +++ b/client/src/app/shared/video/abstract-video-list.html @@ -4,6 +4,7 @@
Date: Mon, 11 Dec 2017 09:08:15 +0100 Subject: Videos watch responsive --- .../videos/+video-watch/video-watch.component.html | 2 +- .../videos/+video-watch/video-watch.component.scss | 31 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'client') diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index dfed4768c..43b175acc 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -1,7 +1,7 @@
- +
Video not found :'(
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index 2ccfd2749..fcf625d6c 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -7,9 +7,14 @@ width: 888px; height: 500px; + @media screen and (max-width: 800px) { + height: auto; + } + // VideoJS create an inner video player video { outline: 0; + position: relative !important; } } } @@ -210,3 +215,29 @@ } } + +@media screen and (max-width: 800px) { + .other-videos { + display: none; + } + + .video-bottom { + .video-info { + .video-info-name-actions { + align-items: left; + flex-direction: column; + margin-bottom: 30px; + } + + .video-info-date-views-bar { + align-items: left; + flex-direction: column; + margin-bottom: 30px; + + .video-info-likes-dislikes-bar { + margin-top: 0; + } + } + } + } +} -- cgit v1.2.3 From a86309b4af9ad8391822f628d74fc5c7d1a01974 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 11 Dec 2017 09:13:01 +0100 Subject: Responsive player --- client/src/assets/player/peertube-videojs-plugin.ts | 4 ++-- client/src/sass/video-js-custom.scss | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'client') diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts index add4e521e..4ba37b7d9 100644 --- a/client/src/assets/player/peertube-videojs-plugin.ts +++ b/client/src/assets/player/peertube-videojs-plugin.ts @@ -156,7 +156,7 @@ const WebTorrentButton = videojsUntyped.extend(Button, { div.className = 'vjs-webtorrent' // Hide the stats before we get the info - subDiv.style.display = 'none' + subDiv.className = 'vjs-webtorrent-hidden' this.player_.on('torrentInfo', (event, data) => { const downloadSpeed = bytes(data.downloadSpeed) @@ -171,7 +171,7 @@ const WebTorrentButton = videojsUntyped.extend(Button, { peersNumber.textContent = numPeers - subDiv.style.display = 'block' + subDiv.className = 'vjs-webtorrent-displayed' }) return div diff --git a/client/src/sass/video-js-custom.scss b/client/src/sass/video-js-custom.scss index 1200c07a5..2fcfc6203 100644 --- a/client/src/sass/video-js-custom.scss +++ b/client/src/sass/video-js-custom.scss @@ -138,6 +138,14 @@ $control-bar-height: 34px; text-align: right; padding-right: 60px; + .vjs-webtorrent-displayed { + display: block; + } + + .vjs-webtorrent-hidden { + display: none; + } + .download-speed-number, .upload-speed-number, .peers-number { font-weight: $font-semibold; } @@ -303,6 +311,12 @@ $control-bar-height: 34px; } } } + + @media screen and (max-width: 450px) { + .vjs-webtorrent-displayed { + display: none !important; + } + } } // Thanks: https://projects.lukehaas.me/css-loaders/ @@ -340,3 +354,4 @@ $control-bar-height: 34px; } } } + -- cgit v1.2.3 From 9b7d1c723d7c11572d91d606954997e413f56a1f Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 11 Dec 2017 09:39:40 +0100 Subject: Responsive my account --- .../account-videos/account-videos.component.html | 5 +- .../account-videos/account-videos.component.scss | 20 +++++++ client/src/app/menu/menu.component.scss | 62 ++++++++++++++++++++-- .../videos/+video-watch/video-watch.component.scss | 2 + client/src/sass/application.scss | 19 ++++--- 5 files changed, 94 insertions(+), 14 deletions(-) (limited to 'client') diff --git a/client/src/app/account/account-videos/account-videos.component.html b/client/src/app/account/account-videos/account-videos.component.html index 641fcb38a..77f959fef 100644 --- a/client/src/app/account/account-videos/account-videos.component.html +++ b/client/src/app/account/account-videos/account-videos.component.html @@ -1,4 +1,5 @@
- +
- +
diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss index 670fe992c..4c00431fa 100644 --- a/client/src/app/account/account-videos/account-videos.component.scss +++ b/client/src/app/account/account-videos/account-videos.component.scss @@ -64,3 +64,23 @@ } } } + +@media screen and (max-width: 800px) { + .video { + flex-direction: column; + height: auto; + text-align: center; + + input[type=checkbox] { + display: none; + } + + my-video-thumbnail { + margin-right: 0; + } + + .video-buttons { + margin-top: 10px; + } + } +} diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index 63d63d287..97ceadde3 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss @@ -9,10 +9,6 @@ menu { z-index: 1000; color: $menu-color; - @media screen and (max-width: 550px) { - font-size: 90%; - } - .logged-in-block { height: 100px; background-color: rgba(255, 255, 255, 0.15); @@ -68,7 +64,7 @@ menu { font-size: 15px; height: $button-height; line-height: $button-height; - width: 190px; + width: 100%; border-radius: 3px; text-align: center; color: $menu-color; @@ -139,3 +135,59 @@ menu { } } } + +@media screen and (max-width: 800px) { + menu { + .logged-in-block { + padding-left: 10px; + + img { + display: none; + } + + .logged-in-info { + .logged-in-username { + font-size: 14px; + } + + .logged-in-email { + font-size: 11px; + max-width: 120px; + } + } + + .logged-in-more { + margin-right: 5px; + + .login-button, .create-account-button { + font-weight: $font-semibold; + font-size: 15px; + height: $button-height; + line-height: $button-height; + width: 190px; + } + } + } + + .button-block { + margin: 20px 10px 25px 10px; + + .login-button, .create-account-button { + font-size: 13px; + } + } + + .panel-block { + margin-bottom: 30px; + margin-left: 10px; + + a { + font-size: 14px; + + .icon { + margin-right: 10px; + } + } + } + } +} diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index fcf625d6c..83a7cc41d 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -223,6 +223,8 @@ .video-bottom { .video-info { + margin-right: 10px; + .video-info-name-actions { align-items: left; flex-direction: column; diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index f7d83c5c3..6352dd4fb 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -110,7 +110,7 @@ label { // On small screen, menu is absolute @media screen and (max-width: 800px) { .title-menu-left { - width: 120px; + width: 150px !important; position: absolute !important; z-index: 10000; } @@ -120,12 +120,17 @@ label { &, &.expanded { .margin-content { - //display: flex; - //flex-direction: column; - //align-items: center; - //justify-content: center; - margin-left: auto; - margin-right: auto; + margin-left: 10px; + margin-right: 10px; + } + + .sub-menu { + padding-left: 10px; + margin-bottom: 10px; + } + + input[type=text], input[type=password] { + width: 100% !important; } } } -- cgit v1.2.3 From 20206dfb0bfe1537912ae0a5b99f2fa40c881d33 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 11 Dec 2017 10:02:17 +0100 Subject: Fix loading spinner in player --- client/src/app/header/header.component.scss | 2 +- .../videos/+video-watch/video-watch.component.html | 230 ++++++++++----------- .../videos/+video-watch/video-watch.component.scss | 8 +- client/src/sass/application.scss | 58 +++--- client/src/sass/video-js-custom.scss | 14 +- 5 files changed, 152 insertions(+), 160 deletions(-) (limited to 'client') diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss index 5f64ede98..fba70dd2f 100644 --- a/client/src/app/header/header.component.scss +++ b/client/src/app/header/header.component.scss @@ -7,7 +7,7 @@ color: #000; } - @media screen and (max-width: 800px) { + @media screen and (max-width: 600px) { width: calc(100% - 150px); } diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index 43b175acc..f99e84caf 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -5,148 +5,148 @@
Video not found :'(
-
- - -
-
-
-
{{ video.name }}
-
-
- -
+ +
+
+
+
{{ video.name }}
-
- -
+
+
+ +
-
- - Share -
+
+ +
-
-
- +
+ + Share
- +
+
+ +
+ + +
-
-
-
- {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views -
+
+
+ {{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views +
-
- +
+ +
-
-
- {{ video.channel.name }} - -
+
+ {{ video.channel.name }} + +
-
- By {{ video.by }} - Account avatar -
+
+ By {{ video.by }} + Account avatar +
-
-
+
+
-
- Show more - - -
+
+ Show more + + +
-
- Show less - +
+ Show less + +
-
-
-
- - Privacy - - - {{ video.privacyLabel }} - -
+
+
+ + Privacy + + + {{ video.privacyLabel }} + +
-
- - Category - - - {{ video.categoryLabel }} - -
+
+ + Category + + + {{ video.categoryLabel }} + +
-
- - Licence - - - {{ video.licenceLabel }} - -
+
+ + Licence + + + {{ video.licenceLabel }} + +
-
- - Language - - - {{ video.languageLabel }} - -
+
+ + Language + + + {{ video.languageLabel }} + +
-
- - Tags - +
+ + Tags + - - {{ getVideoTags() }} - + + {{ getVideoTags() }} + +
-
-
- -
-
- Other videos
-
- +
+
+ Other videos +
+ +
+ +
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index 83a7cc41d..9daa757b4 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss @@ -216,14 +216,18 @@ } -@media screen and (max-width: 800px) { +@media screen and (max-width: 1000px) { .other-videos { display: none; } +} +@media screen and (max-width: 800px) { .video-bottom { + margin: 20px 0 0 0; + .video-info { - margin-right: 10px; + margin-right: 0; .video-info-name-actions { align-items: left; diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 6352dd4fb..9a93411e9 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -107,35 +107,6 @@ label { font-weight: bold; } -// On small screen, menu is absolute -@media screen and (max-width: 800px) { - .title-menu-left { - width: 150px !important; - position: absolute !important; - z-index: 10000; - } - - .main-col { - margin-left: 0; - - &, &.expanded { - .margin-content { - margin-left: 10px; - margin-right: 10px; - } - - .sub-menu { - padding-left: 10px; - margin-bottom: 10px; - } - - input[type=text], input[type=password] { - width: 100% !important; - } - } - } -} - // Thanks https://gist.github.com/alexandrevicenzi/680147013e902a4eaa5d .glyphicon-refresh-animate { -animation: spin .7s infinite linear; @@ -330,3 +301,32 @@ p-datatable { @include peertube-button-link; @include grey-button; } + +// On small screen, menu is absolute +@media screen and (max-width: 800px) { + .title-menu-left { + width: 150px !important; + position: absolute !important; + z-index: 10000; + } + + .main-col { + margin-left: 0; + + &, &.expanded { + .margin-content { + margin-left: 10px; + margin-right: 10px; + } + + .sub-menu { + padding-left: 10px; + margin-bottom: 10px; + } + + input[type=text], input[type=password] { + width: 100% !important; + } + } + } +} diff --git a/client/src/sass/video-js-custom.scss b/client/src/sass/video-js-custom.scss index 2fcfc6203..fe9495e77 100644 --- a/client/src/sass/video-js-custom.scss +++ b/client/src/sass/video-js-custom.scss @@ -28,10 +28,6 @@ $control-bar-height: 34px; $big-play-width: 3em; $big-play-height: 1.5em; - line-height: $big-play-height; - height: $big-play-height; - width: $big-play-width; - border: 0; border-radius: 0.3em; @@ -39,10 +35,7 @@ $control-bar-height: 34px; top: 50%; margin-left: -($big-play-width / 2); margin-top: -($big-play-height / 2); - } - - &:hover .vjs-big-play-button { - background-color: transparent; + background-color: transparent !important; } .vjs-control-bar, @@ -321,13 +314,8 @@ $control-bar-height: 34px; // Thanks: https://projects.lukehaas.me/css-loaders/ .vjs-loading-spinner { - margin: 0 !important; - //position: absolute; - // 15px is the nav bar height - top: calc(50% - 15px); left: 50%; font-size: 10px; - position: relative; text-indent: -9999em; border: 0.7em solid rgba(255, 255, 255, 0.2); border-left-color: #ffffff; -- cgit v1.2.3 From 9e6b41cc1d825182bcfdf51364fdf3eae8001244 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 11 Dec 2017 10:26:29 +0100 Subject: Embed player responsive --- client/src/sass/video-js-custom.scss | 1 + client/src/standalone/videos/embed.html | 2 +- client/src/standalone/videos/embed.scss | 32 ++++++++++++++++++++++---------- 3 files changed, 24 insertions(+), 11 deletions(-) (limited to 'client') diff --git a/client/src/sass/video-js-custom.scss b/client/src/sass/video-js-custom.scss index fe9495e77..1c5701bea 100644 --- a/client/src/sass/video-js-custom.scss +++ b/client/src/sass/video-js-custom.scss @@ -23,6 +23,7 @@ $control-bar-height: 34px; } .vjs-big-play-button { + outline: 0; font-size: 8em; $big-play-width: 3em; diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html index 0a35bc362..fa4d0bdba 100644 --- a/client/src/standalone/videos/embed.html +++ b/client/src/standalone/videos/embed.html @@ -11,7 +11,7 @@ -