Merge branch 'develop' into gsouquet/threaded-messaging-2349
This commit is contained in:
commit
edd4d42e7f
14 changed files with 84 additions and 39 deletions
|
@ -62,6 +62,9 @@ $SpaceRoomViewInnerWidth: 428px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SpaceRoomView {
|
.mx_SpaceRoomView {
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
.mx_MainSplit > div:first-child {
|
.mx_MainSplit > div:first-child {
|
||||||
padding: 80px 60px;
|
padding: 80px 60px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
@ -248,6 +251,7 @@ $SpaceRoomViewInnerWidth: 428px;
|
||||||
.mx_SpaceRoomView_landing {
|
.mx_SpaceRoomView_landing {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
> .mx_BaseAvatar_image,
|
> .mx_BaseAvatar_image,
|
||||||
> .mx_BaseAvatar > .mx_BaseAvatar_image {
|
> .mx_BaseAvatar > .mx_BaseAvatar_image {
|
||||||
|
|
|
@ -7,13 +7,15 @@
|
||||||
background: $primary-bg-color;
|
background: $primary-bg-color;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
max-height: 50vh;
|
max-height: 35vh;
|
||||||
overflow: auto;
|
overflow: clip;
|
||||||
|
display: flex;
|
||||||
box-shadow: 0px -16px 32px $composer-shadow-color;
|
box-shadow: 0px -16px 32px $composer-shadow-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Autocomplete_ProviderSection {
|
.mx_Autocomplete_ProviderSection {
|
||||||
border-bottom: 1px solid $primary-hairline-color;
|
border-bottom: 1px solid $primary-hairline-color;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a "block" completion takes up a whole line */
|
/* a "block" completion takes up a whole line */
|
||||||
|
@ -59,8 +61,8 @@
|
||||||
|
|
||||||
.mx_Autocomplete_Completion_container_pill {
|
.mx_Autocomplete_Completion_container_pill {
|
||||||
margin: 12px;
|
margin: 12px;
|
||||||
display: flex;
|
height: 100%;
|
||||||
flex-direction: column;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Autocomplete_Completion_container_truncate {
|
.mx_Autocomplete_Completion_container_truncate {
|
||||||
|
|
|
@ -105,6 +105,8 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_ReplyTile .mx_SenderProfile {
|
.mx_ReplyTile .mx_SenderProfile {
|
||||||
display: block;
|
display: block;
|
||||||
|
top: unset;
|
||||||
|
left: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ReactionsRow {
|
.mx_ReactionsRow {
|
||||||
|
@ -188,8 +190,6 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ReplyThread {
|
.mx_ReplyThread {
|
||||||
margin: 0 calc(-1 * var(--gutterSize));
|
|
||||||
|
|
||||||
.mx_EventTile_reply {
|
.mx_EventTile_reply {
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -223,11 +223,6 @@ limitations under the License.
|
||||||
margin-left: -9px;
|
margin-left: -9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ReplyThread {
|
|
||||||
border-left-width: 2px;
|
|
||||||
border-left-color: $eventbubble-reply-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Special layout scenario for "Unable To Decrypt (UTD)" events */
|
/* Special layout scenario for "Unable To Decrypt (UTD)" events */
|
||||||
&.mx_EventTile_bad > .mx_EventTile_line {
|
&.mx_EventTile_bad > .mx_EventTile_line {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
@ -67,7 +67,7 @@ export default class EmojiProvider extends AutocompleteProvider {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(EMOJI_REGEX);
|
super(EMOJI_REGEX);
|
||||||
this.matcher = new QueryMatcher<ISortedEmoji>(SORTED_EMOJI, {
|
this.matcher = new QueryMatcher<ISortedEmoji>(SORTED_EMOJI, {
|
||||||
keys: ['emoji.emoticon'],
|
keys: [],
|
||||||
funcs: [o => o.emoji.shortcodes.map(s => `:${s}:`)],
|
funcs: [o => o.emoji.shortcodes.map(s => `:${s}:`)],
|
||||||
// For matching against ascii equivalents
|
// For matching against ascii equivalents
|
||||||
shouldMatchWordsOnly: false,
|
shouldMatchWordsOnly: false,
|
||||||
|
@ -91,7 +91,8 @@ export default class EmojiProvider extends AutocompleteProvider {
|
||||||
|
|
||||||
let completions = [];
|
let completions = [];
|
||||||
const { command, range } = this.getCurrentCommand(query, selection);
|
const { command, range } = this.getCurrentCommand(query, selection);
|
||||||
if (command) {
|
|
||||||
|
if (command && command[0].length > 2) {
|
||||||
const matchedString = command[0];
|
const matchedString = command[0];
|
||||||
completions = this.matcher.match(matchedString, limit);
|
completions = this.matcher.match(matchedString, limit);
|
||||||
|
|
||||||
|
|
|
@ -628,9 +628,12 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const wrapperClasses = classNames({
|
||||||
|
'mx_MatrixChat_wrapper': true,
|
||||||
|
'mx_MatrixChat_useCompactLayout': this.state.useCompactLayout,
|
||||||
|
});
|
||||||
const bodyClasses = classNames({
|
const bodyClasses = classNames({
|
||||||
'mx_MatrixChat': true,
|
'mx_MatrixChat': true,
|
||||||
'mx_MatrixChat_useCompactLayout': this.state.useCompactLayout,
|
|
||||||
'mx_MatrixChat--with-avatar': this.state.backgroundImage,
|
'mx_MatrixChat--with-avatar': this.state.backgroundImage,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -645,7 +648,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
<div
|
<div
|
||||||
onPaste={this.onPaste}
|
onPaste={this.onPaste}
|
||||||
onKeyDown={this.onReactKeyDown}
|
onKeyDown={this.onReactKeyDown}
|
||||||
className='mx_MatrixChat_wrapper'
|
className={wrapperClasses}
|
||||||
aria-hidden={this.props.hideToSRUsers}
|
aria-hidden={this.props.hideToSRUsers}
|
||||||
>
|
>
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
|
|
|
@ -215,7 +215,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
|
||||||
{
|
{
|
||||||
a: (sub) => <a
|
a: (sub) => <a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://github.com/vector-im/element-web/issues/new"
|
href="https://github.com/vector-im/element-web/issues/new/choose"
|
||||||
>
|
>
|
||||||
{ sub }
|
{ sub }
|
||||||
</a>,
|
</a>,
|
||||||
|
|
|
@ -28,7 +28,7 @@ import StyledRadioGroup from "../elements/StyledRadioGroup";
|
||||||
|
|
||||||
const existingIssuesUrl = "https://github.com/vector-im/element-web/issues" +
|
const existingIssuesUrl = "https://github.com/vector-im/element-web/issues" +
|
||||||
"?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc";
|
"?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc";
|
||||||
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new";
|
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new/choose";
|
||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
const [rating, setRating] = useState("");
|
const [rating, setRating] = useState("");
|
||||||
|
|
|
@ -77,7 +77,7 @@ export default class ErrorBoundary extends React.PureComponent<{}, IState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.error) {
|
if (this.state.error) {
|
||||||
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new";
|
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new/choose";
|
||||||
|
|
||||||
let bugReportSection;
|
let bugReportSection;
|
||||||
if (SdkConfig.get().bug_report_endpoint_url) {
|
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||||
|
|
|
@ -19,14 +19,12 @@ import MAudioBody from "./MAudioBody";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import MVoiceMessageBody from "./MVoiceMessageBody";
|
import MVoiceMessageBody from "./MVoiceMessageBody";
|
||||||
import { IBodyProps } from "./IBodyProps";
|
import { IBodyProps } from "./IBodyProps";
|
||||||
|
import { isVoiceMessage } from "../../../utils/EventUtils";
|
||||||
|
|
||||||
@replaceableComponent("views.messages.MVoiceOrAudioBody")
|
@replaceableComponent("views.messages.MVoiceOrAudioBody")
|
||||||
export default class MVoiceOrAudioBody extends React.PureComponent<IBodyProps> {
|
export default class MVoiceOrAudioBody extends React.PureComponent<IBodyProps> {
|
||||||
public render() {
|
public render() {
|
||||||
// MSC2516 is a legacy identifier. See https://github.com/matrix-org/matrix-doc/pull/3245
|
if (isVoiceMessage(this.props.mxEvent)) {
|
||||||
const isVoiceMessage = !!this.props.mxEvent.getContent()['org.matrix.msc2516.voice']
|
|
||||||
|| !!this.props.mxEvent.getContent()['org.matrix.msc3245.voice'];
|
|
||||||
if (isVoiceMessage) {
|
|
||||||
return <MVoiceMessageBody {...this.props} />;
|
return <MVoiceMessageBody {...this.props} />;
|
||||||
} else {
|
} else {
|
||||||
return <MAudioBody {...this.props} />;
|
return <MAudioBody {...this.props} />;
|
||||||
|
|
|
@ -36,6 +36,7 @@ import { showSpaceInvite } from "../../../utils/space";
|
||||||
import { privateShouldBeEncrypted } from "../../../createRoom";
|
import { privateShouldBeEncrypted } from "../../../createRoom";
|
||||||
import EventTileBubble from "../messages/EventTileBubble";
|
import EventTileBubble from "../messages/EventTileBubble";
|
||||||
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
|
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
|
||||||
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
|
|
||||||
function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room): boolean {
|
function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room): boolean {
|
||||||
const isEncrypted: boolean = matrixClient.isRoomEncrypted(room.roomId);
|
const isEncrypted: boolean = matrixClient.isRoomEncrypted(room.roomId);
|
||||||
|
@ -191,11 +192,21 @@ const NewRoomIntro = () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const sub2 = _t(
|
const subText = _t(
|
||||||
"Your private messages are normally encrypted, but this room isn't. "+
|
"Your private messages are normally encrypted, but this room isn't. "+
|
||||||
"Usually this is due to an unsupported device or method being used, " +
|
"Usually this is due to an unsupported device or method being used, " +
|
||||||
"like email invites. <a>Enable encryption in settings.</a>", {},
|
"like email invites.",
|
||||||
{ a: sub => <a onClick={openRoomSettings} href="#">{ sub }</a> },
|
);
|
||||||
|
|
||||||
|
let subButton;
|
||||||
|
if (room.currentState.mayClientSendStateEvent(EventType.RoomEncryption, MatrixClientPeg.get())) {
|
||||||
|
subButton = (
|
||||||
|
<a onClick={openRoomSettings} href="#"> { _t("Enable encryption in settings.") }</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const subtitle = (
|
||||||
|
<span> { subText } { subButton } </span>
|
||||||
);
|
);
|
||||||
|
|
||||||
return <div className="mx_NewRoomIntro">
|
return <div className="mx_NewRoomIntro">
|
||||||
|
@ -204,7 +215,7 @@ const NewRoomIntro = () => {
|
||||||
<EventTileBubble
|
<EventTileBubble
|
||||||
className="mx_cryptoEvent mx_cryptoEvent_icon_warning"
|
className="mx_cryptoEvent mx_cryptoEvent_icon_warning"
|
||||||
title={_t("End-to-end encryption isn't enabled")}
|
title={_t("End-to-end encryption isn't enabled")}
|
||||||
subtitle={sub2}
|
subtitle={subtitle}
|
||||||
/>
|
/>
|
||||||
) }
|
) }
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,9 @@ import MImageReplyBody from "../messages/MImageReplyBody";
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { EventType, MsgType } from 'matrix-js-sdk/src/@types/event';
|
import { EventType, MsgType } from 'matrix-js-sdk/src/@types/event';
|
||||||
import { replaceableComponent } from '../../../utils/replaceableComponent';
|
import { replaceableComponent } from '../../../utils/replaceableComponent';
|
||||||
import { getEventDisplayInfo } from '../../../utils/EventUtils';
|
import { getEventDisplayInfo, isVoiceMessage } from '../../../utils/EventUtils';
|
||||||
import MFileBody from "../messages/MFileBody";
|
import MFileBody from "../messages/MFileBody";
|
||||||
|
import MVoiceMessageBody from "../messages/MVoiceMessageBody";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -95,7 +96,7 @@ export default class ReplyTile extends React.PureComponent<IProps> {
|
||||||
const msgType = mxEvent.getContent().msgtype;
|
const msgType = mxEvent.getContent().msgtype;
|
||||||
const evType = mxEvent.getType() as EventType;
|
const evType = mxEvent.getType() as EventType;
|
||||||
|
|
||||||
const { tileHandler, isInfoMessage } = getEventDisplayInfo(this.props.mxEvent);
|
const { tileHandler, isInfoMessage } = getEventDisplayInfo(mxEvent);
|
||||||
// This shouldn't happen: the caller should check we support this type
|
// This shouldn't happen: the caller should check we support this type
|
||||||
// before trying to instantiate us
|
// before trying to instantiate us
|
||||||
if (!tileHandler) {
|
if (!tileHandler) {
|
||||||
|
@ -109,14 +110,14 @@ export default class ReplyTile extends React.PureComponent<IProps> {
|
||||||
const EventTileType = sdk.getComponent(tileHandler);
|
const EventTileType = sdk.getComponent(tileHandler);
|
||||||
|
|
||||||
const classes = classNames("mx_ReplyTile", {
|
const classes = classNames("mx_ReplyTile", {
|
||||||
mx_ReplyTile_info: isInfoMessage && !this.props.mxEvent.isRedacted(),
|
mx_ReplyTile_info: isInfoMessage && !mxEvent.isRedacted(),
|
||||||
mx_ReplyTile_audio: msgType === MsgType.Audio,
|
mx_ReplyTile_audio: msgType === MsgType.Audio,
|
||||||
mx_ReplyTile_video: msgType === MsgType.Video,
|
mx_ReplyTile_video: msgType === MsgType.Video,
|
||||||
});
|
});
|
||||||
|
|
||||||
let permalink = "#";
|
let permalink = "#";
|
||||||
if (this.props.permalinkCreator) {
|
if (this.props.permalinkCreator) {
|
||||||
permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
|
permalink = this.props.permalinkCreator.forEvent(mxEvent.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
let sender;
|
let sender;
|
||||||
|
@ -129,7 +130,7 @@ export default class ReplyTile extends React.PureComponent<IProps> {
|
||||||
|
|
||||||
if (needsSenderProfile) {
|
if (needsSenderProfile) {
|
||||||
sender = <SenderProfile
|
sender = <SenderProfile
|
||||||
mxEvent={this.props.mxEvent}
|
mxEvent={mxEvent}
|
||||||
enableFlair={false}
|
enableFlair={false}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
@ -137,7 +138,7 @@ export default class ReplyTile extends React.PureComponent<IProps> {
|
||||||
const msgtypeOverrides = {
|
const msgtypeOverrides = {
|
||||||
[MsgType.Image]: MImageReplyBody,
|
[MsgType.Image]: MImageReplyBody,
|
||||||
// Override audio and video body with file body. We also hide the download/decrypt button using CSS
|
// Override audio and video body with file body. We also hide the download/decrypt button using CSS
|
||||||
[MsgType.Audio]: MFileBody,
|
[MsgType.Audio]: isVoiceMessage(mxEvent) ? MVoiceMessageBody : MFileBody,
|
||||||
[MsgType.Video]: MFileBody,
|
[MsgType.Video]: MFileBody,
|
||||||
};
|
};
|
||||||
const evOverrides = {
|
const evOverrides = {
|
||||||
|
@ -151,14 +152,14 @@ export default class ReplyTile extends React.PureComponent<IProps> {
|
||||||
{ sender }
|
{ sender }
|
||||||
<EventTileType
|
<EventTileType
|
||||||
ref="tile"
|
ref="tile"
|
||||||
mxEvent={this.props.mxEvent}
|
mxEvent={mxEvent}
|
||||||
highlights={this.props.highlights}
|
highlights={this.props.highlights}
|
||||||
highlightLink={this.props.highlightLink}
|
highlightLink={this.props.highlightLink}
|
||||||
onHeightChanged={this.props.onHeightChanged}
|
onHeightChanged={this.props.onHeightChanged}
|
||||||
showUrlPreview={false}
|
showUrlPreview={false}
|
||||||
overrideBodyTypes={msgtypeOverrides}
|
overrideBodyTypes={msgtypeOverrides}
|
||||||
overrideEventTypes={evOverrides}
|
overrideEventTypes={evOverrides}
|
||||||
replacingEventId={this.props.mxEvent.replacingEventId()}
|
replacingEventId={mxEvent.replacingEventId()}
|
||||||
maxImageHeight={96} />
|
maxImageHeight={96} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,6 +25,8 @@ import InteractiveAuthDialog from '../dialogs/InteractiveAuthDialog';
|
||||||
import ConfirmDestroyCrossSigningDialog from '../dialogs/security/ConfirmDestroyCrossSigningDialog';
|
import ConfirmDestroyCrossSigningDialog from '../dialogs/security/ConfirmDestroyCrossSigningDialog';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { MatrixEvent } from 'matrix-js-sdk/src';
|
import { MatrixEvent } from 'matrix-js-sdk/src';
|
||||||
|
import SetupEncryptionDialog from '../dialogs/security/SetupEncryptionDialog';
|
||||||
|
import { accessSecretStorage } from '../../../SecurityManager';
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
error?: Error;
|
error?: Error;
|
||||||
|
@ -72,7 +74,16 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onBootstrapClick = () => {
|
private onBootstrapClick = () => {
|
||||||
this.bootstrapCrossSigning({ forceReset: false });
|
if (this.state.crossSigningPrivateKeysInStorage) {
|
||||||
|
Modal.createTrackedDialog(
|
||||||
|
"Verify session", "Verify session", SetupEncryptionDialog,
|
||||||
|
{}, null, /* priority = */ false, /* static = */ true,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Trigger the flow to set up secure backup, which is what this will do when in
|
||||||
|
// the appropriate state.
|
||||||
|
accessSecretStorage();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onStatusChanged = () => {
|
private onStatusChanged = () => {
|
||||||
|
@ -176,10 +187,14 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
|
||||||
summarisedStatus = <p>{ _t(
|
summarisedStatus = <p>{ _t(
|
||||||
"Your homeserver does not support cross-signing.",
|
"Your homeserver does not support cross-signing.",
|
||||||
) }</p>;
|
) }</p>;
|
||||||
} else if (crossSigningReady) {
|
} else if (crossSigningReady && crossSigningPrivateKeysInStorage) {
|
||||||
summarisedStatus = <p>✅ { _t(
|
summarisedStatus = <p>✅ { _t(
|
||||||
"Cross-signing is ready for use.",
|
"Cross-signing is ready for use.",
|
||||||
) }</p>;
|
) }</p>;
|
||||||
|
} else if (crossSigningReady && !crossSigningPrivateKeysInStorage) {
|
||||||
|
summarisedStatus = <p>⚠️ { _t(
|
||||||
|
"Cross-signing is ready but keys are not backed up.",
|
||||||
|
) }</p>;
|
||||||
} else if (crossSigningPrivateKeysInStorage) {
|
} else if (crossSigningPrivateKeysInStorage) {
|
||||||
summarisedStatus = <p>{ _t(
|
summarisedStatus = <p>{ _t(
|
||||||
"Your account has a cross-signing identity in secret storage, " +
|
"Your account has a cross-signing identity in secret storage, " +
|
||||||
|
@ -210,9 +225,13 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
|
||||||
|
|
||||||
// TODO: determine how better to expose this to users in addition to prompts at login/toast
|
// TODO: determine how better to expose this to users in addition to prompts at login/toast
|
||||||
if (!keysExistEverywhere && homeserverSupportsCrossSigning) {
|
if (!keysExistEverywhere && homeserverSupportsCrossSigning) {
|
||||||
|
let buttonCaption = _t("Set up Secure Backup");
|
||||||
|
if (crossSigningPrivateKeysInStorage) {
|
||||||
|
buttonCaption = _t("Verify this session");
|
||||||
|
}
|
||||||
actions.push(
|
actions.push(
|
||||||
<AccessibleButton key="setup" kind="primary" onClick={this.onBootstrapClick}>
|
<AccessibleButton key="setup" kind="primary" onClick={this.onBootstrapClick}>
|
||||||
{ _t("Set up") }
|
{ buttonCaption }
|
||||||
</AccessibleButton>,
|
</AccessibleButton>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1103,9 +1103,9 @@
|
||||||
"Change Password": "Change Password",
|
"Change Password": "Change Password",
|
||||||
"Your homeserver does not support cross-signing.": "Your homeserver does not support cross-signing.",
|
"Your homeserver does not support cross-signing.": "Your homeserver does not support cross-signing.",
|
||||||
"Cross-signing is ready for use.": "Cross-signing is ready for use.",
|
"Cross-signing is ready for use.": "Cross-signing is ready for use.",
|
||||||
|
"Cross-signing is ready but keys are not backed up.": "Cross-signing is ready but keys are not backed up.",
|
||||||
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
|
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
|
||||||
"Cross-signing is not set up.": "Cross-signing is not set up.",
|
"Cross-signing is not set up.": "Cross-signing is not set up.",
|
||||||
"Set up": "Set up",
|
|
||||||
"Reset": "Reset",
|
"Reset": "Reset",
|
||||||
"Cross-signing public keys:": "Cross-signing public keys:",
|
"Cross-signing public keys:": "Cross-signing public keys:",
|
||||||
"in memory": "in memory",
|
"in memory": "in memory",
|
||||||
|
@ -1203,6 +1203,7 @@
|
||||||
"Algorithm:": "Algorithm:",
|
"Algorithm:": "Algorithm:",
|
||||||
"Your keys are <b>not being backed up from this session</b>.": "Your keys are <b>not being backed up from this session</b>.",
|
"Your keys are <b>not being backed up from this session</b>.": "Your keys are <b>not being backed up from this session</b>.",
|
||||||
"Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.",
|
"Back up your keys before signing out to avoid losing them.": "Back up your keys before signing out to avoid losing them.",
|
||||||
|
"Set up": "Set up",
|
||||||
"well formed": "well formed",
|
"well formed": "well formed",
|
||||||
"unexpected type": "unexpected type",
|
"unexpected type": "unexpected type",
|
||||||
"Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.": "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.",
|
"Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.": "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.",
|
||||||
|
@ -1572,7 +1573,8 @@
|
||||||
"Invite to just this room": "Invite to just this room",
|
"Invite to just this room": "Invite to just this room",
|
||||||
"Add a photo, so people can easily spot your room.": "Add a photo, so people can easily spot your room.",
|
"Add a photo, so people can easily spot your room.": "Add a photo, so people can easily spot your room.",
|
||||||
"This is the start of <roomName/>.": "This is the start of <roomName/>.",
|
"This is the start of <roomName/>.": "This is the start of <roomName/>.",
|
||||||
"Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites. <a>Enable encryption in settings.</a>": "Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites. <a>Enable encryption in settings.</a>",
|
"Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.": "Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.",
|
||||||
|
"Enable encryption in settings.": "Enable encryption in settings.",
|
||||||
"End-to-end encryption isn't enabled": "End-to-end encryption isn't enabled",
|
"End-to-end encryption isn't enabled": "End-to-end encryption isn't enabled",
|
||||||
"Unpin": "Unpin",
|
"Unpin": "Unpin",
|
||||||
"View message": "View message",
|
"View message": "View message",
|
||||||
|
|
|
@ -144,3 +144,12 @@ export function getEventDisplayInfo(mxEvent: MatrixEvent): {
|
||||||
|
|
||||||
return { tileHandler, isInfoMessage, isBubbleMessage, isLeftAlignedBubbleMessage };
|
return { tileHandler, isInfoMessage, isBubbleMessage, isLeftAlignedBubbleMessage };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isVoiceMessage(mxEvent: MatrixEvent): boolean {
|
||||||
|
const content = mxEvent.getContent();
|
||||||
|
// MSC2516 is a legacy identifier. See https://github.com/matrix-org/matrix-doc/pull/3245
|
||||||
|
return (
|
||||||
|
!!content['org.matrix.msc2516.voice'] ||
|
||||||
|
!!content['org.matrix.msc3245.voice']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue