Show bubble tile timestamps for bubble layout inside the bubble (#7622)

This commit is contained in:
Michael Telatynski 2022-01-25 13:10:17 +00:00 committed by GitHub
parent 8ddd677c35
commit fb49ccce35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 66 additions and 11 deletions

View file

@ -183,6 +183,10 @@ limitations under the License.
} }
} }
.mx_MessageTimestamp {
margin-left: 16px;
}
&.mx_CallEvent_narrow { &.mx_CallEvent_narrow {
height: unset; height: unset;
width: 290px; width: 290px;

View file

@ -22,7 +22,7 @@ limitations under the License.
max-width: 75%; max-width: 75%;
box-sizing: border-box; box-sizing: border-box;
display: grid; display: grid;
grid-template-columns: 24px minmax(0, 1fr) min-content; grid-template-columns: 24px minmax(0, 1fr) min-content min-content;
&::before, &::after { &::before, &::after {
position: relative; position: relative;
@ -57,4 +57,11 @@ limitations under the License.
grid-column: 2; grid-column: 2;
grid-row: 2; grid-row: 2;
} }
.mx_MessageTimestamp {
grid-column: 4;
grid-row: 1 / 3;
align-self: center;
margin-left: 16px;
}
} }

View file

@ -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_bubbleContainer[data-layout=bubble],
.mx_EventTile.mx_EventTile_leftAlignedBubble[data-layout=bubble], .mx_EventTile.mx_EventTile_leftAlignedBubble[data-layout=bubble],
.mx_EventTile.mx_EventTile_info[data-layout=bubble], .mx_EventTile.mx_EventTile_info[data-layout=bubble],

View file

@ -33,6 +33,7 @@ const MAX_NON_NARROW_WIDTH = 450 / 70 * 100;
interface IProps { interface IProps {
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
callEventGrouper: CallEventGrouper; callEventGrouper: CallEventGrouper;
timestamp?: JSX.Element;
} }
interface IState { interface IState {
@ -145,6 +146,7 @@ export default class CallEvent extends React.PureComponent<IProps, IState> {
> >
<span> { _t("Accept") } </span> <span> { _t("Accept") } </span>
</AccessibleButton> </AccessibleButton>
{ this.props.timestamp }
</div> </div>
); );
} }
@ -157,6 +159,7 @@ export default class CallEvent extends React.PureComponent<IProps, IState> {
<div className="mx_CallEvent_content"> <div className="mx_CallEvent_content">
{ _t("Call declined") } { _t("Call declined") }
{ this.renderCallBackButton(_t("Call back")) } { this.renderCallBackButton(_t("Call back")) }
{ this.props.timestamp }
</div> </div>
); );
} else if (([CallErrorCode.UserHangup, "user hangup"].includes(hangupReason) || !hangupReason)) { } else if (([CallErrorCode.UserHangup, "user hangup"].includes(hangupReason) || !hangupReason)) {
@ -174,6 +177,7 @@ export default class CallEvent extends React.PureComponent<IProps, IState> {
return ( return (
<div className="mx_CallEvent_content"> <div className="mx_CallEvent_content">
{ text } { text }
{ this.props.timestamp }
</div> </div>
); );
} else if (hangupReason === CallErrorCode.InviteTimeout) { } else if (hangupReason === CallErrorCode.InviteTimeout) {
@ -181,6 +185,7 @@ export default class CallEvent extends React.PureComponent<IProps, IState> {
<div className="mx_CallEvent_content"> <div className="mx_CallEvent_content">
{ _t("No answer") } { _t("No answer") }
{ this.renderCallBackButton(_t("Call back")) } { this.renderCallBackButton(_t("Call back")) }
{ this.props.timestamp }
</div> </div>
); );
} }
@ -215,6 +220,7 @@ export default class CallEvent extends React.PureComponent<IProps, IState> {
/> />
{ _t("Connection failed") } { _t("Connection failed") }
{ this.renderCallBackButton(_t("Retry")) } { this.renderCallBackButton(_t("Retry")) }
{ this.props.timestamp }
</div> </div>
); );
} }
@ -222,6 +228,7 @@ export default class CallEvent extends React.PureComponent<IProps, IState> {
return ( return (
<div className="mx_CallEvent_content"> <div className="mx_CallEvent_content">
<Clock seconds={this.state.length} /> <Clock seconds={this.state.length} />
{ this.props.timestamp }
</div> </div>
); );
} }
@ -229,6 +236,7 @@ export default class CallEvent extends React.PureComponent<IProps, IState> {
return ( return (
<div className="mx_CallEvent_content"> <div className="mx_CallEvent_content">
{ _t("Connecting") } { _t("Connecting") }
{ this.props.timestamp }
</div> </div>
); );
} }
@ -237,6 +245,7 @@ export default class CallEvent extends React.PureComponent<IProps, IState> {
<div className="mx_CallEvent_content"> <div className="mx_CallEvent_content">
{ _t("Missed call") } { _t("Missed call") }
{ this.renderCallBackButton(_t("Call back")) } { this.renderCallBackButton(_t("Call back")) }
{ this.props.timestamp }
</div> </div>
); );
} }
@ -244,6 +253,7 @@ export default class CallEvent extends React.PureComponent<IProps, IState> {
return ( return (
<div className="mx_CallEvent_content"> <div className="mx_CallEvent_content">
{ _t("The call is in an unknown state!") } { _t("The call is in an unknown state!") }
{ this.props.timestamp }
</div> </div>
); );
} }

View file

@ -27,11 +27,12 @@ import { objectHasDiff } from "../../../utils/objects";
interface IProps { interface IProps {
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
timestamp?: JSX.Element;
} }
const ALGORITHM = "m.megolm.v1.aes-sha2"; const ALGORITHM = "m.megolm.v1.aes-sha2";
const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent }, ref) => { const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent, timestamp }, ref) => {
const cli = useContext(MatrixClientContext); const cli = useContext(MatrixClientContext);
const roomId = mxEvent.getRoomId(); const roomId = mxEvent.getRoomId();
const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId); const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId);
@ -60,6 +61,7 @@ const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent }, ref) =>
className="mx_cryptoEvent mx_cryptoEvent_icon" className="mx_cryptoEvent mx_cryptoEvent_icon"
title={_t("Encryption enabled")} title={_t("Encryption enabled")}
subtitle={subtitle} subtitle={subtitle}
timestamp={timestamp}
/>; />;
} }
@ -68,6 +70,7 @@ const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent }, ref) =>
className="mx_cryptoEvent mx_cryptoEvent_icon" className="mx_cryptoEvent mx_cryptoEvent_icon"
title={_t("Encryption enabled")} title={_t("Encryption enabled")}
subtitle={_t("Ignored attempt to disable encryption")} subtitle={_t("Ignored attempt to disable encryption")}
timestamp={timestamp}
/>; />;
} }
@ -76,6 +79,7 @@ const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent }, ref) =>
title={_t("Encryption not enabled")} title={_t("Encryption not enabled")}
subtitle={_t("The encryption used by this room isn't supported.")} subtitle={_t("The encryption used by this room isn't supported.")}
ref={ref} ref={ref}
timestamp={timestamp}
/>; />;
}); });

View file

@ -20,15 +20,23 @@ import classNames from "classnames";
interface IProps { interface IProps {
className: string; className: string;
title: string; title: string;
timestamp?: JSX.Element;
subtitle?: ReactNode; subtitle?: ReactNode;
children?: ReactChildren; children?: ReactChildren;
} }
const EventTileBubble = forwardRef<HTMLDivElement, IProps>(({ className, title, subtitle, children }, ref) => { const EventTileBubble = forwardRef<HTMLDivElement, IProps>(({
className,
title,
timestamp,
subtitle,
children,
}, ref) => {
return <div className={classNames("mx_EventTileBubble", className)} ref={ref}> return <div className={classNames("mx_EventTileBubble", className)} ref={ref}>
<div className="mx_EventTileBubble_title">{ title }</div> <div className="mx_EventTileBubble_title">{ title }</div>
{ subtitle && <div className="mx_EventTileBubble_subtitle">{ subtitle }</div> } { subtitle && <div className="mx_EventTileBubble_subtitle">{ subtitle }</div> }
{ children } { children }
{ timestamp }
</div>; </div>;
}); });

View file

@ -26,6 +26,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps { interface IProps {
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
timestamp?: JSX.Element;
} }
@replaceableComponent("views.messages.MJitsiWidgetEvent") @replaceableComponent("views.messages.MJitsiWidgetEvent")
@ -54,6 +55,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
return <EventTileBubble return <EventTileBubble
className="mx_MJitsiWidgetEvent" className="mx_MJitsiWidgetEvent"
title={_t('Video conference ended by %(senderName)s', { senderName })} title={_t('Video conference ended by %(senderName)s', { senderName })}
timestamp={this.props.timestamp}
/>; />;
} else if (prevUrl) { } else if (prevUrl) {
// modified // modified
@ -61,6 +63,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
className="mx_MJitsiWidgetEvent" className="mx_MJitsiWidgetEvent"
title={_t('Video conference updated by %(senderName)s', { senderName })} title={_t('Video conference updated by %(senderName)s', { senderName })}
subtitle={joinCopy} subtitle={joinCopy}
timestamp={this.props.timestamp}
/>; />;
} else { } else {
// assume added // assume added
@ -68,6 +71,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
className="mx_MJitsiWidgetEvent" className="mx_MJitsiWidgetEvent"
title={_t("Video conference started by %(senderName)s", { senderName })} title={_t("Video conference started by %(senderName)s", { senderName })}
subtitle={joinCopy} subtitle={joinCopy}
timestamp={this.props.timestamp}
/>; />;
} }
} }

View file

@ -29,6 +29,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps { interface IProps {
/* the MatrixEvent to show */ /* the MatrixEvent to show */
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
timestamp?: JSX.Element;
} }
@replaceableComponent("views.messages.MKeyVerificationConclusion") @replaceableComponent("views.messages.MKeyVerificationConclusion")
@ -133,6 +134,7 @@ export default class MKeyVerificationConclusion extends React.Component<IProps>
className={classes} className={classes}
title={title} title={title}
subtitle={userLabelForEventRoom(request.otherUserId, mxEvent.getRoomId())} subtitle={userLabelForEventRoom(request.otherUserId, mxEvent.getRoomId())}
timestamp={this.props.timestamp}
/>; />;
} }

View file

@ -30,6 +30,7 @@ import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
interface IProps { interface IProps {
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
timestamp?: JSX.Element;
} }
@replaceableComponent("views.messages.MKeyVerificationRequest") @replaceableComponent("views.messages.MKeyVerificationRequest")
@ -168,6 +169,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
className="mx_cryptoEvent mx_cryptoEvent_icon" className="mx_cryptoEvent mx_cryptoEvent_icon"
title={title} title={title}
subtitle={subtitle} subtitle={subtitle}
timestamp={this.props.timestamp}
> >
{ stateNode } { stateNode }
</EventTileBubble>; </EventTileBubble>;

View file

@ -29,6 +29,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps { interface IProps {
/* the MatrixEvent to show */ /* the MatrixEvent to show */
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
timestamp?: JSX.Element;
} }
@replaceableComponent("views.messages.RoomCreate") @replaceableComponent("views.messages.RoomCreate")
@ -65,6 +66,7 @@ export default class RoomCreate extends React.Component<IProps> {
className="mx_CreateEvent" className="mx_CreateEvent"
title={_t("This room is a continuation of another conversation.")} title={_t("This room is a continuation of another conversation.")}
subtitle={link} subtitle={link}
timestamp={this.props.timestamp}
/>; />;
} }
} }

View file

@ -1273,12 +1273,13 @@ export default class EventTile extends React.Component<IProps, IState> {
? this.props.mxEvent.getTs() ? this.props.mxEvent.getTs()
: thread?.lastReply().getTs(); : thread?.lastReply().getTs();
const timestamp = showTimestamp && ts ? const messageTimestamp = <MessageTimestamp
<MessageTimestamp showRelative={this.props.tileShape === TileShape.ThreadPanel}
showRelative={this.props.tileShape === TileShape.ThreadPanel} showTwelveHour={this.props.isTwelveHour}
showTwelveHour={this.props.isTwelveHour} ts={ts}
ts={ts} />;
/> : null;
const timestamp = showTimestamp && ts ? messageTimestamp : null;
const keyRequestHelpText = const keyRequestHelpText =
<div className="mx_EventTile_keyRequestInfo_tooltip_contents"> <div className="mx_EventTile_keyRequestInfo_tooltip_contents">
@ -1339,9 +1340,10 @@ export default class EventTile extends React.Component<IProps, IState> {
{ timestamp } { timestamp }
</a>; </a>;
const useIRCLayout = this.props.layout == Layout.IRC; const useIRCLayout = this.props.layout === Layout.IRC;
const groupTimestamp = !useIRCLayout ? linkedTimestamp : null; const groupTimestamp = !useIRCLayout ? linkedTimestamp : null;
const ircTimestamp = useIRCLayout ? linkedTimestamp : null; const ircTimestamp = useIRCLayout ? linkedTimestamp : null;
const bubbleTimestamp = this.props.layout === Layout.Bubble ? messageTimestamp : null;
const groupPadlock = !useIRCLayout && !isBubbleMessage && this.renderE2EPadlock(); const groupPadlock = !useIRCLayout && !isBubbleMessage && this.renderE2EPadlock();
const ircPadlock = useIRCLayout && !isBubbleMessage && this.renderE2EPadlock(); const ircPadlock = useIRCLayout && !isBubbleMessage && this.renderE2EPadlock();
@ -1567,7 +1569,8 @@ export default class EventTile extends React.Component<IProps, IState> {
{ groupTimestamp } { groupTimestamp }
{ groupPadlock } { groupPadlock }
{ replyChain } { replyChain }
<EventTileType ref={this.tile} <EventTileType
ref={this.tile}
mxEvent={this.props.mxEvent} mxEvent={this.props.mxEvent}
forExport={this.props.forExport} forExport={this.props.forExport}
replacingEventId={this.props.replacingEventId} replacingEventId={this.props.replacingEventId}
@ -1580,6 +1583,7 @@ export default class EventTile extends React.Component<IProps, IState> {
callEventGrouper={this.props.callEventGrouper} callEventGrouper={this.props.callEventGrouper}
getRelationsForEvent={this.props.getRelationsForEvent} getRelationsForEvent={this.props.getRelationsForEvent}
isSeeingThroughMessageHiddenForModeration={isSeeingThroughMessageHiddenForModeration} isSeeingThroughMessageHiddenForModeration={isSeeingThroughMessageHiddenForModeration}
timestamp={bubbleTimestamp}
/> />
{ keyRequestInfo } { keyRequestInfo }
{ actionBar } { actionBar }