Merge branch 'develop' into gsouquet/message-bubbles-4635
This commit is contained in:
commit
51c5094ca4
9 changed files with 47 additions and 21 deletions
|
@ -49,4 +49,8 @@ limitations under the License.
|
||||||
padding-right: 6px; // with the fixed width this ends up as a visual 8px most of the time, as intended.
|
padding-right: 6px; // with the fixed width this ends up as a visual 8px most of the time, as intended.
|
||||||
padding-left: 8px; // isolate from recording circle / play control
|
padding-left: 8px; // isolate from recording circle / play control
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mx_VoiceMessagePrimaryContainer_noWaveform {
|
||||||
|
max-width: 162px; // with all the padding this results in 185px wide
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,18 @@ limitations under the License.
|
||||||
import { Playback, PlaybackState } from "../../../voice/Playback";
|
import { Playback, PlaybackState } from "../../../voice/Playback";
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||||
import PlaybackWaveform from "./PlaybackWaveform";
|
|
||||||
import PlayPauseButton from "./PlayPauseButton";
|
import PlayPauseButton from "./PlayPauseButton";
|
||||||
import PlaybackClock from "./PlaybackClock";
|
import PlaybackClock from "./PlaybackClock";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import { TileShape } from "../rooms/EventTile";
|
||||||
|
import PlaybackWaveform from "./PlaybackWaveform";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
// Playback instance to render. Cannot change during component lifecycle: create
|
// Playback instance to render. Cannot change during component lifecycle: create
|
||||||
// an all-new component instead.
|
// an all-new component instead.
|
||||||
playback: Playback;
|
playback: Playback;
|
||||||
|
|
||||||
|
tileShape?: TileShape;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -50,15 +53,22 @@ export default class RecordingPlayback extends React.PureComponent<IProps, IStat
|
||||||
this.props.playback.prepare();
|
this.props.playback.prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get isWaveformable(): boolean {
|
||||||
|
return this.props.tileShape !== TileShape.Notif
|
||||||
|
&& this.props.tileShape !== TileShape.FileGrid
|
||||||
|
&& this.props.tileShape !== TileShape.Pinned;
|
||||||
|
}
|
||||||
|
|
||||||
private onPlaybackUpdate = (ev: PlaybackState) => {
|
private onPlaybackUpdate = (ev: PlaybackState) => {
|
||||||
this.setState({ playbackPhase: ev });
|
this.setState({ playbackPhase: ev });
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): ReactNode {
|
public render(): ReactNode {
|
||||||
return <div className='mx_MediaBody mx_VoiceMessagePrimaryContainer'>
|
const shapeClass = !this.isWaveformable ? 'mx_VoiceMessagePrimaryContainer_noWaveform' : '';
|
||||||
|
return <div className={'mx_MediaBody mx_VoiceMessagePrimaryContainer ' + shapeClass}>
|
||||||
<PlayPauseButton playback={this.props.playback} playbackPhase={this.state.playbackPhase} />
|
<PlayPauseButton playback={this.props.playback} playbackPhase={this.state.playbackPhase} />
|
||||||
<PlaybackClock playback={this.props.playback} />
|
<PlaybackClock playback={this.props.playback} />
|
||||||
<PlaybackWaveform playback={this.props.playback} />
|
{ this.isWaveformable && <PlaybackWaveform playback={this.props.playback} /> }
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 New Vector Ltd
|
Copyright 2017 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -32,6 +31,7 @@ import sanitizeHtml from "sanitize-html";
|
||||||
import { UIFeature } from "../../../settings/UIFeature";
|
import { UIFeature } from "../../../settings/UIFeature";
|
||||||
import { PERMITTED_URL_SCHEMES } from "../../../HtmlUtils";
|
import { PERMITTED_URL_SCHEMES } from "../../../HtmlUtils";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import { TileShape } from "../rooms/EventTile";
|
||||||
|
|
||||||
// This component does no cycle detection, simply because the only way to make such a cycle would be to
|
// This component does no cycle detection, simply because the only way to make such a cycle would be to
|
||||||
// craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would
|
// craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would
|
||||||
|
@ -384,7 +384,7 @@ export default class ReplyThread extends React.Component {
|
||||||
{ dateSep }
|
{ dateSep }
|
||||||
<EventTile
|
<EventTile
|
||||||
mxEvent={ev}
|
mxEvent={ev}
|
||||||
tileShape="reply"
|
tileShape={TileShape.Reply}
|
||||||
onHeightChanged={this.props.onHeightChanged}
|
onHeightChanged={this.props.onHeightChanged}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
isRedacted={ev.isRedacted()}
|
isRedacted={ev.isRedacted()}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016, 2018, 2021 The Matrix.org Foundation C.I.C.
|
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -24,6 +24,7 @@ import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { mediaFromContent } from "../../../customisations/Media";
|
import { mediaFromContent } from "../../../customisations/Media";
|
||||||
import ErrorDialog from "../dialogs/ErrorDialog";
|
import ErrorDialog from "../dialogs/ErrorDialog";
|
||||||
|
import { TileShape } from "../rooms/EventTile";
|
||||||
|
|
||||||
let downloadIconUrl; // cached copy of the download.svg asset for the sandboxed iframe later on
|
let downloadIconUrl; // cached copy of the download.svg asset for the sandboxed iframe later on
|
||||||
|
|
||||||
|
@ -306,7 +307,7 @@ export default class MFileBody extends React.Component {
|
||||||
// If the attachment is not encrypted then we check whether we
|
// If the attachment is not encrypted then we check whether we
|
||||||
// are being displayed in the room timeline or in a list of
|
// are being displayed in the room timeline or in a list of
|
||||||
// files in the right hand side of the screen.
|
// files in the right hand side of the screen.
|
||||||
if (this.props.tileShape === "file_grid") {
|
if (this.props.tileShape === TileShape.FileGrid) {
|
||||||
return (
|
return (
|
||||||
<span className="mx_MFileBody">
|
<span className="mx_MFileBody">
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
|
|
@ -25,9 +25,11 @@ import { mediaFromContent } from "../../../customisations/Media";
|
||||||
import { decryptFile } from "../../../utils/DecryptFile";
|
import { decryptFile } from "../../../utils/DecryptFile";
|
||||||
import RecordingPlayback from "../audio_messages/RecordingPlayback";
|
import RecordingPlayback from "../audio_messages/RecordingPlayback";
|
||||||
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
|
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
|
||||||
|
import { TileShape } from "../rooms/EventTile";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
tileShape?: TileShape;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -103,7 +105,7 @@ export default class MVoiceMessageBody extends React.PureComponent<IProps, IStat
|
||||||
// At this point we should have a playable state
|
// At this point we should have a playable state
|
||||||
return (
|
return (
|
||||||
<span className="mx_MVoiceMessageBody">
|
<span className="mx_MVoiceMessageBody">
|
||||||
<RecordingPlayback playback={this.state.playback} />
|
<RecordingPlayback playback={this.state.playback} tileShape={this.props.tileShape} />
|
||||||
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />
|
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default class MessageEvent extends React.Component {
|
||||||
onHeightChanged: PropTypes.func,
|
onHeightChanged: PropTypes.func,
|
||||||
|
|
||||||
/* the shape of the tile, used */
|
/* the shape of the tile, used */
|
||||||
tileShape: PropTypes.string,
|
tileShape: PropTypes.string, // TODO: Use TileShape enum
|
||||||
|
|
||||||
/* the maximum image height to use, if the event is an image */
|
/* the maximum image height to use, if the event is an image */
|
||||||
maxImageHeight: PropTypes.number,
|
maxImageHeight: PropTypes.number,
|
||||||
|
|
|
@ -192,6 +192,7 @@ export enum TileShape {
|
||||||
FileGrid = "file_grid",
|
FileGrid = "file_grid",
|
||||||
Reply = "reply",
|
Reply = "reply",
|
||||||
ReplyPreview = "reply_preview",
|
ReplyPreview = "reply_preview",
|
||||||
|
Pinned = "pinned",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
@ -907,7 +908,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
mx_EventTile_12hr: this.props.isTwelveHour,
|
mx_EventTile_12hr: this.props.isTwelveHour,
|
||||||
// Note: we keep the `sending` state class for tests, not for our styles
|
// Note: we keep the `sending` state class for tests, not for our styles
|
||||||
mx_EventTile_sending: !isEditing && isSending,
|
mx_EventTile_sending: !isEditing && isSending,
|
||||||
mx_EventTile_highlight: this.props.tileShape === 'notif' ? false : this.shouldHighlight(),
|
mx_EventTile_highlight: this.props.tileShape === TileShape.Notif ? false : this.shouldHighlight(),
|
||||||
mx_EventTile_selected: this.props.isSelectedEvent,
|
mx_EventTile_selected: this.props.isSelectedEvent,
|
||||||
mx_EventTile_continuation: this.props.tileShape ? '' : this.props.continuation,
|
mx_EventTile_continuation: this.props.tileShape ? '' : this.props.continuation,
|
||||||
mx_EventTile_last: this.props.last,
|
mx_EventTile_last: this.props.last,
|
||||||
|
@ -940,7 +941,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
let avatarSize;
|
let avatarSize;
|
||||||
let needsSenderProfile;
|
let needsSenderProfile;
|
||||||
|
|
||||||
if (this.props.tileShape === "notif") {
|
if (this.props.tileShape === TileShape.Notif) {
|
||||||
avatarSize = 24;
|
avatarSize = 24;
|
||||||
needsSenderProfile = true;
|
needsSenderProfile = true;
|
||||||
} else if (tileHandler === 'messages.RoomCreate' || isBubbleMessage) {
|
} else if (tileHandler === 'messages.RoomCreate' || isBubbleMessage) {
|
||||||
|
@ -954,7 +955,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
} else if (this.props.layout == Layout.IRC) {
|
} else if (this.props.layout == Layout.IRC) {
|
||||||
avatarSize = 14;
|
avatarSize = 14;
|
||||||
needsSenderProfile = true;
|
needsSenderProfile = true;
|
||||||
} else if (this.props.continuation && this.props.tileShape !== "file_grid") {
|
} else if (this.props.continuation && this.props.tileShape !== TileShape.FileGrid) {
|
||||||
// no avatar or sender profile for continuation messages
|
// no avatar or sender profile for continuation messages
|
||||||
avatarSize = 0;
|
avatarSize = 0;
|
||||||
needsSenderProfile = false;
|
needsSenderProfile = false;
|
||||||
|
@ -984,7 +985,11 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsSenderProfile && this.props.hideSender !== true) {
|
if (needsSenderProfile && this.props.hideSender !== true) {
|
||||||
if (!this.props.tileShape || this.props.tileShape === 'reply' || this.props.tileShape === 'reply_preview') {
|
if (
|
||||||
|
!this.props.tileShape
|
||||||
|
|| this.props.tileShape === TileShape.Reply
|
||||||
|
|| this.props.tileShape === TileShape.ReplyPreview
|
||||||
|
) {
|
||||||
sender = <SenderProfile onClick={this.onSenderProfileClick}
|
sender = <SenderProfile onClick={this.onSenderProfileClick}
|
||||||
mxEvent={this.props.mxEvent}
|
mxEvent={this.props.mxEvent}
|
||||||
enableFlair={this.props.enableFlair}
|
enableFlair={this.props.enableFlair}
|
||||||
|
@ -1074,7 +1079,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this.props.tileShape) {
|
switch (this.props.tileShape) {
|
||||||
case 'notif': {
|
case TileShape.Notif: {
|
||||||
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
||||||
return React.createElement(this.props.as || "li", {
|
return React.createElement(this.props.as || "li", {
|
||||||
"className": classes,
|
"className": classes,
|
||||||
|
@ -1102,11 +1107,12 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
highlightLink={this.props.highlightLink}
|
highlightLink={this.props.highlightLink}
|
||||||
showUrlPreview={this.props.showUrlPreview}
|
showUrlPreview={this.props.showUrlPreview}
|
||||||
onHeightChanged={this.props.onHeightChanged}
|
onHeightChanged={this.props.onHeightChanged}
|
||||||
|
tileShape={this.props.tileShape}
|
||||||
/>
|
/>
|
||||||
</div>,
|
</div>,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
case 'file_grid': {
|
case TileShape.FileGrid: {
|
||||||
return React.createElement(this.props.as || "li", {
|
return React.createElement(this.props.as || "li", {
|
||||||
"className": classes,
|
"className": classes,
|
||||||
"aria-live": ariaLive,
|
"aria-live": ariaLive,
|
||||||
|
@ -1137,10 +1143,10 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'reply':
|
case TileShape.Reply:
|
||||||
case 'reply_preview': {
|
case TileShape.ReplyPreview: {
|
||||||
let thread;
|
let thread;
|
||||||
if (this.props.tileShape === 'reply_preview') {
|
if (this.props.tileShape === TileShape.ReplyPreview) {
|
||||||
thread = ReplyThread.makeThread(
|
thread = ReplyThread.makeThread(
|
||||||
this.props.mxEvent,
|
this.props.mxEvent,
|
||||||
this.props.onHeightChanged,
|
this.props.onHeightChanged,
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { getUserNameColorClass } from "../../../utils/FormattingUtils";
|
import { getUserNameColorClass } from "../../../utils/FormattingUtils";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
|
import { TileShape } from "./EventTile";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -87,6 +88,7 @@ export default class PinnedEventTile extends React.Component<IProps> {
|
||||||
className="mx_PinnedEventTile_body"
|
className="mx_PinnedEventTile_body"
|
||||||
maxImageHeight={150}
|
maxImageHeight={150}
|
||||||
onHeightChanged={() => {}} // we need to give this, apparently
|
onHeightChanged={() => {}} // we need to give this, apparently
|
||||||
|
tileShape={TileShape.Pinned}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 New Vector Ltd
|
Copyright 2017 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -24,6 +24,7 @@ import PropTypes from "prop-types";
|
||||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||||
import { UIFeature } from "../../../settings/UIFeature";
|
import { UIFeature } from "../../../settings/UIFeature";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import { TileShape } from "./EventTile";
|
||||||
|
|
||||||
function cancelQuoting() {
|
function cancelQuoting() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
|
@ -90,7 +91,7 @@ export default class ReplyPreview extends React.Component {
|
||||||
<div className="mx_ReplyPreview_clear" />
|
<div className="mx_ReplyPreview_clear" />
|
||||||
<EventTile
|
<EventTile
|
||||||
alwaysShowTimestamps={true}
|
alwaysShowTimestamps={true}
|
||||||
tileShape="reply_preview"
|
tileShape={TileShape.ReplyPreview}
|
||||||
mxEvent={this.state.event}
|
mxEvent={this.state.event}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")}
|
isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue