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

Show day counts in call durations #9641

Merged
merged 5 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions src/DateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,6 @@ export function formatTime(date: Date, showTwelveHour = false): string {
return pad(date.getHours()) + ':' + pad(date.getMinutes());
}

export function formatCallTime(delta: Date): string {
const hours = delta.getUTCHours();
const minutes = delta.getUTCMinutes();
const seconds = delta.getUTCSeconds();

let output = "";
if (hours) output += `${hours}h `;
if (minutes || output) output += `${minutes}m `;
if (seconds || output) output += `${seconds}s`;

return output;
}

export function formatSeconds(inSeconds: number): string {
const hours = Math.floor(inSeconds / (60 * 60)).toFixed(0).padStart(2, '0');
const minutes = Math.floor((inSeconds % (60 * 60)) / 60).toFixed(0).padStart(2, '0');
Expand Down Expand Up @@ -238,15 +225,16 @@ export function formatRelativeTime(date: Date, showTwelveHour = false): string {
}
}

const MINUTE_MS = 60000;
const HOUR_MS = MINUTE_MS * 60;
const DAY_MS = HOUR_MS * 24;

/**
* Formats duration in ms to human readable string
* Returns value in biggest possible unit (day, hour, min, second)
* Rounds values up until unit threshold
* ie. 23:13:57 -> 23h, 24:13:57 -> 1d, 44:56:56 -> 2d
*/
const MINUTE_MS = 60000;
const HOUR_MS = MINUTE_MS * 60;
const DAY_MS = HOUR_MS * 24;
export function formatDuration(durationMs: number): string {
if (durationMs >= DAY_MS) {
return _t('%(value)sd', { value: Math.round(durationMs / DAY_MS) });
Expand All @@ -259,3 +247,26 @@ export function formatDuration(durationMs: number): string {
}
return _t('%(value)ss', { value: Math.round(durationMs / 1000) });
}

/**
* Formats duration in ms to human readable string
* Returns precise value down to the nearest second
* ie. 23:13:57 -> 23h 13m 57s, 44:56:56 -> 1d 20h 56m 56s
*/
export function formatPreciseDuration(durationMs: number): string {
const days = Math.floor(durationMs / DAY_MS);
const hours = Math.floor((durationMs % DAY_MS) / HOUR_MS);
const minutes = Math.floor((durationMs % HOUR_MS) / MINUTE_MS);
const seconds = Math.floor((durationMs % MINUTE_MS) / 1000);

if (days > 0) {
return _t('%(days)sd %(hours)sh %(minutes)sm %(seconds)ss', { days, hours, minutes, seconds });
}
if (hours > 0) {
return _t('%(hours)sh %(minutes)sm %(seconds)ss', { hours, minutes, seconds });
}
if (minutes > 0) {
return _t('%(minutes)sm %(seconds)ss', { minutes, seconds });
}
return _t('%(value)ss', { value: seconds });
}
4 changes: 2 additions & 2 deletions src/components/structures/LegacyCallEventGrouper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ export default class LegacyCallEventGrouper extends EventEmitter {
return Boolean(this.reject);
}

public get duration(): Date {
public get duration(): number {
if (!this.hangup || !this.selectAnswer) return;
return new Date(this.hangup.getDate().getTime() - this.selectAnswer.getDate().getTime());
return this.hangup.getDate().getTime() - this.selectAnswer.getDate().getTime();
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/messages/LegacyCallEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import LegacyCallEventGrouper, {
import AccessibleButton from '../elements/AccessibleButton';
import InfoTooltip, { InfoTooltipKind } from '../elements/InfoTooltip';
import AccessibleTooltipButton from '../elements/AccessibleTooltipButton';
import { formatCallTime } from "../../../DateUtils";
import { formatPreciseDuration } from "../../../DateUtils";
import Clock from "../audio_messages/Clock";

const MAX_NON_NARROW_WIDTH = 450 / 70 * 100;
Expand Down Expand Up @@ -175,7 +175,7 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
const duration = this.props.callEventGrouper.duration;
let text = _t("Call ended");
if (duration) {
text += " • " + formatCallTime(duration);
text += " • " + formatPreciseDuration(duration);
}
return (
<div className="mx_LegacyCallEvent_content">
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/voip/CallDuration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
import React, { FC, useState, useEffect, memo } from "react";
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";

import { formatCallTime } from "../../../DateUtils";
import { formatPreciseDuration } from "../../../DateUtils";

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

interface GroupCallDurationProps {
Expand Down
4 changes: 4 additions & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
"%(value)sh": "%(value)sh",
"%(value)sm": "%(value)sm",
"%(value)ss": "%(value)ss",
"%(days)sd %(hours)sh %(minutes)sm %(seconds)ss": "%(days)sd %(hours)sh %(minutes)sm %(seconds)ss",
"%(hours)sh %(minutes)sm %(seconds)ss": "%(hours)sh %(minutes)sm %(seconds)ss",
"%(minutes)sm %(seconds)ss": "%(minutes)sm %(seconds)ss",
"%(seconds)ss": "%(seconds)ss",
"Identity server has no terms of service": "Identity server has no 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.": "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.",
"Only continue if you trust the owner of the server.": "Only continue if you trust the owner of the server.",
Expand Down
17 changes: 17 additions & 0 deletions test/utils/DateUtils-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
formatDuration,
formatFullDateNoDayISO,
formatTimeLeft,
formatPreciseDuration,
} from "../../src/DateUtils";
import { REPEATABLE_DATE } from "../test-utils";

Expand Down Expand Up @@ -100,6 +101,22 @@ describe('formatDuration()', () => {
});
});

describe("formatPreciseDuration", () => {
const MINUTE_MS = 1000 * 60;
const HOUR_MS = MINUTE_MS * 60;
const DAY_MS = HOUR_MS * 24;

it.each<[string, string, number]>([
['3 days, 6 hours, 48 minutes, 59 seconds', '3d 6h 48m 59s', 3 * DAY_MS + 6 * HOUR_MS + 48 * MINUTE_MS + 59000],
['6 hours, 48 minutes, 59 seconds', '6h 48m 59s', 6 * HOUR_MS + 48 * MINUTE_MS + 59000],
['48 minutes, 59 seconds', '48m 59s', 48 * MINUTE_MS + 59000],
['59 seconds', '59s', 59000],
['0 seconds', '0s', 0],
])('%s formats to %s', (_description, expectedResult, input) => {
expect(formatPreciseDuration(input)).toEqual(expectedResult);
});
});

describe("formatFullDateNoDayISO", () => {
it("should return ISO format", () => {
expect(formatFullDateNoDayISO(REPEATABLE_DATE)).toEqual("2022-11-17T16:58:32.517Z");
Expand Down