Show io.element.late_event
in MessageTimestamp when known (#11733)
* Use Compound tooltips on MessageTimestamp to improve UX of date time discovery Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Show io.element.late_event in MessageTimestamp when known Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Avoid needing new Compound changes Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update compound-web Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update identifiers Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix types Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Switch to snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
a267b86fef
commit
fe3ad78a02
5 changed files with 108 additions and 27 deletions
|
@ -19,9 +19,14 @@ import React from "react";
|
||||||
import { Tooltip } from "@vector-im/compound-web";
|
import { Tooltip } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import { formatFullDate, formatTime, formatFullTime, formatRelativeTime } from "../../../DateUtils";
|
import { formatFullDate, formatTime, formatFullTime, formatRelativeTime } from "../../../DateUtils";
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
ts: number;
|
ts: number;
|
||||||
|
/**
|
||||||
|
* If specified will render both the sent-at and received-at timestamps in the tooltip
|
||||||
|
*/
|
||||||
|
receivedTs?: number;
|
||||||
showTwelveHour?: boolean;
|
showTwelveHour?: boolean;
|
||||||
showFullDate?: boolean;
|
showFullDate?: boolean;
|
||||||
showSeconds?: boolean;
|
showSeconds?: boolean;
|
||||||
|
@ -42,8 +47,18 @@ export default class MessageTimestamp extends React.Component<IProps> {
|
||||||
timestamp = formatTime(date, this.props.showTwelveHour);
|
timestamp = formatTime(date, this.props.showTwelveHour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let label = formatFullDate(date, this.props.showTwelveHour);
|
||||||
|
let caption: string | undefined;
|
||||||
|
if (this.props.receivedTs !== undefined) {
|
||||||
|
label = _t("timeline|message_timestamp_sent_at", { dateTime: label });
|
||||||
|
const receivedDate = new Date(this.props.receivedTs);
|
||||||
|
caption = _t("timeline|message_timestamp_received_at", {
|
||||||
|
dateTime: formatFullDate(receivedDate, this.props.showTwelveHour),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip label={formatFullDate(date, this.props.showTwelveHour)}>
|
<Tooltip label={label} caption={caption}>
|
||||||
<span className="mx_MessageTimestamp" aria-hidden={true} aria-live="off">
|
<span className="mx_MessageTimestamp" aria-hidden={true} aria-live="off">
|
||||||
{timestamp}
|
{timestamp}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1126,6 +1126,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||||
showRelative={this.context.timelineRenderingType === TimelineRenderingType.ThreadsList}
|
showRelative={this.context.timelineRenderingType === TimelineRenderingType.ThreadsList}
|
||||||
showTwelveHour={this.props.isTwelveHour}
|
showTwelveHour={this.props.isTwelveHour}
|
||||||
ts={ts}
|
ts={ts}
|
||||||
|
receivedTs={this.props.mxEvent.getUnsigned()["io.element.late_event"]?.received_at}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -3417,6 +3417,8 @@
|
||||||
"label": "Message Actions",
|
"label": "Message Actions",
|
||||||
"view_in_room": "View in room"
|
"view_in_room": "View in room"
|
||||||
},
|
},
|
||||||
|
"message_timestamp_received_at": "Recovered at: %(dateTime)s",
|
||||||
|
"message_timestamp_sent_at": "Sent at: %(dateTime)s",
|
||||||
"mjolnir": {
|
"mjolnir": {
|
||||||
"changed_rule_glob": "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s",
|
"changed_rule_glob": "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s",
|
||||||
"changed_rule_rooms": "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s",
|
"changed_rule_rooms": "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s",
|
||||||
|
|
63
test/components/views/messages/MessageTimestamp-test.tsx
Normal file
63
test/components/views/messages/MessageTimestamp-test.tsx
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
|
import MessageTimestamp from "../../../../src/components/views/messages/MessageTimestamp";
|
||||||
|
|
||||||
|
jest.mock("../../../../src/settings/SettingsStore");
|
||||||
|
|
||||||
|
describe("MessageTimestamp", () => {
|
||||||
|
// Friday Dec 17 2021, 9:09am
|
||||||
|
const nowDate = new Date("2021-12-17T08:09:00.000Z");
|
||||||
|
|
||||||
|
const HOUR_MS = 3600000;
|
||||||
|
const DAY_MS = HOUR_MS * 24;
|
||||||
|
|
||||||
|
it("should render HH:MM", () => {
|
||||||
|
const { asFragment } = render(<MessageTimestamp ts={nowDate.getTime()} />);
|
||||||
|
expect(asFragment()).toMatchInlineSnapshot(`
|
||||||
|
<DocumentFragment>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
aria-live="off"
|
||||||
|
class="mx_MessageTimestamp"
|
||||||
|
data-state="closed"
|
||||||
|
>
|
||||||
|
08:09
|
||||||
|
</span>
|
||||||
|
</DocumentFragment>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show full date & time on hover", async () => {
|
||||||
|
const { container } = render(<MessageTimestamp ts={nowDate.getTime()} />);
|
||||||
|
await userEvent.hover(container.querySelector(".mx_MessageTimestamp")!);
|
||||||
|
expect((await screen.findByRole("tooltip")).textContent).toMatchInlineSnapshot(`"Fri, Dec 17, 2021, 08:09:00"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show sent & received time on hover if passed", async () => {
|
||||||
|
const { container } = render(
|
||||||
|
<MessageTimestamp ts={nowDate.getTime()} receivedTs={nowDate.getTime() + DAY_MS} />,
|
||||||
|
);
|
||||||
|
await userEvent.hover(container.querySelector(".mx_MessageTimestamp")!);
|
||||||
|
expect((await screen.findByRole("tooltip")).textContent).toMatchInlineSnapshot(
|
||||||
|
`"Sent at: Fri, Dec 17, 2021, 08:09:00Recovered at: Sat, Dec 18, 2021, 08:09:00"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
52
yarn.lock
52
yarn.lock
|
@ -1286,14 +1286,14 @@
|
||||||
core-js-pure "^3.30.2"
|
core-js-pure "^3.30.2"
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.9", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.9", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
||||||
version "7.22.15"
|
version "7.22.15"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8"
|
||||||
integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==
|
integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
"@babel/runtime@^7.10.2":
|
"@babel/runtime@^7.10.2", "@babel/runtime@^7.13.10":
|
||||||
version "7.23.2"
|
version "7.23.2"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
|
||||||
integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
|
integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
|
||||||
|
@ -1543,9 +1543,9 @@
|
||||||
"@floating-ui/dom" "^1.5.1"
|
"@floating-ui/dom" "^1.5.1"
|
||||||
|
|
||||||
"@floating-ui/utils@^0.1.3":
|
"@floating-ui/utils@^0.1.3":
|
||||||
version "0.1.4"
|
version "0.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.4.tgz#19654d1026cc410975d46445180e70a5089b3e7d"
|
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9"
|
||||||
integrity sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==
|
integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==
|
||||||
|
|
||||||
"@humanwhocodes/config-array@^0.11.10":
|
"@humanwhocodes/config-array@^0.11.10":
|
||||||
version "0.11.10"
|
version "0.11.10"
|
||||||
|
@ -2301,10 +2301,10 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
"@radix-ui/react-dismissable-layer@1.0.4":
|
"@radix-ui/react-dismissable-layer@1.0.5":
|
||||||
version "1.0.4"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz#883a48f5f938fa679427aa17fcba70c5494c6978"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz#3f98425b82b9068dfbab5db5fff3df6ebf48b9d4"
|
||||||
integrity sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==
|
integrity sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/primitive" "1.0.1"
|
"@radix-ui/primitive" "1.0.1"
|
||||||
|
@ -2342,10 +2342,10 @@
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-primitive" "1.0.3"
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
|
||||||
"@radix-ui/react-popper@1.1.2":
|
"@radix-ui/react-popper@1.1.3":
|
||||||
version "1.1.2"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.2.tgz#4c0b96fcd188dc1f334e02dba2d538973ad842e9"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.3.tgz#24c03f527e7ac348fabf18c89795d85d21b00b42"
|
||||||
integrity sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==
|
integrity sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@floating-ui/react-dom" "^2.0.0"
|
"@floating-ui/react-dom" "^2.0.0"
|
||||||
|
@ -2359,10 +2359,10 @@
|
||||||
"@radix-ui/react-use-size" "1.0.1"
|
"@radix-ui/react-use-size" "1.0.1"
|
||||||
"@radix-ui/rect" "1.0.1"
|
"@radix-ui/rect" "1.0.1"
|
||||||
|
|
||||||
"@radix-ui/react-portal@1.0.3":
|
"@radix-ui/react-portal@1.0.4":
|
||||||
version "1.0.3"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.3.tgz#ffb961244c8ed1b46f039e6c215a6c4d9989bda1"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.4.tgz#df4bfd353db3b1e84e639e9c63a5f2565fb00e15"
|
||||||
integrity sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==
|
integrity sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-primitive" "1.0.3"
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
@ -2393,18 +2393,18 @@
|
||||||
"@radix-ui/react-compose-refs" "1.0.1"
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
|
||||||
"@radix-ui/react-tooltip@^1.0.6":
|
"@radix-ui/react-tooltip@^1.0.6":
|
||||||
version "1.0.6"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.6.tgz#87a7786cd9f2b4de957ac645afae1575339c58b0"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e"
|
||||||
integrity sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg==
|
integrity sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/primitive" "1.0.1"
|
"@radix-ui/primitive" "1.0.1"
|
||||||
"@radix-ui/react-compose-refs" "1.0.1"
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
"@radix-ui/react-context" "1.0.1"
|
"@radix-ui/react-context" "1.0.1"
|
||||||
"@radix-ui/react-dismissable-layer" "1.0.4"
|
"@radix-ui/react-dismissable-layer" "1.0.5"
|
||||||
"@radix-ui/react-id" "1.0.1"
|
"@radix-ui/react-id" "1.0.1"
|
||||||
"@radix-ui/react-popper" "1.1.2"
|
"@radix-ui/react-popper" "1.1.3"
|
||||||
"@radix-ui/react-portal" "1.0.3"
|
"@radix-ui/react-portal" "1.0.4"
|
||||||
"@radix-ui/react-presence" "1.0.1"
|
"@radix-ui/react-presence" "1.0.1"
|
||||||
"@radix-ui/react-primitive" "1.0.3"
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
"@radix-ui/react-slot" "1.0.2"
|
"@radix-ui/react-slot" "1.0.2"
|
||||||
|
@ -3136,9 +3136,9 @@
|
||||||
svg2vectordrawable "^2.9.1"
|
svg2vectordrawable "^2.9.1"
|
||||||
|
|
||||||
"@vector-im/compound-web@^0.5.0":
|
"@vector-im/compound-web@^0.5.0":
|
||||||
version "0.5.3"
|
version "0.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-0.5.3.tgz#5d0e22c6cf277a605151099d3850ddc74290078c"
|
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-0.5.4.tgz#a99b346fe8de34a6f8bcbf9e1040ce1d79615dbc"
|
||||||
integrity sha512-h8xIrMHTQklan2W7ImoFUnvMvcNcNaromcS0QffkttwLZmOjX2EXmPhlq5JhTm+ZRr4+WN7Hjok1F1vF08RStA==
|
integrity sha512-w4Nwzid5Y89Dt9GaxKO+kWPTjSitLpkmoAjMYHVUajNMCfUxluzu4eOgjPRCpubPH5lZUB6/95y43wOI+pEC1Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@radix-ui/react-form" "^0.0.3"
|
"@radix-ui/react-form" "^0.0.3"
|
||||||
"@radix-ui/react-tooltip" "^1.0.6"
|
"@radix-ui/react-tooltip" "^1.0.6"
|
||||||
|
|
Loading…
Reference in a new issue