]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - client/src/app/helpers/utils.ts
Fix video update
[github/Chocobozzz/PeerTube.git] / client / src / app / helpers / utils.ts
1 import { first, map } from 'rxjs/operators'
2 import { SelectChannelItem } from 'src/types/select-options-item.model'
3 import { DatePipe } from '@angular/common'
4 import { HttpErrorResponse } from '@angular/common/http'
5 import { Notifier } from '@app/core'
6 import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
7 import { environment } from '../../environments/environment'
8 import { AuthService } from '../core/auth'
9
10 // Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
11 function getParameterByName (name: string, url: string) {
12 if (!url) url = window.location.href
13 name = name.replace(/[\[\]]/g, '\\$&')
14
15 const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)')
16 const results = regex.exec(url)
17
18 if (!results) return null
19 if (!results[2]) return ''
20
21 return decodeURIComponent(results[2].replace(/\+/g, ' '))
22 }
23
24 function listUserChannels (authService: AuthService) {
25 return authService.userInformationLoaded
26 .pipe(
27 first(),
28 map(() => {
29 const user = authService.getUser()
30 if (!user) return undefined
31
32 const videoChannels = user.videoChannels
33 if (Array.isArray(videoChannels) === false) return undefined
34
35 return videoChannels
36 .sort((a, b) => {
37 if (a.updatedAt < b.updatedAt) return 1
38 if (a.updatedAt > b.updatedAt) return -1
39 return 0
40 })
41 .map(c => ({
42 id: c.id,
43 label: c.displayName,
44 support: c.support,
45 avatarPath: c.avatar?.path
46 }) as SelectChannelItem)
47 })
48 )
49 }
50
51 function getAbsoluteAPIUrl () {
52 let absoluteAPIUrl = environment.hmr === true
53 ? 'http://localhost:9000'
54 : environment.apiUrl
55
56 if (!absoluteAPIUrl) {
57 // The API is on the same domain
58 absoluteAPIUrl = window.location.origin
59 }
60
61 return absoluteAPIUrl
62 }
63
64 function getAbsoluteEmbedUrl () {
65 let absoluteEmbedUrl = environment.originServerUrl
66 if (!absoluteEmbedUrl) {
67 // The Embed is on the same domain
68 absoluteEmbedUrl = window.location.origin
69 }
70
71 return absoluteEmbedUrl
72 }
73
74 const datePipe = new DatePipe('en')
75 function dateToHuman (date: string) {
76 return datePipe.transform(date, 'medium')
77 }
78
79 function durationToString (duration: number) {
80 const hours = Math.floor(duration / 3600)
81 const minutes = Math.floor((duration % 3600) / 60)
82 const seconds = duration % 60
83
84 const minutesPadding = minutes >= 10 ? '' : '0'
85 const secondsPadding = seconds >= 10 ? '' : '0'
86 const displayedHours = hours > 0 ? hours.toString() + ':' : ''
87
88 return (
89 displayedHours + minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString()
90 ).replace(/^0/, '')
91 }
92
93 function immutableAssign <A, B> (target: A, source: B) {
94 return Object.assign({}, target, source)
95 }
96
97 // Thanks: https://gist.github.com/ghinda/8442a57f22099bdb2e34
98 function objectToFormData (obj: any, form?: FormData, namespace?: string) {
99 const fd = form || new FormData()
100 let formKey
101
102 for (const key of Object.keys(obj)) {
103 if (namespace) formKey = `${namespace}[${key}]`
104 else formKey = key
105
106 if (obj[key] === undefined) continue
107
108 if (Array.isArray(obj[key]) && obj[key].length === 0) {
109 fd.append(key, null)
110 continue
111 }
112
113 if (obj[key] !== null && typeof obj[ key ] === 'object' && !(obj[ key ] instanceof File)) {
114 objectToFormData(obj[ key ], fd, formKey)
115 } else {
116 fd.append(formKey, obj[ key ])
117 }
118 }
119
120 return fd
121 }
122
123 function objectLineFeedToHtml (obj: any, keyToNormalize: string) {
124 return immutableAssign(obj, {
125 [keyToNormalize]: lineFeedToHtml(obj[keyToNormalize])
126 })
127 }
128
129 function lineFeedToHtml (text: string) {
130 if (!text) return text
131
132 return text.replace(/\r?\n|\r/g, '<br />')
133 }
134
135 function removeElementFromArray <T> (arr: T[], elem: T) {
136 const index = arr.indexOf(elem)
137 if (index !== -1) arr.splice(index, 1)
138 }
139
140 function sortBy (obj: any[], key1: string, key2?: string) {
141 return obj.sort((a, b) => {
142 const elem1 = key2 ? a[key1][key2] : a[key1]
143 const elem2 = key2 ? b[key1][key2] : b[key1]
144
145 if (elem1 < elem2) return -1
146 if (elem1 === elem2) return 0
147 return 1
148 })
149 }
150
151 function scrollToTop (behavior: 'auto' | 'smooth' = 'auto') {
152 window.scrollTo({
153 left: 0,
154 top: 0,
155 behavior
156 })
157 }
158
159 function isInViewport (el: HTMLElement) {
160 const bounding = el.getBoundingClientRect()
161 return (
162 bounding.top >= 0 &&
163 bounding.left >= 0 &&
164 bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
165 bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
166 )
167 }
168
169 function isXPercentInViewport (el: HTMLElement, percentVisible: number) {
170 const rect = el.getBoundingClientRect()
171 const windowHeight = (window.innerHeight || document.documentElement.clientHeight)
172
173 return !(
174 Math.floor(100 - (((rect.top >= 0 ? 0 : rect.top) / +-(rect.height / 1)) * 100)) < percentVisible ||
175 Math.floor(100 - ((rect.bottom - windowHeight) / rect.height) * 100) < percentVisible
176 )
177 }
178
179 function genericUploadErrorHandler (parameters: {
180 err: Pick<HttpErrorResponse, 'message' | 'status' | 'headers'>
181 name: string
182 notifier: Notifier
183 sticky?: boolean
184 }) {
185 const { err, name, notifier, sticky } = { sticky: false, ...parameters }
186 const title = $localize`The upload failed`
187 let message = err.message
188
189 if (err instanceof ErrorEvent) { // network error
190 message = $localize`The connection was interrupted`
191 notifier.error(message, title, null, sticky)
192 } else if (err.status === HttpStatusCode.INTERNAL_SERVER_ERROR_500) {
193 message = $localize`The server encountered an error`
194 notifier.error(message, title, null, sticky)
195 } else if (err.status === HttpStatusCode.REQUEST_TIMEOUT_408) {
196 message = $localize`Your ${name} file couldn't be transferred before the set timeout (usually 10min)`
197 notifier.error(message, title, null, sticky)
198 } else if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) {
199 const maxFileSize = err.headers?.get('X-File-Maximum-Size') || '8G'
200 message = $localize`Your ${name} file was too large (max. size: ${maxFileSize})`
201 notifier.error(message, title, null, sticky)
202 } else {
203 notifier.error(err.message, title)
204 }
205
206 return message
207 }
208
209 export {
210 sortBy,
211 durationToString,
212 lineFeedToHtml,
213 getParameterByName,
214 getAbsoluteAPIUrl,
215 dateToHuman,
216 immutableAssign,
217 objectToFormData,
218 getAbsoluteEmbedUrl,
219 objectLineFeedToHtml,
220 removeElementFromArray,
221 scrollToTop,
222 isInViewport,
223 isXPercentInViewport,
224 listUserChannels,
225 genericUploadErrorHandler
226 }