Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 69e0386

Browse files
robintownturt2live
andauthored
Show day counts in call durations (#9641)
* Show day counts in call durations Previously call durations over a day long would be truncated, for example displaying as '2h 0m 0s' instead of '1d 2h 0m 0s'. * Fix strict mode errors * Fix strings Co-authored-by: Travis Ralston <[email protected]>
1 parent 440f76c commit 69e0386

File tree

6 files changed

+55
-25
lines changed

6 files changed

+55
-25
lines changed

src/DateUtils.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -124,19 +124,6 @@ export function formatTime(date: Date, showTwelveHour = false): string {
124124
return pad(date.getHours()) + ':' + pad(date.getMinutes());
125125
}
126126

127-
export function formatCallTime(delta: Date): string {
128-
const hours = delta.getUTCHours();
129-
const minutes = delta.getUTCMinutes();
130-
const seconds = delta.getUTCSeconds();
131-
132-
let output = "";
133-
if (hours) output += `${hours}h `;
134-
if (minutes || output) output += `${minutes}m `;
135-
if (seconds || output) output += `${seconds}s`;
136-
137-
return output;
138-
}
139-
140127
export function formatSeconds(inSeconds: number): string {
141128
const hours = Math.floor(inSeconds / (60 * 60)).toFixed(0).padStart(2, '0');
142129
const minutes = Math.floor((inSeconds % (60 * 60)) / 60).toFixed(0).padStart(2, '0');
@@ -238,15 +225,16 @@ export function formatRelativeTime(date: Date, showTwelveHour = false): string {
238225
}
239226
}
240227

228+
const MINUTE_MS = 60000;
229+
const HOUR_MS = MINUTE_MS * 60;
230+
const DAY_MS = HOUR_MS * 24;
231+
241232
/**
242233
* Formats duration in ms to human readable string
243234
* Returns value in biggest possible unit (day, hour, min, second)
244235
* Rounds values up until unit threshold
245236
* ie. 23:13:57 -> 23h, 24:13:57 -> 1d, 44:56:56 -> 2d
246237
*/
247-
const MINUTE_MS = 60000;
248-
const HOUR_MS = MINUTE_MS * 60;
249-
const DAY_MS = HOUR_MS * 24;
250238
export function formatDuration(durationMs: number): string {
251239
if (durationMs >= DAY_MS) {
252240
return _t('%(value)sd', { value: Math.round(durationMs / DAY_MS) });
@@ -259,3 +247,26 @@ export function formatDuration(durationMs: number): string {
259247
}
260248
return _t('%(value)ss', { value: Math.round(durationMs / 1000) });
261249
}
250+
251+
/**
252+
* Formats duration in ms to human readable string
253+
* Returns precise value down to the nearest second
254+
* ie. 23:13:57 -> 23h 13m 57s, 44:56:56 -> 1d 20h 56m 56s
255+
*/
256+
export function formatPreciseDuration(durationMs: number): string {
257+
const days = Math.floor(durationMs / DAY_MS);
258+
const hours = Math.floor((durationMs % DAY_MS) / HOUR_MS);
259+
const minutes = Math.floor((durationMs % HOUR_MS) / MINUTE_MS);
260+
const seconds = Math.floor((durationMs % MINUTE_MS) / 1000);
261+
262+
if (days > 0) {
263+
return _t('%(days)sd %(hours)sh %(minutes)sm %(seconds)ss', { days, hours, minutes, seconds });
264+
}
265+
if (hours > 0) {
266+
return _t('%(hours)sh %(minutes)sm %(seconds)ss', { hours, minutes, seconds });
267+
}
268+
if (minutes > 0) {
269+
return _t('%(minutes)sm %(seconds)ss', { minutes, seconds });
270+
}
271+
return _t('%(value)ss', { value: seconds });
272+
}

src/components/structures/LegacyCallEventGrouper.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ export default class LegacyCallEventGrouper extends EventEmitter {
119119
return Boolean(this.reject);
120120
}
121121

122-
public get duration(): Date {
123-
if (!this.hangup || !this.selectAnswer) return;
124-
return new Date(this.hangup.getDate().getTime() - this.selectAnswer.getDate().getTime());
122+
public get duration(): number | null {
123+
if (!this.hangup || !this.selectAnswer) return null;
124+
return this.hangup.getDate().getTime() - this.selectAnswer.getDate().getTime();
125125
}
126126

127127
/**

src/components/views/messages/LegacyCallEvent.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import LegacyCallEventGrouper, {
2828
import AccessibleButton from '../elements/AccessibleButton';
2929
import InfoTooltip, { InfoTooltipKind } from '../elements/InfoTooltip';
3030
import AccessibleTooltipButton from '../elements/AccessibleTooltipButton';
31-
import { formatCallTime } from "../../../DateUtils";
31+
import { formatPreciseDuration } from "../../../DateUtils";
3232
import Clock from "../audio_messages/Clock";
3333

3434
const MAX_NON_NARROW_WIDTH = 450 / 70 * 100;
@@ -172,10 +172,10 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
172172
// https://github.com/vector-im/riot-android/issues/2623
173173
// Also the correct hangup code as of VoIP v1 (with underscore)
174174
// Also, if we don't have a reason
175-
const duration = this.props.callEventGrouper.duration;
175+
const duration = this.props.callEventGrouper.duration!;
176176
let text = _t("Call ended");
177177
if (duration) {
178-
text += " • " + formatCallTime(duration);
178+
text += " • " + formatPreciseDuration(duration);
179179
}
180180
return (
181181
<div className="mx_LegacyCallEvent_content">

src/components/views/voip/CallDuration.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ limitations under the License.
1717
import React, { FC, useState, useEffect, memo } from "react";
1818
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
1919

20-
import { formatCallTime } from "../../../DateUtils";
20+
import { formatPreciseDuration } from "../../../DateUtils";
2121

2222
interface CallDurationProps {
2323
delta: number;
@@ -29,7 +29,7 @@ interface CallDurationProps {
2929
export const CallDuration: FC<CallDurationProps> = memo(({ delta }) => {
3030
// Clock desync could lead to a negative duration, so just hide it if that happens
3131
if (delta <= 0) return null;
32-
return <div className="mx_CallDuration">{ formatCallTime(new Date(delta)) }</div>;
32+
return <div className="mx_CallDuration">{ formatPreciseDuration(delta) }</div>;
3333
});
3434

3535
interface GroupCallDurationProps {

src/i18n/strings/en_EN.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@
5656
"%(value)sh": "%(value)sh",
5757
"%(value)sm": "%(value)sm",
5858
"%(value)ss": "%(value)ss",
59+
"%(days)sd %(hours)sh %(minutes)sm %(seconds)ss": "%(days)sd %(hours)sh %(minutes)sm %(seconds)ss",
60+
"%(hours)sh %(minutes)sm %(seconds)ss": "%(hours)sh %(minutes)sm %(seconds)ss",
61+
"%(minutes)sm %(seconds)ss": "%(minutes)sm %(seconds)ss",
5962
"Identity server has no terms of service": "Identity server has no terms of service",
6063
"This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.": "This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.",
6164
"Only continue if you trust the owner of the server.": "Only continue if you trust the owner of the server.",

test/utils/DateUtils-test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
formatDuration,
2121
formatFullDateNoDayISO,
2222
formatTimeLeft,
23+
formatPreciseDuration,
2324
} from "../../src/DateUtils";
2425
import { REPEATABLE_DATE } from "../test-utils";
2526

@@ -100,6 +101,22 @@ describe('formatDuration()', () => {
100101
});
101102
});
102103

104+
describe("formatPreciseDuration", () => {
105+
const MINUTE_MS = 1000 * 60;
106+
const HOUR_MS = MINUTE_MS * 60;
107+
const DAY_MS = HOUR_MS * 24;
108+
109+
it.each<[string, string, number]>([
110+
['3 days, 6 hours, 48 minutes, 59 seconds', '3d 6h 48m 59s', 3 * DAY_MS + 6 * HOUR_MS + 48 * MINUTE_MS + 59000],
111+
['6 hours, 48 minutes, 59 seconds', '6h 48m 59s', 6 * HOUR_MS + 48 * MINUTE_MS + 59000],
112+
['48 minutes, 59 seconds', '48m 59s', 48 * MINUTE_MS + 59000],
113+
['59 seconds', '59s', 59000],
114+
['0 seconds', '0s', 0],
115+
])('%s formats to %s', (_description, expectedResult, input) => {
116+
expect(formatPreciseDuration(input)).toEqual(expectedResult);
117+
});
118+
});
119+
103120
describe("formatFullDateNoDayISO", () => {
104121
it("should return ISO format", () => {
105122
expect(formatFullDateNoDayISO(REPEATABLE_DATE)).toEqual("2022-11-17T16:58:32.517Z");
@@ -108,7 +125,6 @@ describe("formatFullDateNoDayISO", () => {
108125

109126
describe("formatTimeLeft", () => {
110127
it.each([
111-
[null, "0s left"],
112128
[0, "0s left"],
113129
[23, "23s left"],
114130
[60 + 23, "1m 23s left"],

0 commit comments

Comments
 (0)