From cadb46d832724ea1a17b085b992142aa32e212be Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 8 Dec 2017 08:39:15 +0100 Subject: [PATCH] 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 ++ .../app/shared/forms/form-validators/video.ts | 22 +++------- .../src/app/shared/video/video-edit.model.ts | 26 ++++++------ client/src/app/shared/video/video.service.ts | 11 +++-- .../shared/video-description.component.html | 0 .../shared/video-description.component.scss | 41 +++++++++++++++++++ .../shared/video-description.component.ts | 8 ++-- .../shared/video-edit.component.scss | 9 ++++ .../+video-edit/shared/video-edit.module.ts | 3 +- .../+video-edit/video-add.component.html | 16 +++++--- .../+video-edit/video-add.component.scss | 19 +++++++-- .../videos/+video-edit/video-add.component.ts | 30 +++++++++----- .../+video-edit/video-update.component.html | 4 +- .../+video-watch/video-watch.component.html | 2 +- .../+video-watch/video-watch.component.ts | 5 +++ client/src/app/videos/shared/index.ts | 1 - .../shared/video-description.component.scss | 19 --------- client/src/sass/_mixins.scss | 6 ++- client/src/sass/application.scss | 4 ++ server/controllers/api/videos/index.ts | 28 +++++++------ server/tests/api/single-server.ts | 5 ++- server/tests/utils/videos.ts | 2 +- 24 files changed, 173 insertions(+), 98 deletions(-) rename client/src/app/videos/{ => +video-edit}/shared/video-description.component.html (100%) create mode 100644 client/src/app/videos/+video-edit/shared/video-description.component.scss rename client/src/app/videos/{ => +video-edit}/shared/video-description.component.ts (96%) delete mode 100644 client/src/app/videos/shared/video-description.component.scss 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/shared/video-description.component.html b/client/src/app/videos/+video-edit/shared/video-description.component.html similarity index 100% rename from client/src/app/videos/shared/video-description.component.html rename to client/src/app/videos/+video-edit/shared/video-description.component.html 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/shared/video-description.component.ts b/client/src/app/videos/+video-edit/shared/video-description.component.ts similarity index 96% rename from client/src/app/videos/shared/video-description.component.ts rename to client/src/app/videos/+video-edit/shared/video-description.component.ts index d9ffb7800..8dfb74b2a 100644 --- a/client/src/app/videos/shared/video-description.component.ts +++ b/client/src/app/videos/+video-edit/shared/video-description.component.ts @@ -1,12 +1,10 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core' import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' -import { Subject } from 'rxjs/Subject' +import { truncate } from 'lodash' import 'rxjs/add/operator/debounceTime' import 'rxjs/add/operator/distinctUntilChanged' - -import { truncate } from 'lodash' - -import { MarkdownService } from './markdown.service' +import { Subject } from 'rxjs/Subject' +import { MarkdownService } from '../../shared' @Component({ selector: 'my-video-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.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/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 diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 2b70d535e..f427a25c0 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -15,6 +15,7 @@ import { getServerAccount } from '../../../helpers/utils' import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers' import { database as db } from '../../../initializers/database' import { sendAddVideo } from '../../../lib/activitypub/send/send-add' +import { sendCreateViewToOrigin } from '../../../lib/activitypub/send/send-create' import { sendUpdateVideo } from '../../../lib/activitypub/send/send-update' import { shareVideoByServer } from '../../../lib/activitypub/share' import { getVideoActivityPubUrl } from '../../../lib/activitypub/url' @@ -39,7 +40,6 @@ import { abuseVideoRouter } from './abuse' import { blacklistRouter } from './blacklist' import { videoChannelRouter } from './channel' import { rateVideoRouter } from './rate' -import { sendCreateViewToOrigin } from '../../../lib/activitypub/send/send-create' const videosRouter = express.Router() @@ -154,17 +154,20 @@ async function addVideoRetryWrapper (req: express.Request, res: express.Response errorMessage: 'Cannot insert the video with many retries.' } - await retryTransactionWrapper(addVideo, options) + const video = await retryTransactionWrapper(addVideo, options) - // TODO : include Location of the new video -> 201 - res.type('json').status(204).end() + res.json({ + video: { + id: video.id, + uuid: video.uuid + } + }).end() } -async function addVideo (req: express.Request, res: express.Response, videoPhysicalFile: Express.Multer.File) { +function addVideo (req: express.Request, res: express.Response, videoPhysicalFile: Express.Multer.File) { const videoInfo: VideoCreate = req.body - let videoUUID = '' - await db.sequelize.transaction(async t => { + return db.sequelize.transaction(async t => { const sequelizeOptions = { transaction: t } const videoData = { @@ -223,7 +226,6 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi const videoCreated = await video.save(sequelizeOptions) // Do not forget to add video channel information to the created video videoCreated.VideoChannel = res.locals.videoChannel - videoUUID = videoCreated.uuid videoFile.videoId = video.id @@ -238,15 +240,17 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi } // Let transcoding job send the video to friends because the video file extension might change - if (CONFIG.TRANSCODING.ENABLED === true) return undefined + if (CONFIG.TRANSCODING.ENABLED === true) return videoCreated // Don't send video to remote servers, it is private - if (video.privacy === VideoPrivacy.PRIVATE) return undefined + if (video.privacy === VideoPrivacy.PRIVATE) return videoCreated await sendAddVideo(video, t) await shareVideoByServer(video, t) - }) - logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID) + logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid) + + return videoCreated + }) } async function updateVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { diff --git a/server/tests/api/single-server.ts b/server/tests/api/single-server.ts index fbb2dd1fb..ed79f9e1c 100644 --- a/server/tests/api/single-server.ts +++ b/server/tests/api/single-server.ts @@ -104,7 +104,10 @@ describe('Test a single server', function () { licence: 6, tags: [ 'tag1', 'tag2', 'tag3' ] } - await uploadVideo(server.url, server.accessToken, videoAttributes) + const res = await uploadVideo(server.url, server.accessToken, videoAttributes) + expect(res.body.video).to.not.be.undefined + expect(res.body.video.id).to.equal(1) + expect(res.body.video.uuid).to.have.length.above(5) }) it('Should seed the uploaded video', async function () { diff --git a/server/tests/utils/videos.ts b/server/tests/utils/videos.ts index ff7da9bb2..bdf3368ac 100644 --- a/server/tests/utils/videos.ts +++ b/server/tests/utils/videos.ts @@ -201,7 +201,7 @@ async function testVideoImage (url: string, imageName: string, imagePath: string } } -async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 204) { +async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 201) { const path = '/api/v1/videos/upload' let defaultChannelId = '1' -- 2.25.1