aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-06-15 16:52:15 +0200
committerChocobozzz <me@florianbigard.com>2018-06-15 18:20:56 +0200
commitbbe0f0645ca958d33a3f409b15166609733b663f (patch)
treeedcd5d702c73cda74a2177c4bdc08c616334337d /client/src/app/shared
parent2baea0c77cc765f7cbca9c9a2f4272268892a35c (diff)
downloadPeerTube-bbe0f0645ca958d33a3f409b15166609733b663f.tar.gz
PeerTube-bbe0f0645ca958d33a3f409b15166609733b663f.tar.zst
PeerTube-bbe0f0645ca958d33a3f409b15166609733b663f.zip
Add ability to schedule video publication
Diffstat (limited to 'client/src/app/shared')
-rw-r--r--client/src/app/shared/forms/form-validators/video-validators.service.ts8
-rw-r--r--client/src/app/shared/forms/markdown-textarea.component.ts9
-rw-r--r--client/src/app/shared/i18n/i18n-primeng-calendar.ts94
-rw-r--r--client/src/app/shared/misc/screen.service.ts23
-rw-r--r--client/src/app/shared/misc/utils.ts14
-rw-r--r--client/src/app/shared/shared.module.ts5
-rw-r--r--client/src/app/shared/video/abstract-video-list.ts5
-rw-r--r--client/src/app/shared/video/video-edit.model.ts32
-rw-r--r--client/src/app/shared/video/video-thumbnail.component.ts6
-rw-r--r--client/src/app/shared/video/video.model.ts3
-rw-r--r--client/src/app/shared/video/video.service.ts3
11 files changed, 178 insertions, 24 deletions
diff --git a/client/src/app/shared/forms/form-validators/video-validators.service.ts b/client/src/app/shared/forms/form-validators/video-validators.service.ts
index 76fc5cf04..396be6f3b 100644
--- a/client/src/app/shared/forms/form-validators/video-validators.service.ts
+++ b/client/src/app/shared/forms/form-validators/video-validators.service.ts
@@ -15,6 +15,7 @@ export class VideoValidatorsService {
15 readonly VIDEO_DESCRIPTION: BuildFormValidator 15 readonly VIDEO_DESCRIPTION: BuildFormValidator
16 readonly VIDEO_TAGS: BuildFormValidator 16 readonly VIDEO_TAGS: BuildFormValidator
17 readonly VIDEO_SUPPORT: BuildFormValidator 17 readonly VIDEO_SUPPORT: BuildFormValidator
18 readonly VIDEO_SCHEDULE_PUBLICATION_AT: BuildFormValidator
18 19
19 constructor (private i18n: I18n) { 20 constructor (private i18n: I18n) {
20 21
@@ -84,5 +85,12 @@ export class VideoValidatorsService {
84 'maxlength': this.i18n('Video support cannot be more than 500 characters long.') 85 'maxlength': this.i18n('Video support cannot be more than 500 characters long.')
85 } 86 }
86 } 87 }
88
89 this.VIDEO_SCHEDULE_PUBLICATION_AT = {
90 VALIDATORS: [ ],
91 MESSAGES: {
92 'required': this.i18n('A date is required to schedule video update.')
93 }
94 }
87 } 95 }
88} 96}
diff --git a/client/src/app/shared/forms/markdown-textarea.component.ts b/client/src/app/shared/forms/markdown-textarea.component.ts
index 8b932cd15..db6f9e5c8 100644
--- a/client/src/app/shared/forms/markdown-textarea.component.ts
+++ b/client/src/app/shared/forms/markdown-textarea.component.ts
@@ -1,10 +1,10 @@
1import { debounceTime, distinctUntilChanged } from 'rxjs/operators' 1import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
2import { Component, forwardRef, Input, OnInit } from '@angular/core' 2import { Component, forwardRef, Input, OnInit } from '@angular/core'
3import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 3import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
4import { isInSmallView } from '@app/shared/misc/utils'
5import { MarkdownService } from '@app/videos/shared' 4import { MarkdownService } from '@app/videos/shared'
6import { Subject } from 'rxjs/Subject' 5import { Subject } from 'rxjs/Subject'
7import truncate from 'lodash-es/truncate' 6import truncate from 'lodash-es/truncate'
7import { ScreenService } from '@app/shared/misc/screen.service'
8 8
9@Component({ 9@Component({
10 selector: 'my-markdown-textarea', 10 selector: 'my-markdown-textarea',
@@ -35,7 +35,10 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
35 35
36 private contentChanged = new Subject<string>() 36 private contentChanged = new Subject<string>()
37 37
38 constructor (private markdownService: MarkdownService) {} 38 constructor (
39 private screenService: ScreenService,
40 private markdownService: MarkdownService
41) {}
39 42
40 ngOnInit () { 43 ngOnInit () {
41 this.contentChanged 44 this.contentChanged
@@ -76,7 +79,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
76 } 79 }
77 80
78 arePreviewsDisplayed () { 81 arePreviewsDisplayed () {
79 return isInSmallView() === false 82 return this.screenService.isInSmallView() === false
80 } 83 }
81 84
82 private updatePreviews () { 85 private updatePreviews () {
diff --git a/client/src/app/shared/i18n/i18n-primeng-calendar.ts b/client/src/app/shared/i18n/i18n-primeng-calendar.ts
new file mode 100644
index 000000000..b05852ff8
--- /dev/null
+++ b/client/src/app/shared/i18n/i18n-primeng-calendar.ts
@@ -0,0 +1,94 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Injectable } from '@angular/core'
3
4@Injectable()
5export class I18nPrimengCalendarService {
6 private readonly calendarLocale: any = {}
7
8 constructor (private i18n: I18n) {
9 this.calendarLocale = {
10 firstDayOfWeek: 0,
11 dayNames: [
12 this.i18n('Sunday'),
13 this.i18n('Monday'),
14 this.i18n('Tuesday'),
15 this.i18n('Wednesday'),
16 this.i18n('Thursday'),
17 this.i18n('Friday'),
18 this.i18n('Saturday')
19 ],
20
21 dayNamesShort: [
22 this.i18n({ value: 'Sun', description: 'Day name short' }),
23 this.i18n({ value: 'Mon', description: 'Day name short' }),
24 this.i18n({ value: 'Tue', description: 'Day name short' }),
25 this.i18n({ value: 'Wed', description: 'Day name short' }),
26 this.i18n({ value: 'Thu', description: 'Day name short' }),
27 this.i18n({ value: 'Fri', description: 'Day name short' }),
28 this.i18n({ value: 'Sat', description: 'Day name short' })
29 ],
30
31 dayNamesMin: [
32 this.i18n({ value: 'Su', description: 'Day name min' }),
33 this.i18n({ value: 'Mo', description: 'Day name min' }),
34 this.i18n({ value: 'Tu', description: 'Day name min' }),
35 this.i18n({ value: 'We', description: 'Day name min' }),
36 this.i18n({ value: 'Th', description: 'Day name min' }),
37 this.i18n({ value: 'Fr', description: 'Day name min' }),
38 this.i18n({ value: 'Sa', description: 'Day name min' })
39 ],
40
41 monthNames: [
42 this.i18n('January'),
43 this.i18n('February'),
44 this.i18n('March'),
45 this.i18n('April'),
46 this.i18n('May'),
47 this.i18n('June'),
48 this.i18n('July'),
49 this.i18n('August'),
50 this.i18n('September'),
51 this.i18n('October'),
52 this.i18n('November'),
53 this.i18n('December')
54 ],
55
56 monthNamesShort: [
57 this.i18n({ value: 'Jan', description: 'Month name short' }),
58 this.i18n({ value: 'Feb', description: 'Month name short' }),
59 this.i18n({ value: 'Mar', description: 'Month name short' }),
60 this.i18n({ value: 'Apr', description: 'Month name short' }),
61 this.i18n({ value: 'May', description: 'Month name short' }),
62 this.i18n({ value: 'Jun', description: 'Month name short' }),
63 this.i18n({ value: 'Jul', description: 'Month name short' }),
64 this.i18n({ value: 'Aug', description: 'Month name short' }),
65 this.i18n({ value: 'Sep', description: 'Month name short' }),
66 this.i18n({ value: 'Oct', description: 'Month name short' }),
67 this.i18n({ value: 'Nov', description: 'Month name short' }),
68 this.i18n({ value: 'Dec', description: 'Month name short' })
69 ],
70
71 today: this.i18n('Today'),
72
73 clear: this.i18n('Clear')
74 }
75 }
76
77 getCalendarLocale () {
78 return this.calendarLocale
79 }
80
81 getTimezone () {
82 const gmt = new Date().toString().match(/([A-Z]+[\+-][0-9]+)/)[1]
83 const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
84
85 return `${timezone} - ${gmt}`
86 }
87
88 getDateFormat () {
89 return this.i18n({
90 value: 'yy-mm-dd ',
91 description: 'Date format in this locale.'
92 })
93 }
94}
diff --git a/client/src/app/shared/misc/screen.service.ts b/client/src/app/shared/misc/screen.service.ts
new file mode 100644
index 000000000..5b17a914a
--- /dev/null
+++ b/client/src/app/shared/misc/screen.service.ts
@@ -0,0 +1,23 @@
1import { Injectable, NgZone } from '@angular/core'
2
3@Injectable()
4export class ScreenService {
5 private windowInnerWidth: number
6
7 constructor (private zone: NgZone) {
8 this.windowInnerWidth = window.innerWidth
9
10 // Try to cache a little bit window.innerWidth
11 this.zone.runOutsideAngular(() => {
12 setInterval(() => this.windowInnerWidth = window.innerWidth, 500)
13 })
14 }
15
16 isInSmallView () {
17 return this.windowInnerWidth < 600
18 }
19
20 isInMobileView () {
21 return this.windowInnerWidth < 500
22 }
23}
diff --git a/client/src/app/shared/misc/utils.ts b/client/src/app/shared/misc/utils.ts
index 2219ac802..53aff1b24 100644
--- a/client/src/app/shared/misc/utils.ts
+++ b/client/src/app/shared/misc/utils.ts
@@ -96,26 +96,12 @@ function lineFeedToHtml (obj: object, keyToNormalize: string) {
96 }) 96 })
97} 97}
98 98
99// Try to cache a little bit window.innerWidth
100let windowInnerWidth = window.innerWidth
101setInterval(() => windowInnerWidth = window.innerWidth, 500)
102
103function isInSmallView () {
104 return windowInnerWidth < 600
105}
106
107function isInMobileView () {
108 return windowInnerWidth < 500
109}
110
111export { 99export {
112 objectToUrlEncoded, 100 objectToUrlEncoded,
113 getParameterByName, 101 getParameterByName,
114 populateAsyncUserVideoChannels, 102 populateAsyncUserVideoChannels,
115 getAbsoluteAPIUrl, 103 getAbsoluteAPIUrl,
116 dateToHuman, 104 dateToHuman,
117 isInSmallView,
118 isInMobileView,
119 immutableAssign, 105 immutableAssign,
120 objectToFormData, 106 objectToFormData,
121 lineFeedToHtml 107 lineFeedToHtml
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index b85445ef5..97e49e7ab 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -41,6 +41,8 @@ import {
41 ResetPasswordValidatorsService, 41 ResetPasswordValidatorsService,
42 UserValidatorsService, VideoAbuseValidatorsService, VideoChannelValidatorsService, VideoCommentValidatorsService, VideoValidatorsService 42 UserValidatorsService, VideoAbuseValidatorsService, VideoChannelValidatorsService, VideoCommentValidatorsService, VideoValidatorsService
43} from '@app/shared/forms' 43} from '@app/shared/forms'
44import { I18nPrimengCalendarService } from '@app/shared/i18n/i18n-primeng-calendar'
45import { ScreenService } from '@app/shared/misc/screen.service'
44 46
45@NgModule({ 47@NgModule({
46 imports: [ 48 imports: [
@@ -128,6 +130,9 @@ import {
128 VideoCommentValidatorsService, 130 VideoCommentValidatorsService,
129 VideoValidatorsService, 131 VideoValidatorsService,
130 132
133 I18nPrimengCalendarService,
134 ScreenService,
135
131 I18n 136 I18n
132 ] 137 ]
133}) 138})
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts
index 1c84573da..a468d3231 100644
--- a/client/src/app/shared/video/abstract-video-list.ts
+++ b/client/src/app/shared/video/abstract-video-list.ts
@@ -2,7 +2,6 @@ import { debounceTime } from 'rxjs/operators'
2import { ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core' 2import { ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { Location } from '@angular/common' 4import { Location } from '@angular/common'
5import { isInMobileView } from '@app/shared/misc/utils'
6import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive' 5import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive'
7import { NotificationsService } from 'angular2-notifications' 6import { NotificationsService } from 'angular2-notifications'
8import { fromEvent, Observable, Subscription } from 'rxjs' 7import { fromEvent, Observable, Subscription } from 'rxjs'
@@ -11,6 +10,7 @@ import { ComponentPagination } from '../rest/component-pagination.model'
11import { VideoSortField } from './sort-field.type' 10import { VideoSortField } from './sort-field.type'
12import { Video } from './video.model' 11import { Video } from './video.model'
13import { I18n } from '@ngx-translate/i18n-polyfill' 12import { I18n } from '@ngx-translate/i18n-polyfill'
13import { ScreenService } from '@app/shared/misc/screen.service'
14 14
15export abstract class AbstractVideoList implements OnInit, OnDestroy { 15export abstract class AbstractVideoList implements OnInit, OnDestroy {
16 private static LINES_PER_PAGE = 4 16 private static LINES_PER_PAGE = 4
@@ -41,6 +41,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
41 protected abstract authService: AuthService 41 protected abstract authService: AuthService
42 protected abstract router: Router 42 protected abstract router: Router
43 protected abstract route: ActivatedRoute 43 protected abstract route: ActivatedRoute
44 protected abstract screenService: ScreenService
44 protected abstract i18n: I18n 45 protected abstract i18n: I18n
45 protected abstract location: Location 46 protected abstract location: Location
46 protected abstract currentRoute: string 47 protected abstract currentRoute: string
@@ -199,7 +200,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
199 } 200 }
200 201
201 private calcPageSizes () { 202 private calcPageSizes () {
202 if (isInMobileView() || this.baseVideoWidth === -1) { 203 if (this.screenService.isInMobileView() || this.baseVideoWidth === -1) {
203 this.pagination.itemsPerPage = 5 204 this.pagination.itemsPerPage = 5
204 205
205 // Video takes all the width 206 // Video takes all the width
diff --git a/client/src/app/shared/video/video-edit.model.ts b/client/src/app/shared/video/video-edit.model.ts
index f045a3acd..78aed4f9f 100644
--- a/client/src/app/shared/video/video-edit.model.ts
+++ b/client/src/app/shared/video/video-edit.model.ts
@@ -1,8 +1,11 @@
1import { VideoDetails } from './video-details.model' 1import { VideoDetails } from './video-details.model'
2import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' 2import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum'
3import { VideoUpdate } from '../../../../../shared/models/videos' 3import { VideoUpdate } from '../../../../../shared/models/videos'
4import { VideoScheduleUpdate } from '../../../../../shared/models/videos/video-schedule-update.model'
4 5
5export class VideoEdit implements VideoUpdate { 6export class VideoEdit implements VideoUpdate {
7 static readonly SPECIAL_SCHEDULED_PRIVACY = -1
8
6 category: number 9 category: number
7 licence: number 10 licence: number
8 language: string 11 language: string
@@ -21,6 +24,7 @@ export class VideoEdit implements VideoUpdate {
21 previewUrl: string 24 previewUrl: string
22 uuid?: string 25 uuid?: string
23 id?: number 26 id?: number
27 scheduleUpdate?: VideoScheduleUpdate
24 28
25 constructor (videoDetails?: VideoDetails) { 29 constructor (videoDetails?: VideoDetails) {
26 if (videoDetails) { 30 if (videoDetails) {
@@ -40,6 +44,8 @@ export class VideoEdit implements VideoUpdate {
40 this.support = videoDetails.support 44 this.support = videoDetails.support
41 this.thumbnailUrl = videoDetails.thumbnailUrl 45 this.thumbnailUrl = videoDetails.thumbnailUrl
42 this.previewUrl = videoDetails.previewUrl 46 this.previewUrl = videoDetails.previewUrl
47
48 this.scheduleUpdate = videoDetails.scheduledUpdate
43 } 49 }
44 } 50 }
45 51
@@ -47,10 +53,22 @@ export class VideoEdit implements VideoUpdate {
47 Object.keys(values).forEach((key) => { 53 Object.keys(values).forEach((key) => {
48 this[ key ] = values[ key ] 54 this[ key ] = values[ key ]
49 }) 55 })
56
57 // If schedule publication, the video is private and will be changed to public privacy
58 if (values['schedulePublicationAt']) {
59 const updateAt = (values['schedulePublicationAt'] as Date)
60 updateAt.setSeconds(0)
61
62 this.privacy = VideoPrivacy.PRIVATE
63 this.scheduleUpdate = {
64 updateAt: updateAt.toISOString(),
65 privacy: VideoPrivacy.PUBLIC
66 }
67 }
50 } 68 }
51 69
52 toJSON () { 70 toFormPatch () {
53 return { 71 const json = {
54 category: this.category, 72 category: this.category,
55 licence: this.licence, 73 licence: this.licence,
56 language: this.language, 74 language: this.language,
@@ -64,5 +82,15 @@ export class VideoEdit implements VideoUpdate {
64 channelId: this.channelId, 82 channelId: this.channelId,
65 privacy: this.privacy 83 privacy: this.privacy
66 } 84 }
85
86 // Special case if we scheduled an update
87 if (this.scheduleUpdate) {
88 Object.assign(json, {
89 privacy: VideoEdit.SPECIAL_SCHEDULED_PRIVACY,
90 schedulePublicationAt: new Date(this.scheduleUpdate.updateAt.toString())
91 })
92 }
93
94 return json
67 } 95 }
68} 96}
diff --git a/client/src/app/shared/video/video-thumbnail.component.ts b/client/src/app/shared/video/video-thumbnail.component.ts
index e52f7dfb0..86d8f6f74 100644
--- a/client/src/app/shared/video/video-thumbnail.component.ts
+++ b/client/src/app/shared/video/video-thumbnail.component.ts
@@ -1,6 +1,6 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { isInMobileView } from '@app/shared/misc/utils'
3import { Video } from './video.model' 2import { Video } from './video.model'
3import { ScreenService } from '@app/shared/misc/screen.service'
4 4
5@Component({ 5@Component({
6 selector: 'my-video-thumbnail', 6 selector: 'my-video-thumbnail',
@@ -11,10 +11,12 @@ export class VideoThumbnailComponent {
11 @Input() video: Video 11 @Input() video: Video
12 @Input() nsfw = false 12 @Input() nsfw = false
13 13
14 constructor (private screenService: ScreenService) {}
15
14 getImageUrl () { 16 getImageUrl () {
15 if (!this.video) return '' 17 if (!this.video) return ''
16 18
17 if (isInMobileView()) { 19 if (this.screenService.isInMobileView()) {
18 return this.video.previewUrl 20 return this.video.previewUrl
19 } 21 }
20 22
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts
index 48a4b4260..7f421dbbb 100644
--- a/client/src/app/shared/video/video.model.ts
+++ b/client/src/app/shared/video/video.model.ts
@@ -6,6 +6,7 @@ import { getAbsoluteAPIUrl } from '../misc/utils'
6import { ServerConfig } from '../../../../../shared/models' 6import { ServerConfig } from '../../../../../shared/models'
7import { Actor } from '@app/shared/actor/actor.model' 7import { Actor } from '@app/shared/actor/actor.model'
8import { peertubeTranslate } from '@app/shared/i18n/i18n-utils' 8import { peertubeTranslate } from '@app/shared/i18n/i18n-utils'
9import { VideoScheduleUpdate } from '../../../../../shared/models/videos/video-schedule-update.model'
9 10
10export class Video implements VideoServerModel { 11export class Video implements VideoServerModel {
11 by: string 12 by: string
@@ -38,6 +39,7 @@ export class Video implements VideoServerModel {
38 39
39 waitTranscoding?: boolean 40 waitTranscoding?: boolean
40 state?: VideoConstant<VideoState> 41 state?: VideoConstant<VideoState>
42 scheduledUpdate?: VideoScheduleUpdate
41 43
42 account: { 44 account: {
43 id: number 45 id: number
@@ -109,6 +111,7 @@ export class Video implements VideoServerModel {
109 this.language.label = peertubeTranslate(this.language.label, translations) 111 this.language.label = peertubeTranslate(this.language.label, translations)
110 this.privacy.label = peertubeTranslate(this.privacy.label, translations) 112 this.privacy.label = peertubeTranslate(this.privacy.label, translations)
111 113
114 this.scheduledUpdate = hash.scheduledUpdate
112 if (this.state) this.state.label = peertubeTranslate(this.state.label, translations) 115 if (this.state) this.state.label = peertubeTranslate(this.state.label, translations)
113 } 116 }
114 117
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts
index d63915ad2..3af90e7ad 100644
--- a/client/src/app/shared/video/video.service.ts
+++ b/client/src/app/shared/video/video.service.ts
@@ -83,7 +83,8 @@ export class VideoService {
83 waitTranscoding: video.waitTranscoding, 83 waitTranscoding: video.waitTranscoding,
84 commentsEnabled: video.commentsEnabled, 84 commentsEnabled: video.commentsEnabled,
85 thumbnailfile: video.thumbnailfile, 85 thumbnailfile: video.thumbnailfile,
86 previewfile: video.previewfile 86 previewfile: video.previewfile,
87 scheduleUpdate: video.scheduleUpdate || undefined
87 } 88 }
88 89
89 const data = objectToFormData(body) 90 const data = objectToFormData(body)