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