Display relative time in thread panel event tile (#7068)
* Null-guard for missing root event in thread panel * Add formatRelativeTime date utility * Display relative time format in thread panel event tiles
This commit is contained in:
parent
801eb068d6
commit
2a20d9a7df
4 changed files with 76 additions and 5 deletions
|
@ -149,12 +149,20 @@ export function formatSeconds(inSeconds: number): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
const MILLIS_IN_DAY = 86400000;
|
const MILLIS_IN_DAY = 86400000;
|
||||||
|
function withinPast24Hours(prevDate: Date, nextDate: Date): boolean {
|
||||||
|
return Math.abs(prevDate.getTime() - nextDate.getTime()) <= MILLIS_IN_DAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function withinCurrentYear(prevDate: Date, nextDate: Date): boolean {
|
||||||
|
return prevDate.getFullYear() === nextDate.getFullYear();
|
||||||
|
}
|
||||||
|
|
||||||
export function wantsDateSeparator(prevEventDate: Date, nextEventDate: Date): boolean {
|
export function wantsDateSeparator(prevEventDate: Date, nextEventDate: Date): boolean {
|
||||||
if (!nextEventDate || !prevEventDate) {
|
if (!nextEventDate || !prevEventDate) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Return early for events that are > 24h apart
|
// Return early for events that are > 24h apart
|
||||||
if (Math.abs(prevEventDate.getTime() - nextEventDate.getTime()) > MILLIS_IN_DAY) {
|
if (!withinPast24Hours(prevEventDate, nextEventDate)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,3 +186,18 @@ export function formatFullDateNoDayNoTime(date: Date) {
|
||||||
pad(date.getDate())
|
pad(date.getDate())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatRelativeTime(date: Date, showTwelveHour = false): string {
|
||||||
|
const now = new Date(Date.now());
|
||||||
|
if (withinPast24Hours(date, now)) {
|
||||||
|
return formatTime(date, showTwelveHour);
|
||||||
|
} else {
|
||||||
|
const months = getMonthsArray();
|
||||||
|
let relativeDate = `${months[date.getMonth()]} ${date.getDate()}`;
|
||||||
|
|
||||||
|
if (!withinCurrentYear(date, now)) {
|
||||||
|
relativeDate += `, ${date.getFullYear()}`;
|
||||||
|
}
|
||||||
|
return relativeDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { formatFullDate, formatTime, formatFullTime } from '../../../DateUtils';
|
import { formatFullDate, formatTime, formatFullTime, formatRelativeTime } from '../../../DateUtils';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
@ -24,6 +24,7 @@ interface IProps {
|
||||||
showTwelveHour?: boolean;
|
showTwelveHour?: boolean;
|
||||||
showFullDate?: boolean;
|
showFullDate?: boolean;
|
||||||
showSeconds?: boolean;
|
showSeconds?: boolean;
|
||||||
|
showRelative?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.messages.MessageTimestamp")
|
@replaceableComponent("views.messages.MessageTimestamp")
|
||||||
|
@ -31,7 +32,9 @@ export default class MessageTimestamp extends React.Component<IProps> {
|
||||||
public render() {
|
public render() {
|
||||||
const date = new Date(this.props.ts);
|
const date = new Date(this.props.ts);
|
||||||
let timestamp;
|
let timestamp;
|
||||||
if (this.props.showFullDate) {
|
if (this.props.showRelative) {
|
||||||
|
timestamp = formatRelativeTime(date, this.props.showTwelveHour);
|
||||||
|
} else if (this.props.showFullDate) {
|
||||||
timestamp = formatFullDate(date, this.props.showTwelveHour, this.props.showSeconds);
|
timestamp = formatFullDate(date, this.props.showTwelveHour, this.props.showSeconds);
|
||||||
} else if (this.props.showSeconds) {
|
} else if (this.props.showSeconds) {
|
||||||
timestamp = formatFullTime(date, this.props.showTwelveHour);
|
timestamp = formatFullTime(date, this.props.showTwelveHour);
|
||||||
|
|
|
@ -1132,7 +1132,11 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
|| this.state.actionBarFocused);
|
|| this.state.actionBarFocused);
|
||||||
|
|
||||||
const timestamp = showTimestamp ?
|
const timestamp = showTimestamp ?
|
||||||
<MessageTimestamp showTwelveHour={this.props.isTwelveHour} ts={this.props.mxEvent.getTs()} /> : null;
|
<MessageTimestamp
|
||||||
|
showRelative={this.props.tileShape === TileShape.ThreadPanel}
|
||||||
|
showTwelveHour={this.props.isTwelveHour}
|
||||||
|
ts={this.props.mxEvent.getTs()}
|
||||||
|
/> : null;
|
||||||
|
|
||||||
const keyRequestHelpText =
|
const keyRequestHelpText =
|
||||||
<div className="mx_EventTile_keyRequestInfo_tooltip_contents">
|
<div className="mx_EventTile_keyRequestInfo_tooltip_contents">
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { formatSeconds } from "../../src/DateUtils";
|
import { formatSeconds, formatRelativeTime } from "../../src/DateUtils";
|
||||||
|
|
||||||
describe("formatSeconds", () => {
|
describe("formatSeconds", () => {
|
||||||
it("correctly formats time with hours", () => {
|
it("correctly formats time with hours", () => {
|
||||||
|
@ -29,3 +29,44 @@ describe("formatSeconds", () => {
|
||||||
expect(formatSeconds((60 * 60 * 0) + (60 * 31) + (0))).toBe("31:00");
|
expect(formatSeconds((60 * 60 * 0) + (60 * 31) + (0))).toBe("31:00");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("formatRelativeTime", () => {
|
||||||
|
let dateSpy;
|
||||||
|
beforeAll(() => {
|
||||||
|
dateSpy = jest
|
||||||
|
.spyOn(global.Date, 'now')
|
||||||
|
// Tuesday, 2 November 2021 11:18:03
|
||||||
|
.mockImplementation(() => 1635851883000);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
dateSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns hour format for events created less than 24 hours ago", () => {
|
||||||
|
const date = new Date(1635850883000);
|
||||||
|
expect(formatRelativeTime(date)).toBe("11:01");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("honours the hour format setting", () => {
|
||||||
|
const date = new Date(1635850883000);
|
||||||
|
expect(formatRelativeTime(date)).toBe("11:01");
|
||||||
|
expect(formatRelativeTime(date, false)).toBe("11:01");
|
||||||
|
expect(formatRelativeTime(date, true)).toBe("11:01AM");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns month and day for events created in the current year", () => {
|
||||||
|
const date = new Date(1632567741000);
|
||||||
|
expect(formatRelativeTime(date, true)).toBe("Sep 25");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not return a leading 0 for single digit days", () => {
|
||||||
|
const date = new Date(1635764541000);
|
||||||
|
expect(formatRelativeTime(date, true)).toBe("Nov 1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("appends the year for events created in previous years", () => {
|
||||||
|
const date = new Date(1604142141000);
|
||||||
|
expect(formatRelativeTime(date, true)).toBe("Oct 31, 2020");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue