Skip to content

Commit f3d46a7

Browse files
committed
refactor(frontend/utils): typescript
1 parent 1e048bd commit f3d46a7

10 files changed

+126
-92
lines changed

frontend/app/utils/format-duration.js renamed to frontend/app/utils/format-duration.ts

+6-9
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,18 @@
33
* @submodule timed-utils
44
* @public
55
*/
6-
import moment from "moment";
6+
import moment, { type Duration } from "moment";
77
import { pad2joincolon } from "timed/utils/pad";
88

99
const { floor, abs } = Math;
1010

1111
/**
1212
* Converts a moment duration into a string with zeropadded digits
13-
*
14-
* @function formatDuration
15-
* @param {moment.duration} duration The duration to format
16-
* @param {Boolean} seconds Whether to show seconds
17-
* @return {String} The formatted duration
18-
* @public
1913
*/
20-
export default function formatDuration(duration, seconds = true) {
14+
export default function formatDuration(
15+
duration: Duration | number,
16+
seconds: boolean = true
17+
): string {
2118
if (typeof duration === "number") {
2219
duration = moment.duration(duration);
2320
}
@@ -26,7 +23,7 @@ export default function formatDuration(duration, seconds = true) {
2623
return seconds ? "--:--:--" : "--:--";
2724
}
2825

29-
const prefix = duration < 0 ? "-" : "";
26+
const prefix = +duration < 0 ? "-" : "";
3027

3128
const hours = floor(abs(duration.asHours()));
3229
const minutes = abs(duration.minutes());

frontend/app/utils/humanize-duration.js renamed to frontend/app/utils/humanize-duration.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,23 @@
44
* @public
55
*/
66

7+
import type { Duration } from "moment";
8+
79
const { abs, floor } = Math;
810

911
/**
1012
* Converts a moment duration into a string with hours minutes and optionally
1113
* seconds
12-
*
13-
* @function humanizeDuration
14-
* @param {moment.duration} duration The duration to format
15-
* @param {Boolean} seconds Whether to show seconds
16-
* @return {String} The formatted duration
17-
* @public
1814
*/
19-
export default function humanizeDuration(duration, seconds = false) {
15+
export default function humanizeDuration(
16+
duration: Duration,
17+
seconds: boolean = false
18+
): string {
2019
if (!duration || duration.milliseconds() < 0) {
2120
return seconds ? "0h 0m 0s" : "0h 0m";
2221
}
2322

24-
const prefix = duration < 0 ? "-" : "";
23+
const prefix = +duration < 0 ? "-" : "";
2524

2625
// TODO: The locale should be defined by the browser
2726
const h = floor(abs(duration.asHours())).toLocaleString("de-CH");

frontend/app/utils/pad.js renamed to frontend/app/utils/pad.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,8 @@
66

77
/**
88
* Pad items with 0 and join them with a colon
9-
*
10-
* @function pad2joincolon
11-
* @param {any[]} items - The items to pad and join
12-
* @return {String} The joined string
13-
* @public
149
*/
15-
function pad2joincolon(...items) {
10+
function pad2joincolon(...items: unknown[]): string {
1611
return items.map((v) => String(v).padStart(2, "0")).join(":");
1712
}
1813

frontend/app/utils/parse-django-duration.js

-34
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @module timed
3+
* @submodule timed-utils
4+
* @public
5+
*/
6+
import moment, { type Duration } from "moment";
7+
8+
interface Groups {
9+
days?: number;
10+
hours: number;
11+
minutes: number;
12+
seconds: number;
13+
microseconds?: number;
14+
}
15+
16+
/**
17+
* Converts a django duration string to a moment duration
18+
*/
19+
export default function parseDjangoDuration(str: string): Duration | null {
20+
if (!str) {
21+
return null;
22+
}
23+
24+
const re =
25+
/^(?<days>-?\d+)?\s?(?<hours>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2})(?<microseconds>\.\d{6})?$/;
26+
27+
const matches = str.match(re) as {
28+
groups: Groups;
29+
} | null;
30+
31+
if (!matches) {
32+
return null;
33+
}
34+
35+
const {
36+
days: _days,
37+
hours,
38+
minutes,
39+
seconds,
40+
microseconds: _microseconds,
41+
} = matches.groups;
42+
43+
const [days, microseconds] = [_days, _microseconds].map(
44+
(v) => Number(v) || 0
45+
) as [number, number];
46+
47+
return moment.duration({
48+
days,
49+
hours,
50+
minutes,
51+
seconds,
52+
milliseconds: microseconds * 1000,
53+
});
54+
}

frontend/app/utils/parse-filename.js renamed to frontend/app/utils/parse-filename.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
const REGEX =
1212
/filename[^;=\n]*=(?<filename>(?<quote>['"]).*?\k<quote>|[^;\n]*)/;
1313

14-
const parseFileName = (contentDisposition) => {
14+
const parseFileName = (contentDisposition: string) => {
1515
const { quote, filename } = REGEX.exec(contentDisposition)?.groups ?? {};
1616
if (!filename) return "Unknown file";
1717
const _filename = filename.startsWith("utf-8''")

frontend/app/utils/query-params.js renamed to frontend/app/utils/query-params.ts

+29-20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type Controller from "@ember/controller";
12
import { get } from "@ember/object";
23
import { underscore } from "@ember/string";
34
import {
@@ -8,56 +9,61 @@ import {
89
/**
910
* Filter params by key
1011
*/
11-
export const filterQueryParams = (params, ...keys) => {
12-
return Object.keys(params).reduce((obj, key) => {
13-
return keys.includes(key) ? obj : { ...obj, [key]: get(params, key) };
14-
}, {});
12+
export const filterQueryParams = <
13+
T extends Record<string, unknown>,
14+
K extends string[]
15+
>(
16+
params: T,
17+
...keys: K
18+
) => {
19+
return Object.fromEntries(
20+
Object.entries(params).filter(([k]) => !keys.includes(k))
21+
) as Omit<T, K[number]>;
1522
};
1623

1724
/**
1825
* Underscore all object keys
1926
*/
20-
export const underscoreQueryParams = (params) => {
21-
return Object.keys(params).reduce((obj, key) => {
22-
return { ...obj, [underscore(key)]: get(params, key) };
23-
}, {});
24-
};
27+
export const underscoreQueryParams = (params: Record<string, unknown>) =>
28+
Object.fromEntries(
29+
Object.entries(params).map(([k, v]) => [underscore(k), v])
30+
);
2531

26-
export const serializeQueryParams = (params, queryParamsObject) => {
32+
export const serializeQueryParams = <T extends Record<string, unknown>>(
33+
params: T,
34+
queryParamsObject: { [K in keyof T]?: (deserialized: T[K]) => string }
35+
) => {
2736
return Object.keys(params).reduce((parsed, key) => {
28-
const serializeFn = get(queryParamsObject, key)?.serialize;
29-
const value = get(params, key);
37+
const serializeFn = queryParamsObject[key as keyof T];
38+
const value = params[key as keyof T];
3039

3140
return key === "type"
3241
? parsed
3342
: {
3443
...parsed,
3544
[key]: serializeFn ? serializeFn(value) : value,
3645
};
37-
}, {});
46+
}, {} as Record<keyof T, string>);
3847
};
3948

4049
/**
41-
*
42-
* @param {string} param
43-
* @returns {string} | {undefined}
4450
* ? in all controllers, the only parameter that have the default value is `ordering`, and the value is "-date"
4551
*/
46-
export function getDefaultQueryParamValue(param) {
52+
export function getDefaultQueryParamValue(param: string) {
4753
if (param === "ordering") return "-date";
4854
else if (param === "type") return "year";
4955
return undefined;
5056
}
5157

52-
export function allQueryParams(controller) {
58+
export function allQueryParams<C extends Controller>(controller: C) {
5359
const params = {};
5460
for (const qpKey of controller.queryParams) {
5561
params[qpKey] = controller[qpKey];
5662
}
5763
return params;
5864
}
5965

60-
export function queryParamsState(controller) {
66+
export function queryParamsState<C extends Controller>(controller: C) {
6167
const states = {};
6268
for (const param of controller.queryParams) {
6369
const defaultValue = getDefaultQueryParamValue(param);
@@ -94,7 +100,10 @@ export function queryParamsState(controller) {
94100
return states;
95101
}
96102

97-
export function resetQueryParams(controller, ...args) {
103+
export function resetQueryParams<C extends Controller>(
104+
controller: C,
105+
...args: string[]
106+
) {
98107
if (!args[0]) {
99108
return;
100109
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import moment from "moment";
1+
import moment, { type Moment, type MomentInput } from "moment";
22

33
export const DATE_FORMAT = "YYYY-MM-DD";
44

5-
export function serializeMoment(momentObject) {
5+
export function serializeMoment(momentObject: Moment) {
66
if (momentObject) {
77
momentObject = moment(momentObject);
88
}
99
return (momentObject && momentObject.format(DATE_FORMAT)) || null;
1010
}
11-
export function deserializeMoment(momentString) {
11+
export function deserializeMoment(momentString: MomentInput) {
1212
return (momentString && moment(momentString, DATE_FORMAT)) || null;
1313
}

frontend/app/utils/url.js

-11
This file was deleted.

frontend/app/utils/url.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
type ExcludeNullOrUndefined<T> = T extends null | undefined ? never : T;
2+
3+
type CleanedObject<T extends object> = {
4+
[K in keyof T as ExcludeNullOrUndefined<T[K]> extends never
5+
? never
6+
: K]: ExcludeNullOrUndefined<T[K]>;
7+
};
8+
9+
const notNullOrUndefined = <T>(value: T): value is ExcludeNullOrUndefined<T> =>
10+
value !== null && value !== undefined;
11+
12+
export const cleanParams = <T extends object>(params: T): CleanedObject<T> => {
13+
const cleanedEntries = Object.entries(params).filter(([, value]) =>
14+
notNullOrUndefined(value)
15+
);
16+
return Object.fromEntries(cleanedEntries) as CleanedObject<T>;
17+
};
18+
19+
export const toQueryString = (params: Record<string, unknown>): string =>
20+
Object.entries(params)
21+
.map(
22+
([key, value]) =>
23+
`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
24+
)
25+
.join("&");

0 commit comments

Comments
 (0)