From dd24f1bb0a4b252e5342b251ba36853364da7b8e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 19 Aug 2021 09:24:29 +0200 Subject: Add video filters to common video pages --- client/src/app/helpers/utils.ts | 226 -------------------------------- client/src/app/helpers/utils/channel.ts | 34 +++++ client/src/app/helpers/utils/date.ts | 25 ++++ client/src/app/helpers/utils/html.ts | 18 +++ client/src/app/helpers/utils/index.ts | 7 + client/src/app/helpers/utils/object.ts | 47 +++++++ client/src/app/helpers/utils/ui.ts | 33 +++++ client/src/app/helpers/utils/upload.ts | 37 ++++++ client/src/app/helpers/utils/url.ts | 71 ++++++++++ 9 files changed, 272 insertions(+), 226 deletions(-) delete mode 100644 client/src/app/helpers/utils.ts create mode 100644 client/src/app/helpers/utils/channel.ts create mode 100644 client/src/app/helpers/utils/date.ts create mode 100644 client/src/app/helpers/utils/html.ts create mode 100644 client/src/app/helpers/utils/index.ts create mode 100644 client/src/app/helpers/utils/object.ts create mode 100644 client/src/app/helpers/utils/ui.ts create mode 100644 client/src/app/helpers/utils/upload.ts create mode 100644 client/src/app/helpers/utils/url.ts (limited to 'client/src/app/helpers') diff --git a/client/src/app/helpers/utils.ts b/client/src/app/helpers/utils.ts deleted file mode 100644 index 8636f3a55..000000000 --- a/client/src/app/helpers/utils.ts +++ /dev/null @@ -1,226 +0,0 @@ -import { first, map } from 'rxjs/operators' -import { SelectChannelItem } from 'src/types/select-options-item.model' -import { DatePipe } from '@angular/common' -import { HttpErrorResponse } from '@angular/common/http' -import { Notifier } from '@app/core' -import { HttpStatusCode } from '@shared/models' -import { environment } from '../../environments/environment' -import { AuthService } from '../core/auth' - -// 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, ' ')) -} - -function listUserChannels (authService: AuthService) { - return authService.userInformationLoaded - .pipe( - first(), - map(() => { - const user = authService.getUser() - if (!user) return undefined - - const videoChannels = user.videoChannels - if (Array.isArray(videoChannels) === false) return undefined - - return videoChannels - .sort((a, b) => { - if (a.updatedAt < b.updatedAt) return 1 - if (a.updatedAt > b.updatedAt) return -1 - return 0 - }) - .map(c => ({ - id: c.id, - label: c.displayName, - support: c.support, - avatarPath: c.avatar?.path - }) as SelectChannelItem) - }) - ) -} - -function getAbsoluteAPIUrl () { - let absoluteAPIUrl = environment.hmr === true - ? 'http://localhost:9000' - : environment.apiUrl - - if (!absoluteAPIUrl) { - // The API is on the same domain - absoluteAPIUrl = window.location.origin - } - - return absoluteAPIUrl -} - -function getAbsoluteEmbedUrl () { - let absoluteEmbedUrl = environment.originServerUrl - if (!absoluteEmbedUrl) { - // The Embed is on the same domain - absoluteEmbedUrl = window.location.origin - } - - return absoluteEmbedUrl -} - -const datePipe = new DatePipe('en') -function dateToHuman (date: string) { - return datePipe.transform(date, 'medium') -} - -function durationToString (duration: number) { - const hours = Math.floor(duration / 3600) - const minutes = Math.floor((duration % 3600) / 60) - const seconds = duration % 60 - - const minutesPadding = minutes >= 10 ? '' : '0' - const secondsPadding = seconds >= 10 ? '' : '0' - const displayedHours = hours > 0 ? hours.toString() + ':' : '' - - return ( - displayedHours + minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString() - ).replace(/^0/, '') -} - -function immutableAssign (target: A, source: B) { - return Object.assign({}, target, source) -} - -// Thanks: https://gist.github.com/ghinda/8442a57f22099bdb2e34 -function objectToFormData (obj: any, form?: FormData, namespace?: string) { - const fd = form || new FormData() - let formKey - - for (const key of Object.keys(obj)) { - if (namespace) formKey = `${namespace}[${key}]` - else formKey = key - - if (obj[key] === undefined) continue - - if (Array.isArray(obj[key]) && obj[key].length === 0) { - fd.append(key, null) - continue - } - - if (obj[key] !== null && typeof obj[key] === 'object' && !(obj[key] instanceof File)) { - objectToFormData(obj[key], fd, formKey) - } else { - fd.append(formKey, obj[key]) - } - } - - return fd -} - -function objectLineFeedToHtml (obj: any, keyToNormalize: string) { - return immutableAssign(obj, { - [keyToNormalize]: lineFeedToHtml(obj[keyToNormalize]) - }) -} - -function lineFeedToHtml (text: string) { - if (!text) return text - - return text.replace(/\r?\n|\r/g, '
') -} - -function removeElementFromArray (arr: T[], elem: T) { - const index = arr.indexOf(elem) - if (index !== -1) arr.splice(index, 1) -} - -function sortBy (obj: any[], key1: string, key2?: string) { - return obj.sort((a, b) => { - const elem1 = key2 ? a[key1][key2] : a[key1] - const elem2 = key2 ? b[key1][key2] : b[key1] - - if (elem1 < elem2) return -1 - if (elem1 === elem2) return 0 - return 1 - }) -} - -function scrollToTop (behavior: 'auto' | 'smooth' = 'auto') { - window.scrollTo({ - left: 0, - top: 0, - behavior - }) -} - -function isInViewport (el: HTMLElement) { - const bounding = el.getBoundingClientRect() - return ( - bounding.top >= 0 && - bounding.left >= 0 && - bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) && - bounding.right <= (window.innerWidth || document.documentElement.clientWidth) - ) -} - -function isXPercentInViewport (el: HTMLElement, percentVisible: number) { - const rect = el.getBoundingClientRect() - const windowHeight = (window.innerHeight || document.documentElement.clientHeight) - - return !( - Math.floor(100 - (((rect.top >= 0 ? 0 : rect.top) / +-(rect.height / 1)) * 100)) < percentVisible || - Math.floor(100 - ((rect.bottom - windowHeight) / rect.height) * 100) < percentVisible - ) -} - -function genericUploadErrorHandler (parameters: { - err: Pick - name: string - notifier: Notifier - sticky?: boolean -}) { - const { err, name, notifier, sticky } = { sticky: false, ...parameters } - const title = $localize`The upload failed` - let message = err.message - - if (err instanceof ErrorEvent) { // network error - message = $localize`The connection was interrupted` - notifier.error(message, title, null, sticky) - } else if (err.status === HttpStatusCode.INTERNAL_SERVER_ERROR_500) { - message = $localize`The server encountered an error` - notifier.error(message, title, null, sticky) - } else if (err.status === HttpStatusCode.REQUEST_TIMEOUT_408) { - message = $localize`Your ${name} file couldn't be transferred before the set timeout (usually 10min)` - notifier.error(message, title, null, sticky) - } else if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) { - const maxFileSize = err.headers?.get('X-File-Maximum-Size') || '8G' - message = $localize`Your ${name} file was too large (max. size: ${maxFileSize})` - notifier.error(message, title, null, sticky) - } else { - notifier.error(err.message, title) - } - - return message -} - -export { - sortBy, - durationToString, - lineFeedToHtml, - getParameterByName, - getAbsoluteAPIUrl, - dateToHuman, - immutableAssign, - objectToFormData, - getAbsoluteEmbedUrl, - objectLineFeedToHtml, - removeElementFromArray, - scrollToTop, - isInViewport, - isXPercentInViewport, - listUserChannels, - genericUploadErrorHandler -} diff --git a/client/src/app/helpers/utils/channel.ts b/client/src/app/helpers/utils/channel.ts new file mode 100644 index 000000000..93863a8af --- /dev/null +++ b/client/src/app/helpers/utils/channel.ts @@ -0,0 +1,34 @@ +import { first, map } from 'rxjs/operators' +import { SelectChannelItem } from 'src/types/select-options-item.model' +import { AuthService } from '../../core/auth' + +function listUserChannels (authService: AuthService) { + return authService.userInformationLoaded + .pipe( + first(), + map(() => { + const user = authService.getUser() + if (!user) return undefined + + const videoChannels = user.videoChannels + if (Array.isArray(videoChannels) === false) return undefined + + return videoChannels + .sort((a, b) => { + if (a.updatedAt < b.updatedAt) return 1 + if (a.updatedAt > b.updatedAt) return -1 + return 0 + }) + .map(c => ({ + id: c.id, + label: c.displayName, + support: c.support, + avatarPath: c.avatar?.path + }) as SelectChannelItem) + }) + ) +} + +export { + listUserChannels +} diff --git a/client/src/app/helpers/utils/date.ts b/client/src/app/helpers/utils/date.ts new file mode 100644 index 000000000..012b959ea --- /dev/null +++ b/client/src/app/helpers/utils/date.ts @@ -0,0 +1,25 @@ +import { DatePipe } from '@angular/common' + +const datePipe = new DatePipe('en') +function dateToHuman (date: string) { + return datePipe.transform(date, 'medium') +} + +function durationToString (duration: number) { + const hours = Math.floor(duration / 3600) + const minutes = Math.floor((duration % 3600) / 60) + const seconds = duration % 60 + + const minutesPadding = minutes >= 10 ? '' : '0' + const secondsPadding = seconds >= 10 ? '' : '0' + const displayedHours = hours > 0 ? hours.toString() + ':' : '' + + return ( + displayedHours + minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString() + ).replace(/^0/, '') +} + +export { + durationToString, + dateToHuman +} diff --git a/client/src/app/helpers/utils/html.ts b/client/src/app/helpers/utils/html.ts new file mode 100644 index 000000000..2d520aee9 --- /dev/null +++ b/client/src/app/helpers/utils/html.ts @@ -0,0 +1,18 @@ +import { immutableAssign } from './object' + +function objectLineFeedToHtml (obj: any, keyToNormalize: string) { + return immutableAssign(obj, { + [keyToNormalize]: lineFeedToHtml(obj[keyToNormalize]) + }) +} + +function lineFeedToHtml (text: string) { + if (!text) return text + + return text.replace(/\r?\n|\r/g, '
') +} + +export { + objectLineFeedToHtml, + lineFeedToHtml +} diff --git a/client/src/app/helpers/utils/index.ts b/client/src/app/helpers/utils/index.ts new file mode 100644 index 000000000..dc09c92ab --- /dev/null +++ b/client/src/app/helpers/utils/index.ts @@ -0,0 +1,7 @@ +export * from './channel' +export * from './date' +export * from './html' +export * from './object' +export * from './ui' +export * from './upload' +export * from './url' diff --git a/client/src/app/helpers/utils/object.ts b/client/src/app/helpers/utils/object.ts new file mode 100644 index 000000000..1ca4a23ac --- /dev/null +++ b/client/src/app/helpers/utils/object.ts @@ -0,0 +1,47 @@ +function immutableAssign (target: A, source: B) { + return Object.assign({}, target, source) +} + +function removeElementFromArray (arr: T[], elem: T) { + const index = arr.indexOf(elem) + if (index !== -1) arr.splice(index, 1) +} + +function sortBy (obj: any[], key1: string, key2?: string) { + return obj.sort((a, b) => { + const elem1 = key2 ? a[key1][key2] : a[key1] + const elem2 = key2 ? b[key1][key2] : b[key1] + + if (elem1 < elem2) return -1 + if (elem1 === elem2) return 0 + return 1 + }) +} + +function intoArray (value: any) { + if (!value) return undefined + if (Array.isArray(value)) return value + + if (typeof value === 'string') return value.split(',') + + return [ value ] +} + +function toBoolean (value: any) { + if (!value) return undefined + + if (typeof value === 'boolean') return value + + if (value === 'true') return true + if (value === 'false') return false + + return undefined +} + +export { + sortBy, + immutableAssign, + removeElementFromArray, + intoArray, + toBoolean +} diff --git a/client/src/app/helpers/utils/ui.ts b/client/src/app/helpers/utils/ui.ts new file mode 100644 index 000000000..ac8298926 --- /dev/null +++ b/client/src/app/helpers/utils/ui.ts @@ -0,0 +1,33 @@ +function scrollToTop (behavior: 'auto' | 'smooth' = 'auto') { + window.scrollTo({ + left: 0, + top: 0, + behavior + }) +} + +function isInViewport (el: HTMLElement) { + const bounding = el.getBoundingClientRect() + return ( + bounding.top >= 0 && + bounding.left >= 0 && + bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) && + bounding.right <= (window.innerWidth || document.documentElement.clientWidth) + ) +} + +function isXPercentInViewport (el: HTMLElement, percentVisible: number) { + const rect = el.getBoundingClientRect() + const windowHeight = (window.innerHeight || document.documentElement.clientHeight) + + return !( + Math.floor(100 - (((rect.top >= 0 ? 0 : rect.top) / +-(rect.height / 1)) * 100)) < percentVisible || + Math.floor(100 - ((rect.bottom - windowHeight) / rect.height) * 100) < percentVisible + ) +} + +export { + scrollToTop, + isInViewport, + isXPercentInViewport +} diff --git a/client/src/app/helpers/utils/upload.ts b/client/src/app/helpers/utils/upload.ts new file mode 100644 index 000000000..a3fce7fee --- /dev/null +++ b/client/src/app/helpers/utils/upload.ts @@ -0,0 +1,37 @@ +import { HttpErrorResponse } from '@angular/common/http' +import { Notifier } from '@app/core' +import { HttpStatusCode } from '@shared/models' + +function genericUploadErrorHandler (parameters: { + err: Pick + name: string + notifier: Notifier + sticky?: boolean +}) { + const { err, name, notifier, sticky } = { sticky: false, ...parameters } + const title = $localize`The upload failed` + let message = err.message + + if (err instanceof ErrorEvent) { // network error + message = $localize`The connection was interrupted` + notifier.error(message, title, null, sticky) + } else if (err.status === HttpStatusCode.INTERNAL_SERVER_ERROR_500) { + message = $localize`The server encountered an error` + notifier.error(message, title, null, sticky) + } else if (err.status === HttpStatusCode.REQUEST_TIMEOUT_408) { + message = $localize`Your ${name} file couldn't be transferred before the set timeout (usually 10min)` + notifier.error(message, title, null, sticky) + } else if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) { + const maxFileSize = err.headers?.get('X-File-Maximum-Size') || '8G' + message = $localize`Your ${name} file was too large (max. size: ${maxFileSize})` + notifier.error(message, title, null, sticky) + } else { + notifier.error(err.message, title) + } + + return message +} + +export { + genericUploadErrorHandler +} diff --git a/client/src/app/helpers/utils/url.ts b/client/src/app/helpers/utils/url.ts new file mode 100644 index 000000000..82d9cc11b --- /dev/null +++ b/client/src/app/helpers/utils/url.ts @@ -0,0 +1,71 @@ +import { environment } from '../../../environments/environment' + +// 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, ' ')) +} + +function getAbsoluteAPIUrl () { + let absoluteAPIUrl = environment.hmr === true + ? 'http://localhost:9000' + : environment.apiUrl + + if (!absoluteAPIUrl) { + // The API is on the same domain + absoluteAPIUrl = window.location.origin + } + + return absoluteAPIUrl +} + +function getAbsoluteEmbedUrl () { + let absoluteEmbedUrl = environment.originServerUrl + if (!absoluteEmbedUrl) { + // The Embed is on the same domain + absoluteEmbedUrl = window.location.origin + } + + return absoluteEmbedUrl +} + +// Thanks: https://gist.github.com/ghinda/8442a57f22099bdb2e34 +function objectToFormData (obj: any, form?: FormData, namespace?: string) { + const fd = form || new FormData() + let formKey + + for (const key of Object.keys(obj)) { + if (namespace) formKey = `${namespace}[${key}]` + else formKey = key + + if (obj[key] === undefined) continue + + if (Array.isArray(obj[key]) && obj[key].length === 0) { + fd.append(key, null) + continue + } + + if (obj[key] !== null && typeof obj[key] === 'object' && !(obj[key] instanceof File)) { + objectToFormData(obj[key], fd, formKey) + } else { + fd.append(formKey, obj[key]) + } + } + + return fd +} + +export { + getParameterByName, + objectToFormData, + getAbsoluteAPIUrl, + getAbsoluteEmbedUrl +} -- cgit v1.2.3