mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-01 21:13:09 +00:00
Feature/shared refactor (#1176)
* refactor: move most of the shared entities to @start8labs/shared library
This commit is contained in:
165
frontend/projects/shared/src/util/misc.util.ts
Normal file
165
frontend/projects/shared/src/util/misc.util.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { OperatorFunction } from 'rxjs'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
export function trace<T>(t: T): T {
|
||||
console.log(`TRACE`, t)
|
||||
return t
|
||||
}
|
||||
|
||||
// curried description. This allows e.g somePromise.thentraceDesc('my result'))
|
||||
export function traceDesc<T>(description: string): (t: T) => T {
|
||||
return t => {
|
||||
console.log(`TRACE`, description, t)
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
// for use in observables. This allows e.g. someObservable.pipe(traceM('my result'))
|
||||
// the practical equivalent of `tap(t => console.log(t, description))`
|
||||
export function traceWheel<T>(description?: string): OperatorFunction<T, T> {
|
||||
return description ? map(traceDesc(description)) : map(trace)
|
||||
}
|
||||
|
||||
export function traceThrowDesc<T>(description: string, t: T | undefined): T {
|
||||
if (!t) throw new Error(description)
|
||||
return t
|
||||
}
|
||||
|
||||
export function inMs(
|
||||
count: number,
|
||||
unit: 'days' | 'hours' | 'minutes' | 'seconds',
|
||||
) {
|
||||
switch (unit) {
|
||||
case 'seconds':
|
||||
return count * 1000
|
||||
case 'minutes':
|
||||
return inMs(count * 60, 'seconds')
|
||||
case 'hours':
|
||||
return inMs(count * 60, 'minutes')
|
||||
case 'days':
|
||||
return inMs(count * 24, 'hours')
|
||||
}
|
||||
}
|
||||
|
||||
// arr1 - arr2
|
||||
export function diff<T>(arr1: T[], arr2: T[]): T[] {
|
||||
return arr1.filter(x => !arr2.includes(x))
|
||||
}
|
||||
|
||||
// arr1 & arr2
|
||||
export function both<T>(arr1: T[], arr2: T[]): T[] {
|
||||
return arr1.filter(x => arr2.includes(x))
|
||||
}
|
||||
|
||||
export function isObject(val: any): boolean {
|
||||
return val && typeof val === 'object' && !Array.isArray(val)
|
||||
}
|
||||
|
||||
export function isEmptyObject(obj: object): boolean {
|
||||
if (obj === undefined) return true
|
||||
return !Object.keys(obj).length
|
||||
}
|
||||
|
||||
export function pauseFor(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
export function toObject<T>(t: T[], map: (t0: T) => string): Record<string, T> {
|
||||
return t.reduce((acc, next) => {
|
||||
acc[map(next)] = next
|
||||
return acc
|
||||
}, {} as Record<string, T>)
|
||||
}
|
||||
|
||||
export function update<T>(
|
||||
t: Record<string, T>,
|
||||
u: Record<string, T>,
|
||||
): Record<string, T> {
|
||||
return { ...t, ...u }
|
||||
}
|
||||
|
||||
export function deepCloneUnknown<T>(value: T): T {
|
||||
if (typeof value !== 'object' || value === null) {
|
||||
return value
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return deepCloneArray(value)
|
||||
}
|
||||
return deepCloneObject(value)
|
||||
}
|
||||
|
||||
export function deepCloneObject<T>(source: T) {
|
||||
const result = {}
|
||||
Object.keys(source).forEach(key => {
|
||||
const value = source[key]
|
||||
result[key] = deepCloneUnknown(value)
|
||||
}, {})
|
||||
return result as T
|
||||
}
|
||||
|
||||
export function deepCloneArray(collection: any) {
|
||||
return collection.map(value => {
|
||||
return deepCloneUnknown(value)
|
||||
})
|
||||
}
|
||||
|
||||
export function partitionArray<T>(
|
||||
ts: T[],
|
||||
condition: (t: T) => boolean,
|
||||
): [T[], T[]] {
|
||||
const yes = [] as T[]
|
||||
const no = [] as T[]
|
||||
ts.forEach(t => {
|
||||
if (condition(t)) {
|
||||
yes.push(t)
|
||||
} else {
|
||||
no.push(t)
|
||||
}
|
||||
})
|
||||
return [yes, no]
|
||||
}
|
||||
|
||||
export function uniqueBy<T>(
|
||||
ts: T[],
|
||||
uniqueBy: (t: T) => string,
|
||||
prioritize: (t1: T, t2: T) => T,
|
||||
) {
|
||||
return Object.values(
|
||||
ts.reduce((acc, next) => {
|
||||
const previousValue = acc[uniqueBy(next)]
|
||||
if (previousValue) {
|
||||
acc[uniqueBy(next)] = prioritize(acc[uniqueBy(next)], previousValue)
|
||||
} else {
|
||||
acc[uniqueBy(next)] = previousValue
|
||||
}
|
||||
return acc
|
||||
}, {}),
|
||||
)
|
||||
}
|
||||
|
||||
export function capitalizeFirstLetter(string: string): string {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||
}
|
||||
|
||||
export const exists = (t: any) => {
|
||||
return t !== undefined
|
||||
}
|
||||
|
||||
export function debounce(delay: number = 300): MethodDecorator {
|
||||
return function (
|
||||
target: any,
|
||||
propertyKey: string,
|
||||
descriptor: PropertyDescriptor,
|
||||
) {
|
||||
const timeoutKey = Symbol()
|
||||
|
||||
const original = descriptor.value
|
||||
|
||||
descriptor.value = function (...args) {
|
||||
clearTimeout(this[timeoutKey])
|
||||
this[timeoutKey] = setTimeout(() => original.apply(this, args), delay)
|
||||
}
|
||||
|
||||
return descriptor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { isEmptyObject } from './misc.util'
|
||||
import { InstallProgress } from '../types/install-progress'
|
||||
import { ProgressData } from '../types/progress-data'
|
||||
|
||||
export function packageLoadingProgress(
|
||||
loadData: InstallProgress,
|
||||
): ProgressData | null {
|
||||
if (isEmptyObject(loadData)) {
|
||||
return null
|
||||
}
|
||||
|
||||
let {
|
||||
downloaded,
|
||||
validated,
|
||||
unpacked,
|
||||
size,
|
||||
'download-complete': downloadComplete,
|
||||
'validation-complete': validationComplete,
|
||||
'unpack-complete': unpackComplete,
|
||||
} = loadData
|
||||
|
||||
// only permit 100% when "complete" == true
|
||||
downloaded = downloadComplete ? size : Math.max(downloaded - 1, 0)
|
||||
validated = validationComplete ? size : Math.max(validated - 1, 0)
|
||||
unpacked = unpackComplete ? size : Math.max(unpacked - 1, 0)
|
||||
|
||||
const downloadWeight = 1
|
||||
const validateWeight = 0.2
|
||||
const unpackWeight = 0.7
|
||||
|
||||
const numerator = Math.floor(
|
||||
downloadWeight * downloaded +
|
||||
validateWeight * validated +
|
||||
unpackWeight * unpacked,
|
||||
)
|
||||
|
||||
const denominator = Math.floor(
|
||||
size * (downloadWeight + validateWeight + unpackWeight),
|
||||
)
|
||||
const totalProgress = Math.floor((100 * numerator) / denominator)
|
||||
|
||||
return {
|
||||
totalProgress,
|
||||
downloadProgress: Math.floor((100 * downloaded) / size),
|
||||
validateProgress: Math.floor((100 * validated) / size),
|
||||
unpackProgress: Math.floor((100 * unpacked) / size),
|
||||
isComplete: downloadComplete && validationComplete && unpackComplete,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user