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

This commit is contained in:
Florian Duros 2026-02-04 12:15:06 +01:00 committed by GitHub
parent ae013686f5
commit 877ab183d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -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.
*/
import React, { type HTMLProps } from "react";
import React, { type JSX, type HTMLProps, useMemo } from "react";
import { Temporal } from "temporal-polyfill";
import classNames from "classnames";
import { formatSeconds } from "../../utils/DateUtils";
export interface Props extends Pick<HTMLProps<HTMLSpanElement>, "aria-live" | "role" | "className"> {
/**
* The number of seconds to display.
*/
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.
* Simply converts seconds using formatSeconds().
* 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> {
public shouldComponentUpdate(nextProps: Readonly<Props>): boolean {
const currentFloor = Math.floor(this.props.seconds);
const nextFloor = Math.floor(nextProps.seconds);
return currentFloor !== nextFloor;
}
export function Clock({ seconds, className, ...rest }: Props): JSX.Element {
// Memoize current second to avoid recalculating the duration when seconds changes slightly (e.g. 1.2 -> 1.3)
const currentSecond = useMemo(() => Math.floor(seconds), [seconds]);
const duration = useMemo(() => calculateDuration(currentSecond), [currentSecond]);
private calculateDuration(seconds: number): string | undefined {
if (isNaN(seconds)) return undefined;
return new Temporal.Duration(0, 0, 0, 0, 0, 0, Math.round(seconds))
.round({ smallestUnit: "seconds", largestUnit: "hours" })
.toString();
}
public render(): React.ReactNode {
const { seconds, role } = this.props;
return (
<time
dateTime={this.calculateDuration(seconds)}
aria-live={this.props["aria-live"]}
role={role}
/* Keep class for backward compatibility with parent component */
className={classNames("mx_Clock", this.props.className)}
>
{formatSeconds(seconds)}
</time>
);
}
return (
<time
dateTime={duration}
/* Keep class for backward compatibility with parent component */
className={classNames("mx_Clock", className)}
{...rest}
>
{formatSeconds(seconds)}
</time>
);
}
/**
* Calculates an ISO 8601 duration string from seconds.
* @param seconds
* @returns ISO 8601 duration string or undefined if input is NaN
*/
function calculateDuration(seconds: number): string | undefined {
// This shouldn't happen but it's in the original implementation
if (isNaN(seconds)) return undefined;
return new Temporal.Duration(0, 0, 0, 0, 0, 0, Math.round(seconds))
.round({ smallestUnit: "seconds", largestUnit: "hours" })
.toString();
}