Files
Anton GilgurandGitHub 13ce982611 refactor(deps)!: remove moment dep and usage (#535)
* refactor(deps): remove `moment` dep and usage

`moment` has been [deprecated since Sep 2020](https://momentjs.com/docs/#/-project-status/) and recommends using native `Intl` or newer libraries that make use of native `Intl`, such as `luxon` and `dayjs`
- `moment` is also a very large dependency and hence is ripe for pruning and replacement as well

- remove `formatTimestamp` function from `v2/utils` as it is unused
  - it is unused [internally](https://github.com/search?q=repo%3Aargoproj%2Fargo-ui+formattimestamp&type=code) (it is only exported here), unused by [CD](https://github.com/search?q=repo%3Aargoproj%2Fargo-cd%20formattimestamp&type=code), unused by [Workflows](https://github.com/search?q=repo%3Aargoproj%2Fargo-workflows+formattimestamp&type=code)
    - it's also and unused by Rollouts, which has [two](https://github.com/argoproj/argo-rollouts/blob/c688dd89a7f883c8c004b4024b390104b5be5770/ui/src/app/shared/utils/utils.tsx#L17) [internal](https://github.com/argoproj/argo-rollouts/blob/c688dd89a7f883c8c004b4024b390104b5be5770/ui/src/app/components/rollout/revision.tsx#L21) copies of this function

- change `Ticker` component to use plain `Date` instead of `moment`
  - downstream projects should get a type error on this which should make the switch straightforward

- change `Duration` component to use existing `formatDuration` function which doesn't use `moment`
  - this will look different than the previous version, but the formatting between the two should really be consistent in any case
  - other option was to just remove this component entirely, since this repo is not actively maintained
    - leaving the component in instead of deleting it gives a more gradual off-ramp
  - CD uses this component in [two](https://github.com/argoproj/argo-cd/blob/9ecc5aec2a4dba485eb8b5b0ab75fff8927b3418/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx#L1) [places](https://github.com/argoproj/argo-cd/blob/9ecc5aec2a4dba485eb8b5b0ab75fff8927b3418/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx#L1) and Rollouts uses this in [one place](https://github.com/argoproj/argo-rollouts/blob/c688dd89a7f883c8c004b4024b390104b5be5770/ui/src/app/components/pods/pods.tsx#L3)
    - Workflows does not use this, it has [its own `DurationPanel` component](https://github.com/argoproj/argo-workflows/blob/1dbc856e51967feb58066a4087a8679b08b87be3/ui/src/app/shared/components/duration-panel.tsx#L7) and its own [internal copy](https://github.com/argoproj/argo-workflows/blob/1dbc856e51967feb58066a4087a8679b08b87be3/ui/src/app/shared/duration.ts#L11) of the `formatDuration` function as well
    - none of these use the `allowNewLines` prop, so it is safe to remove

Signed-off-by: Anton Gilgur <[email protected]>

* fix(ui): duration component should div not mult

ms -> s

Signed-off-by: Anton Gilgur <[email protected]>

* fix incorrect parameter naming

- apparently `durationMs` was incorrectly named as it actually took seconds, not milliseconds
- for backward compat, leave `durationMs`, but also introduce `durationS`
  - and add JSDoc to indicate this to users of the component

Signed-off-by: Anton Gilgur <[email protected]>

* use 2 significant figures

Signed-off-by: Anton Gilgur <[email protected]>

---------

Signed-off-by: Anton Gilgur <[email protected]>
2024-08-22 14:03:20 -04:00

134 lines
3.9 KiB
TypeScript

import * as React from 'react';
export interface Error {
state: boolean;
retry: () => void;
}
export function useData<T>(getData: () => Promise<T>, init?: T, callback?: (data: T) => void, dependencies?: any[]): [T, boolean, Error] {
const [data, setData] = React.useState(init as T);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(false);
const [retrying, retry] = React.useState(false);
React.useEffect(() => {
const fx = async () => {
try {
setLoading(true);
const intData = await getData();
setLoading(false);
setData(intData);
if (callback) {
callback(intData);
}
} catch (e) {
setError(e);
}
};
fx();
}, [...(dependencies || []), retrying]);
return [data as T, loading, {state: error, retry: () => retry(!retrying)} as Error];
}
export const appendSuffixToClasses = (classNames: string, suffix: string): string => {
const clString = classNames;
const cl = (clString || '').split(' ') || [];
const suffixed = [];
for (const c of cl) {
if (!c.endsWith(suffix) && c !== ' ' && c !== '') {
suffixed.push(c + suffix);
}
}
return suffixed.join(' ');
};
export const useClickOutside = (ref: any, callback: () => void) => {
React.useEffect(() => {
const handler = (e: any) => {
if (ref.current && !ref.current.contains(e.target)) {
callback();
}
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
}, [ref, callback]);
};
export const useWidth = (ref: any): number => {
const [width, setWidth] = React.useState(0);
React.useEffect(() => {
setWidth(ref.current ? ref.current.offsetWidth : 0);
}, [ref]);
return width;
};
export const useTimeout = (fx: () => void, timeoutMs: number, dependencies: any[]) => {
React.useEffect(() => {
const to = setTimeout(fx, timeoutMs);
return () => clearInterval(to);
}, dependencies);
};
export const debounce = (fxn: () => any, ms: number) => {
let timer: any;
return () => {
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
fxn.apply(this);
}, ms);
};
};
export function formatDuration(seconds: number, sigfigs = 1) {
let remainingSeconds = Math.abs(Math.round(seconds));
let formattedDuration = '';
const figs = [];
if (remainingSeconds > 86400) {
const days = Math.floor(remainingSeconds / 86400) + 'd';
figs.push(days);
formattedDuration += days;
remainingSeconds = remainingSeconds % 86400;
}
if (remainingSeconds > 3600) {
const hours = Math.floor(remainingSeconds / 3600) + 'h';
figs.push(hours);
formattedDuration += hours;
remainingSeconds = remainingSeconds % 3600;
}
if (remainingSeconds > 60) {
const minutes = Math.floor(remainingSeconds / 60) + 'm';
figs.push(minutes);
formattedDuration += minutes;
remainingSeconds = remainingSeconds % 60;
}
if (remainingSeconds > 0 || Math.round(seconds) === 0) {
figs.push(remainingSeconds + 's');
formattedDuration += remainingSeconds + 's';
}
if (sigfigs <= figs.length) {
formattedDuration = '';
for (let i = 0; i < sigfigs; i++) {
formattedDuration += figs[i];
}
return formattedDuration;
}
return formattedDuration;
}
export const ago = (date: Date) => {
const secondsAgo = (new Date().getTime() - date.getTime()) / 1000;
const duration = formatDuration(secondsAgo);
if (secondsAgo < 0) {
return 'in ' + duration;
} else {
return duration + ' ago';
}
};