refactor: move Clock from class component to functional component (#31964)

This commit is contained in:
Florian Duros
2026-02-04 11:15:06 +00:00
committed by GitHub
parent ae013686f5
commit 877ab183d9
@@ -5,13 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
import React, { type HTMLProps } from "react"; import React, { type JSX, type HTMLProps, useMemo } from "react";
import { Temporal } from "temporal-polyfill"; import { Temporal } from "temporal-polyfill";
import classNames from "classnames"; import classNames from "classnames";
import { formatSeconds } from "../../utils/DateUtils"; import { formatSeconds } from "../../utils/DateUtils";
export interface Props extends Pick<HTMLProps<HTMLSpanElement>, "aria-live" | "role" | "className"> { export interface Props extends Pick<HTMLProps<HTMLSpanElement>, "aria-live" | "role" | "className"> {
/**
* The number of seconds to display.
*/
seconds: number; seconds: number;
} }
@@ -19,33 +22,39 @@ export interface Props extends Pick<HTMLProps<HTMLSpanElement>, "aria-live" | "r
* Clock which represents time periods rather than absolute time. * Clock which represents time periods rather than absolute time.
* Simply converts seconds using formatSeconds(). * Simply converts seconds using formatSeconds().
* Note that in this case hours will not be displayed, making it possible to see "82:29". * Note that in this case hours will not be displayed, making it possible to see "82:29".
*
* @example
* ```tsx
* <Clock seconds={125} />
* ```
*/ */
export class Clock extends React.Component<Props> { export function Clock({ seconds, className, ...rest }: Props): JSX.Element {
public shouldComponentUpdate(nextProps: Readonly<Props>): boolean { // Memoize current second to avoid recalculating the duration when seconds changes slightly (e.g. 1.2 -> 1.3)
const currentFloor = Math.floor(this.props.seconds); const currentSecond = useMemo(() => Math.floor(seconds), [seconds]);
const nextFloor = Math.floor(nextProps.seconds); const duration = useMemo(() => calculateDuration(currentSecond), [currentSecond]);
return currentFloor !== nextFloor;
}
private calculateDuration(seconds: number): string | undefined { return (
if (isNaN(seconds)) return undefined; <time
return new Temporal.Duration(0, 0, 0, 0, 0, 0, Math.round(seconds)) dateTime={duration}
.round({ smallestUnit: "seconds", largestUnit: "hours" }) /* Keep class for backward compatibility with parent component */
.toString(); className={classNames("mx_Clock", className)}
} {...rest}
>
{formatSeconds(seconds)}
</time>
);
}
public render(): React.ReactNode { /**
const { seconds, role } = this.props; * Calculates an ISO 8601 duration string from seconds.
return ( * @param seconds
<time * @returns ISO 8601 duration string or undefined if input is NaN
dateTime={this.calculateDuration(seconds)} */
aria-live={this.props["aria-live"]} function calculateDuration(seconds: number): string | undefined {
role={role} // This shouldn't happen but it's in the original implementation
/* Keep class for backward compatibility with parent component */ if (isNaN(seconds)) return undefined;
className={classNames("mx_Clock", this.props.className)}
> return new Temporal.Duration(0, 0, 0, 0, 0, 0, Math.round(seconds))
{formatSeconds(seconds)} .round({ smallestUnit: "seconds", largestUnit: "hours" })
</time> .toString();
);
}
} }