diff --git a/res/css/views/messages/_CallEvent.scss b/res/css/views/messages/_CallEvent.scss index 7934f8f3c2..2587e73e50 100644 --- a/res/css/views/messages/_CallEvent.scss +++ b/res/css/views/messages/_CallEvent.scss @@ -183,6 +183,10 @@ limitations under the License. } } + .mx_MessageTimestamp { + margin-left: 16px; + } + &.mx_CallEvent_narrow { height: unset; width: 290px; diff --git a/res/css/views/messages/_EventTileBubble.scss b/res/css/views/messages/_EventTileBubble.scss index e0f5d521cb..ff88015e9c 100644 --- a/res/css/views/messages/_EventTileBubble.scss +++ b/res/css/views/messages/_EventTileBubble.scss @@ -22,7 +22,7 @@ limitations under the License. max-width: 75%; box-sizing: border-box; display: grid; - grid-template-columns: 24px minmax(0, 1fr) min-content; + grid-template-columns: 24px minmax(0, 1fr) min-content min-content; &::before, &::after { position: relative; @@ -57,4 +57,11 @@ limitations under the License. grid-column: 2; grid-row: 2; } + + .mx_MessageTimestamp { + grid-column: 4; + grid-row: 1 / 3; + align-self: center; + margin-left: 16px; + } } diff --git a/res/css/views/rooms/_EventBubbleTile.scss b/res/css/views/rooms/_EventBubbleTile.scss index 04ab166149..d0cb8d2450 100644 --- a/res/css/views/rooms/_EventBubbleTile.scss +++ b/res/css/views/rooms/_EventBubbleTile.scss @@ -450,6 +450,14 @@ limitations under the License. } } +.mx_EventTile.mx_EventTile_bubbleContainer[data-layout=bubble], +.mx_EventTile.mx_EventTile_leftAlignedBubble[data-layout=bubble] { + .mx_EventTile_line > a { + // hide this timestamp as the tile will render its own + display: none; + } +} + .mx_EventTile.mx_EventTile_bubbleContainer[data-layout=bubble], .mx_EventTile.mx_EventTile_leftAlignedBubble[data-layout=bubble], .mx_EventTile.mx_EventTile_info[data-layout=bubble], diff --git a/src/components/views/messages/CallEvent.tsx b/src/components/views/messages/CallEvent.tsx index d6df8c354e..64594d12d1 100644 --- a/src/components/views/messages/CallEvent.tsx +++ b/src/components/views/messages/CallEvent.tsx @@ -33,6 +33,7 @@ const MAX_NON_NARROW_WIDTH = 450 / 70 * 100; interface IProps { mxEvent: MatrixEvent; callEventGrouper: CallEventGrouper; + timestamp?: JSX.Element; } interface IState { @@ -145,6 +146,7 @@ export default class CallEvent extends React.PureComponent { > { _t("Accept") } + { this.props.timestamp } ); } @@ -157,6 +159,7 @@ export default class CallEvent extends React.PureComponent {
{ _t("Call declined") } { this.renderCallBackButton(_t("Call back")) } + { this.props.timestamp }
); } else if (([CallErrorCode.UserHangup, "user hangup"].includes(hangupReason) || !hangupReason)) { @@ -174,6 +177,7 @@ export default class CallEvent extends React.PureComponent { return (
{ text } + { this.props.timestamp }
); } else if (hangupReason === CallErrorCode.InviteTimeout) { @@ -181,6 +185,7 @@ export default class CallEvent extends React.PureComponent {
{ _t("No answer") } { this.renderCallBackButton(_t("Call back")) } + { this.props.timestamp }
); } @@ -215,6 +220,7 @@ export default class CallEvent extends React.PureComponent { /> { _t("Connection failed") } { this.renderCallBackButton(_t("Retry")) } + { this.props.timestamp } ); } @@ -222,6 +228,7 @@ export default class CallEvent extends React.PureComponent { return (
+ { this.props.timestamp }
); } @@ -229,6 +236,7 @@ export default class CallEvent extends React.PureComponent { return (
{ _t("Connecting") } + { this.props.timestamp }
); } @@ -237,6 +245,7 @@ export default class CallEvent extends React.PureComponent {
{ _t("Missed call") } { this.renderCallBackButton(_t("Call back")) } + { this.props.timestamp }
); } @@ -244,6 +253,7 @@ export default class CallEvent extends React.PureComponent { return (
{ _t("The call is in an unknown state!") } + { this.props.timestamp }
); } diff --git a/src/components/views/messages/EncryptionEvent.tsx b/src/components/views/messages/EncryptionEvent.tsx index 8f352610e0..809cf75f76 100644 --- a/src/components/views/messages/EncryptionEvent.tsx +++ b/src/components/views/messages/EncryptionEvent.tsx @@ -27,11 +27,12 @@ import { objectHasDiff } from "../../../utils/objects"; interface IProps { mxEvent: MatrixEvent; + timestamp?: JSX.Element; } const ALGORITHM = "m.megolm.v1.aes-sha2"; -const EncryptionEvent = forwardRef(({ mxEvent }, ref) => { +const EncryptionEvent = forwardRef(({ mxEvent, timestamp }, ref) => { const cli = useContext(MatrixClientContext); const roomId = mxEvent.getRoomId(); const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId); @@ -60,6 +61,7 @@ const EncryptionEvent = forwardRef(({ mxEvent }, ref) => className="mx_cryptoEvent mx_cryptoEvent_icon" title={_t("Encryption enabled")} subtitle={subtitle} + timestamp={timestamp} />; } @@ -68,6 +70,7 @@ const EncryptionEvent = forwardRef(({ mxEvent }, ref) => className="mx_cryptoEvent mx_cryptoEvent_icon" title={_t("Encryption enabled")} subtitle={_t("Ignored attempt to disable encryption")} + timestamp={timestamp} />; } @@ -76,6 +79,7 @@ const EncryptionEvent = forwardRef(({ mxEvent }, ref) => title={_t("Encryption not enabled")} subtitle={_t("The encryption used by this room isn't supported.")} ref={ref} + timestamp={timestamp} />; }); diff --git a/src/components/views/messages/EventTileBubble.tsx b/src/components/views/messages/EventTileBubble.tsx index 9fa3586ea5..c4e8843c89 100644 --- a/src/components/views/messages/EventTileBubble.tsx +++ b/src/components/views/messages/EventTileBubble.tsx @@ -20,15 +20,23 @@ import classNames from "classnames"; interface IProps { className: string; title: string; + timestamp?: JSX.Element; subtitle?: ReactNode; children?: ReactChildren; } -const EventTileBubble = forwardRef(({ className, title, subtitle, children }, ref) => { +const EventTileBubble = forwardRef(({ + className, + title, + timestamp, + subtitle, + children, +}, ref) => { return
{ title }
{ subtitle &&
{ subtitle }
} { children } + { timestamp }
; }); diff --git a/src/components/views/messages/MJitsiWidgetEvent.tsx b/src/components/views/messages/MJitsiWidgetEvent.tsx index 5b7134c216..03448bf0d2 100644 --- a/src/components/views/messages/MJitsiWidgetEvent.tsx +++ b/src/components/views/messages/MJitsiWidgetEvent.tsx @@ -26,6 +26,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent"; interface IProps { mxEvent: MatrixEvent; + timestamp?: JSX.Element; } @replaceableComponent("views.messages.MJitsiWidgetEvent") @@ -54,6 +55,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent { return ; } else if (prevUrl) { // modified @@ -61,6 +63,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent { className="mx_MJitsiWidgetEvent" title={_t('Video conference updated by %(senderName)s', { senderName })} subtitle={joinCopy} + timestamp={this.props.timestamp} />; } else { // assume added @@ -68,6 +71,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent { className="mx_MJitsiWidgetEvent" title={_t("Video conference started by %(senderName)s", { senderName })} subtitle={joinCopy} + timestamp={this.props.timestamp} />; } } diff --git a/src/components/views/messages/MKeyVerificationConclusion.tsx b/src/components/views/messages/MKeyVerificationConclusion.tsx index a73c0f0c90..1a1d0ccc13 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.tsx +++ b/src/components/views/messages/MKeyVerificationConclusion.tsx @@ -29,6 +29,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent"; interface IProps { /* the MatrixEvent to show */ mxEvent: MatrixEvent; + timestamp?: JSX.Element; } @replaceableComponent("views.messages.MKeyVerificationConclusion") @@ -133,6 +134,7 @@ export default class MKeyVerificationConclusion extends React.Component className={classes} title={title} subtitle={userLabelForEventRoom(request.otherUserId, mxEvent.getRoomId())} + timestamp={this.props.timestamp} />; } diff --git a/src/components/views/messages/MKeyVerificationRequest.tsx b/src/components/views/messages/MKeyVerificationRequest.tsx index 0243ea0200..5dccfeec72 100644 --- a/src/components/views/messages/MKeyVerificationRequest.tsx +++ b/src/components/views/messages/MKeyVerificationRequest.tsx @@ -30,6 +30,7 @@ import RightPanelStore from '../../../stores/right-panel/RightPanelStore'; interface IProps { mxEvent: MatrixEvent; + timestamp?: JSX.Element; } @replaceableComponent("views.messages.MKeyVerificationRequest") @@ -168,6 +169,7 @@ export default class MKeyVerificationRequest extends React.Component { className="mx_cryptoEvent mx_cryptoEvent_icon" title={title} subtitle={subtitle} + timestamp={this.props.timestamp} > { stateNode } ; diff --git a/src/components/views/messages/RoomCreate.tsx b/src/components/views/messages/RoomCreate.tsx index 455c1bcc71..5be349fbce 100644 --- a/src/components/views/messages/RoomCreate.tsx +++ b/src/components/views/messages/RoomCreate.tsx @@ -29,6 +29,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent"; interface IProps { /* the MatrixEvent to show */ mxEvent: MatrixEvent; + timestamp?: JSX.Element; } @replaceableComponent("views.messages.RoomCreate") @@ -65,6 +66,7 @@ export default class RoomCreate extends React.Component { className="mx_CreateEvent" title={_t("This room is a continuation of another conversation.")} subtitle={link} + timestamp={this.props.timestamp} />; } } diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 9275558d21..e0a907f488 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -1273,12 +1273,13 @@ export default class EventTile extends React.Component { ? this.props.mxEvent.getTs() : thread?.lastReply().getTs(); - const timestamp = showTimestamp && ts ? - : null; + const messageTimestamp = ; + + const timestamp = showTimestamp && ts ? messageTimestamp : null; const keyRequestHelpText =
@@ -1339,9 +1340,10 @@ export default class EventTile extends React.Component { { timestamp } ; - const useIRCLayout = this.props.layout == Layout.IRC; + const useIRCLayout = this.props.layout === Layout.IRC; const groupTimestamp = !useIRCLayout ? linkedTimestamp : null; const ircTimestamp = useIRCLayout ? linkedTimestamp : null; + const bubbleTimestamp = this.props.layout === Layout.Bubble ? messageTimestamp : null; const groupPadlock = !useIRCLayout && !isBubbleMessage && this.renderE2EPadlock(); const ircPadlock = useIRCLayout && !isBubbleMessage && this.renderE2EPadlock(); @@ -1567,7 +1569,8 @@ export default class EventTile extends React.Component { { groupTimestamp } { groupPadlock } { replyChain } - { callEventGrouper={this.props.callEventGrouper} getRelationsForEvent={this.props.getRelationsForEvent} isSeeingThroughMessageHiddenForModeration={isSeeingThroughMessageHiddenForModeration} + timestamp={bubbleTimestamp} /> { keyRequestInfo } { actionBar }