Conform src/components/views/messages/*
code to strictNullChecks
(#10461)
This commit is contained in:
parent
7cb90d0f78
commit
cefd94859c
21 changed files with 109 additions and 96 deletions
src
components/views/messages
DownloadActionButton.tsxEditHistoryMessage.tsxMAudioBody.tsxMBeaconBody.tsxMFileBody.tsxMImageBody.tsxMImageReplyBody.tsxMJitsiWidgetEvent.tsxMKeyVerificationConclusion.tsxMKeyVerificationRequest.tsxMPollBody.tsxMVideoBody.tsxMessageEvent.tsxReactionsRow.tsxReactionsRowButton.tsxReactionsRowButtonTooltip.tsxRedactedBody.tsxRoomAvatarEvent.tsxTextualBody.tsx
settings/enums
utils
|
@ -63,17 +63,17 @@ export default class DownloadActionButton extends React.PureComponent<IProps, IS
|
||||||
|
|
||||||
if (this.state.blob) {
|
if (this.state.blob) {
|
||||||
// Cheat and trigger a download, again.
|
// Cheat and trigger a download, again.
|
||||||
return this.doDownload();
|
return this.doDownload(this.state.blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
const blob = await this.props.mediaEventHelperGet().sourceBlob.value;
|
const blob = await this.props.mediaEventHelperGet().sourceBlob.value;
|
||||||
this.setState({ blob });
|
this.setState({ blob });
|
||||||
await this.doDownload();
|
await this.doDownload(blob);
|
||||||
};
|
};
|
||||||
|
|
||||||
private async doDownload(): Promise<void> {
|
private async doDownload(blob: Blob): Promise<void> {
|
||||||
await this.downloader.download({
|
await this.downloader.download({
|
||||||
blob: this.state.blob,
|
blob,
|
||||||
name: this.props.mediaEventHelperGet().fileName,
|
name: this.props.mediaEventHelperGet().fileName,
|
||||||
});
|
});
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
|
|
|
@ -79,7 +79,7 @@ export default class EditHistoryMessage extends React.PureComponent<IProps, ISta
|
||||||
ConfirmAndWaitRedactDialog,
|
ConfirmAndWaitRedactDialog,
|
||||||
{
|
{
|
||||||
redact: async () => {
|
redact: async () => {
|
||||||
await cli.redactEvent(event.getRoomId(), event.getId());
|
await cli.redactEvent(event.getRoomId()!, event.getId());
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"mx_Dialog_confirmredact",
|
"mx_Dialog_confirmredact",
|
||||||
|
|
|
@ -76,7 +76,7 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
|
||||||
this.setState({ playback });
|
this.setState({ playback });
|
||||||
|
|
||||||
if (isVoiceMessage(this.props.mxEvent)) {
|
if (isVoiceMessage(this.props.mxEvent)) {
|
||||||
PlaybackQueue.forRoom(this.props.mxEvent.getRoomId()).unsortedEnqueue(this.props.mxEvent, playback);
|
PlaybackQueue.forRoom(this.props.mxEvent.getRoomId()!).unsortedEnqueue(this.props.mxEvent, playback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: the components later on will handle preparing the Playback class for us.
|
// Note: the components later on will handle preparing the Playback class for us.
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
MatrixEventEvent,
|
MatrixEventEvent,
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
RelationType,
|
RelationType,
|
||||||
|
IRedactOpts,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { BeaconLocationState } from "matrix-js-sdk/src/content-helpers";
|
import { BeaconLocationState } from "matrix-js-sdk/src/content-helpers";
|
||||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||||
|
@ -107,15 +108,15 @@ const useHandleBeaconRedaction = (
|
||||||
const onBeforeBeaconInfoRedaction = useCallback(
|
const onBeforeBeaconInfoRedaction = useCallback(
|
||||||
(_event: MatrixEvent, redactionEvent: MatrixEvent) => {
|
(_event: MatrixEvent, redactionEvent: MatrixEvent) => {
|
||||||
const relations = getRelationsForEvent
|
const relations = getRelationsForEvent
|
||||||
? getRelationsForEvent(event.getId(), RelationType.Reference, M_BEACON.name)
|
? getRelationsForEvent(event.getId()!, RelationType.Reference, M_BEACON.name)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
relations?.getRelations()?.forEach((locationEvent) => {
|
relations?.getRelations()?.forEach((locationEvent) => {
|
||||||
matrixClient.redactEvent(
|
matrixClient.redactEvent(
|
||||||
locationEvent.getRoomId(),
|
locationEvent.getRoomId()!,
|
||||||
locationEvent.getId(),
|
locationEvent.getId()!,
|
||||||
undefined,
|
undefined,
|
||||||
redactionEvent.getContent(),
|
redactionEvent.getContent<IRedactOpts>(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -132,7 +133,7 @@ const useHandleBeaconRedaction = (
|
||||||
|
|
||||||
const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelationsForEvent }, ref) => {
|
const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelationsForEvent }, ref) => {
|
||||||
const { beacon, isLive, latestLocationState, waitingToStart } = useBeaconState(mxEvent);
|
const { beacon, isLive, latestLocationState, waitingToStart } = useBeaconState(mxEvent);
|
||||||
const mapId = useUniqueId(mxEvent.getId());
|
const mapId = useUniqueId(mxEvent.getId()!);
|
||||||
|
|
||||||
const matrixClient = useContext(MatrixClientContext);
|
const matrixClient = useContext(MatrixClientContext);
|
||||||
const [error, setError] = useState<Error>();
|
const [error, setError] = useState<Error>();
|
||||||
|
@ -159,7 +160,7 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelati
|
||||||
Modal.createDialog(
|
Modal.createDialog(
|
||||||
BeaconViewDialog,
|
BeaconViewDialog,
|
||||||
{
|
{
|
||||||
roomId: mxEvent.getRoomId(),
|
roomId: mxEvent.getRoomId()!,
|
||||||
matrixClient,
|
matrixClient,
|
||||||
initialFocusedBeacon: beacon,
|
initialFocusedBeacon: beacon,
|
||||||
},
|
},
|
||||||
|
@ -170,11 +171,11 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelati
|
||||||
};
|
};
|
||||||
|
|
||||||
let map: JSX.Element;
|
let map: JSX.Element;
|
||||||
if (displayStatus === BeaconDisplayStatus.Active && !isMapDisplayError) {
|
if (displayStatus === BeaconDisplayStatus.Active && !isMapDisplayError && latestLocationState?.uri) {
|
||||||
map = (
|
map = (
|
||||||
<Map
|
<Map
|
||||||
id={mapId}
|
id={mapId}
|
||||||
centerGeoUri={latestLocationState?.uri}
|
centerGeoUri={latestLocationState.uri}
|
||||||
onError={setError}
|
onError={setError}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className="mx_MBeaconBody_map"
|
className="mx_MBeaconBody_map"
|
||||||
|
@ -183,7 +184,7 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelati
|
||||||
<SmartMarker
|
<SmartMarker
|
||||||
map={map}
|
map={map}
|
||||||
id={`${mapId}-marker`}
|
id={`${mapId}-marker`}
|
||||||
geoUri={latestLocationState?.uri}
|
geoUri={latestLocationState.uri!}
|
||||||
roomMember={markerRoomMember ?? undefined}
|
roomMember={markerRoomMember ?? undefined}
|
||||||
useMemberColor
|
useMemberColor
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -78,7 +78,7 @@ cacheDownloadIcon();
|
||||||
* @param {HTMLElement} element The element to get the current style of.
|
* @param {HTMLElement} element The element to get the current style of.
|
||||||
* @return {string} The CSS style encoded as a string.
|
* @return {string} The CSS style encoded as a string.
|
||||||
*/
|
*/
|
||||||
export function computedStyle(element: HTMLElement): string {
|
export function computedStyle(element: HTMLElement | null): string {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -142,6 +142,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private downloadFile(fileName: string, text: string): void {
|
private downloadFile(fileName: string, text: string): void {
|
||||||
|
if (!this.state.decryptedBlob) return;
|
||||||
this.fileDownloader.download({
|
this.fileDownloader.download({
|
||||||
blob: this.state.decryptedBlob,
|
blob: this.state.decryptedBlob,
|
||||||
name: fileName,
|
name: fileName,
|
||||||
|
|
|
@ -104,9 +104,10 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||||
|
|
||||||
const content = this.props.mxEvent.getContent<IMediaEventContent>();
|
const content = this.props.mxEvent.getContent<IMediaEventContent>();
|
||||||
const httpUrl = this.state.contentUrl;
|
const httpUrl = this.state.contentUrl;
|
||||||
|
if (!httpUrl) return;
|
||||||
const params: Omit<ComponentProps<typeof ImageView>, "onFinished"> = {
|
const params: Omit<ComponentProps<typeof ImageView>, "onFinished"> = {
|
||||||
src: httpUrl,
|
src: httpUrl,
|
||||||
name: content.body?.length > 0 ? content.body : _t("Attachment"),
|
name: content.body && content.body.length > 0 ? content.body : _t("Attachment"),
|
||||||
mxEvent: this.props.mxEvent,
|
mxEvent: this.props.mxEvent,
|
||||||
permalinkCreator: this.props.permalinkCreator,
|
permalinkCreator: this.props.permalinkCreator,
|
||||||
};
|
};
|
||||||
|
@ -135,7 +136,12 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||||
protected onImageEnter = (e: React.MouseEvent<HTMLImageElement>): void => {
|
protected onImageEnter = (e: React.MouseEvent<HTMLImageElement>): void => {
|
||||||
this.setState({ hover: true });
|
this.setState({ hover: true });
|
||||||
|
|
||||||
if (!this.state.showImage || !this.state.isAnimated || SettingsStore.getValue("autoplayGifs")) {
|
if (
|
||||||
|
!this.state.contentUrl ||
|
||||||
|
!this.state.showImage ||
|
||||||
|
!this.state.isAnimated ||
|
||||||
|
SettingsStore.getValue("autoplayGifs")
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const imgElement = e.currentTarget;
|
const imgElement = e.currentTarget;
|
||||||
|
@ -145,11 +151,12 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||||
protected onImageLeave = (e: React.MouseEvent<HTMLImageElement>): void => {
|
protected onImageLeave = (e: React.MouseEvent<HTMLImageElement>): void => {
|
||||||
this.setState({ hover: false });
|
this.setState({ hover: false });
|
||||||
|
|
||||||
if (!this.state.showImage || !this.state.isAnimated || SettingsStore.getValue("autoplayGifs")) {
|
const url = this.state.thumbUrl ?? this.state.contentUrl;
|
||||||
|
if (!url || !this.state.showImage || !this.state.isAnimated || SettingsStore.getValue("autoplayGifs")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const imgElement = e.currentTarget;
|
const imgElement = e.currentTarget;
|
||||||
imgElement.src = this.state.thumbUrl ?? this.state.contentUrl;
|
imgElement.src = url;
|
||||||
};
|
};
|
||||||
|
|
||||||
private clearError = (): void => {
|
private clearError = (): void => {
|
||||||
|
@ -285,7 +292,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||||
img.onerror = reject;
|
img.onerror = reject;
|
||||||
});
|
});
|
||||||
img.crossOrigin = "Anonymous"; // CORS allow canvas access
|
img.crossOrigin = "Anonymous"; // CORS allow canvas access
|
||||||
img.src = contentUrl;
|
img.src = contentUrl ?? "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await loadPromise;
|
await loadPromise;
|
||||||
|
@ -379,7 +386,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||||
|
|
||||||
protected messageContent(
|
protected messageContent(
|
||||||
contentUrl: string,
|
contentUrl: string,
|
||||||
thumbUrl: string,
|
thumbUrl: string | null,
|
||||||
content: IMediaEventContent,
|
content: IMediaEventContent,
|
||||||
forcedHeight?: number,
|
forcedHeight?: number,
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
|
@ -579,7 +586,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let contentUrl = this.state.contentUrl;
|
let contentUrl = this.state.contentUrl;
|
||||||
let thumbUrl: string | undefined;
|
let thumbUrl: string | null;
|
||||||
if (this.props.forExport) {
|
if (this.props.forExport) {
|
||||||
contentUrl = this.props.mxEvent.getContent().url ?? this.props.mxEvent.getContent().file?.url;
|
contentUrl = this.props.mxEvent.getContent().url ?? this.props.mxEvent.getContent().file?.url;
|
||||||
thumbUrl = contentUrl;
|
thumbUrl = contentUrl;
|
||||||
|
@ -589,7 +596,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
||||||
thumbUrl = this.state.thumbUrl ?? this.state.contentUrl;
|
thumbUrl = this.state.thumbUrl ?? this.state.contentUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
const thumbnail = this.messageContent(contentUrl, thumbUrl, content);
|
const thumbnail = contentUrl ? this.messageContent(contentUrl, thumbUrl, content) : undefined;
|
||||||
const fileBody = this.getFileBody();
|
const fileBody = this.getFileBody();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -36,7 +36,9 @@ export default class MImageReplyBody extends MImageBody {
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = this.props.mxEvent.getContent<IMediaEventContent>();
|
const content = this.props.mxEvent.getContent<IMediaEventContent>();
|
||||||
const thumbnail = this.messageContent(this.state.contentUrl, this.state.thumbUrl, content, FORCED_IMAGE_HEIGHT);
|
const thumbnail = this.state.contentUrl
|
||||||
|
? this.messageContent(this.state.contentUrl, this.state.thumbUrl, content, FORCED_IMAGE_HEIGHT)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return <div className="mx_MImageReplyBody">{thumbnail}</div>;
|
return <div className="mx_MImageReplyBody">{thumbnail}</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
|
||||||
const prevUrl = this.props.mxEvent.getPrevContent()["url"];
|
const prevUrl = this.props.mxEvent.getPrevContent()["url"];
|
||||||
const senderName = this.props.mxEvent.sender?.name || this.props.mxEvent.getSender();
|
const senderName = this.props.mxEvent.sender?.name || this.props.mxEvent.getSender();
|
||||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||||
|
if (!room) return null;
|
||||||
const widgetId = this.props.mxEvent.getStateKey();
|
const widgetId = this.props.mxEvent.getStateKey();
|
||||||
const widget = WidgetStore.instance.getRoom(room.roomId, true).widgets.find((w) => w.id === widgetId);
|
const widget = WidgetStore.instance.getRoom(room.roomId, true).widgets.find((w) => w.id === widgetId);
|
||||||
|
|
||||||
|
|
|
@ -114,16 +114,16 @@ export default class MKeyVerificationConclusion extends React.Component<IProps>
|
||||||
|
|
||||||
if (request.done) {
|
if (request.done) {
|
||||||
title = _t("You verified %(name)s", {
|
title = _t("You verified %(name)s", {
|
||||||
name: getNameForEventRoom(request.otherUserId, mxEvent.getRoomId()),
|
name: getNameForEventRoom(request.otherUserId, mxEvent.getRoomId()!),
|
||||||
});
|
});
|
||||||
} else if (request.cancelled) {
|
} else if (request.cancelled) {
|
||||||
const userId = request.cancellingUserId;
|
const userId = request.cancellingUserId;
|
||||||
if (userId === myUserId) {
|
if (userId === myUserId) {
|
||||||
title = _t("You cancelled verifying %(name)s", {
|
title = _t("You cancelled verifying %(name)s", {
|
||||||
name: getNameForEventRoom(request.otherUserId, mxEvent.getRoomId()),
|
name: getNameForEventRoom(request.otherUserId, mxEvent.getRoomId()!),
|
||||||
});
|
});
|
||||||
} else {
|
} else if (userId) {
|
||||||
title = _t("%(name)s cancelled verifying", { name: getNameForEventRoom(userId, mxEvent.getRoomId()) });
|
title = _t("%(name)s cancelled verifying", { name: getNameForEventRoom(userId, mxEvent.getRoomId()!) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ export default class MKeyVerificationConclusion extends React.Component<IProps>
|
||||||
<EventTileBubble
|
<EventTileBubble
|
||||||
className={classes}
|
className={classes}
|
||||||
title={title}
|
title={title}
|
||||||
subtitle={userLabelForEventRoom(request.otherUserId, mxEvent.getRoomId())}
|
subtitle={userLabelForEventRoom(request.otherUserId, mxEvent.getRoomId()!)}
|
||||||
timestamp={this.props.timestamp}
|
timestamp={this.props.timestamp}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { MatrixEvent, User } from "matrix-js-sdk/src/matrix";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { VerificationRequestEvent } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
import { VerificationRequestEvent } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||||
|
|
||||||
|
@ -48,8 +48,11 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private openRequest = (): void => {
|
private openRequest = (): void => {
|
||||||
|
let member: User | undefined;
|
||||||
const { verificationRequest } = this.props.mxEvent;
|
const { verificationRequest } = this.props.mxEvent;
|
||||||
const member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId);
|
if (verificationRequest) {
|
||||||
|
member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId) ?? undefined;
|
||||||
|
}
|
||||||
RightPanelStore.instance.setCards([
|
RightPanelStore.instance.setCards([
|
||||||
{ phase: RightPanelPhases.RoomSummary },
|
{ phase: RightPanelPhases.RoomSummary },
|
||||||
{ phase: RightPanelPhases.RoomMemberInfo, state: { member } },
|
{ phase: RightPanelPhases.RoomMemberInfo, state: { member } },
|
||||||
|
@ -90,14 +93,14 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
if (userId === myUserId) {
|
if (userId === myUserId) {
|
||||||
return _t("You accepted");
|
return _t("You accepted");
|
||||||
} else {
|
} else {
|
||||||
return _t("%(name)s accepted", { name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId()) });
|
return _t("%(name)s accepted", { name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId()!) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private cancelledLabel(userId: string): string {
|
private cancelledLabel(userId: string): string {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const myUserId = client.getUserId();
|
const myUserId = client.getUserId();
|
||||||
const { cancellationCode } = this.props.mxEvent.verificationRequest;
|
const cancellationCode = this.props.mxEvent.verificationRequest?.cancellationCode;
|
||||||
const declined = cancellationCode === "m.user";
|
const declined = cancellationCode === "m.user";
|
||||||
if (userId === myUserId) {
|
if (userId === myUserId) {
|
||||||
if (declined) {
|
if (declined) {
|
||||||
|
@ -107,9 +110,9 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (declined) {
|
if (declined) {
|
||||||
return _t("%(name)s declined", { name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId()) });
|
return _t("%(name)s declined", { name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId()!) });
|
||||||
} else {
|
} else {
|
||||||
return _t("%(name)s cancelled", { name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId()) });
|
return _t("%(name)s cancelled", { name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId()!) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +139,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
} else if (request.cancelled) {
|
} else if (request.cancelled) {
|
||||||
stateLabel = this.cancelledLabel(request.cancellingUserId);
|
stateLabel = this.cancelledLabel(request.cancellingUserId!);
|
||||||
} else if (request.accepting) {
|
} else if (request.accepting) {
|
||||||
stateLabel = _t("Accepting…");
|
stateLabel = _t("Accepting…");
|
||||||
} else if (request.declining) {
|
} else if (request.declining) {
|
||||||
|
@ -146,9 +149,9 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.initiatedByMe) {
|
if (!request.initiatedByMe) {
|
||||||
const name = getNameForEventRoom(request.requestingUserId, mxEvent.getRoomId());
|
const name = getNameForEventRoom(request.requestingUserId, mxEvent.getRoomId()!);
|
||||||
title = _t("%(name)s wants to verify", { name });
|
title = _t("%(name)s wants to verify", { name });
|
||||||
subtitle = userLabelForEventRoom(request.requestingUserId, mxEvent.getRoomId());
|
subtitle = userLabelForEventRoom(request.requestingUserId, mxEvent.getRoomId()!);
|
||||||
if (request.canAccept) {
|
if (request.canAccept) {
|
||||||
stateNode = (
|
stateNode = (
|
||||||
<div className="mx_cryptoEvent_buttons">
|
<div className="mx_cryptoEvent_buttons">
|
||||||
|
@ -164,7 +167,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
} else {
|
} else {
|
||||||
// request sent by us
|
// request sent by us
|
||||||
title = _t("You sent a verification request");
|
title = _t("You sent a verification request");
|
||||||
subtitle = userLabelForEventRoom(request.receivingUserId, mxEvent.getRoomId());
|
subtitle = userLabelForEventRoom(request.receivingUserId, mxEvent.getRoomId()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
|
|
|
@ -118,16 +118,17 @@ export function pollAlreadyHasVotes(mxEvent: MatrixEvent, getRelationsForEvent?:
|
||||||
}
|
}
|
||||||
|
|
||||||
export function launchPollEditor(mxEvent: MatrixEvent, getRelationsForEvent?: GetRelationsForEvent): void {
|
export function launchPollEditor(mxEvent: MatrixEvent, getRelationsForEvent?: GetRelationsForEvent): void {
|
||||||
|
const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId());
|
||||||
if (pollAlreadyHasVotes(mxEvent, getRelationsForEvent)) {
|
if (pollAlreadyHasVotes(mxEvent, getRelationsForEvent)) {
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
title: _t("Can't edit poll"),
|
title: _t("Can't edit poll"),
|
||||||
description: _t("Sorry, you can't edit a poll after votes have been cast."),
|
description: _t("Sorry, you can't edit a poll after votes have been cast."),
|
||||||
});
|
});
|
||||||
} else {
|
} else if (room) {
|
||||||
Modal.createDialog(
|
Modal.createDialog(
|
||||||
PollCreateDialog,
|
PollCreateDialog,
|
||||||
{
|
{
|
||||||
room: MatrixClientPeg.get().getRoom(mxEvent.getRoomId()),
|
room,
|
||||||
threadId: mxEvent.getThread()?.id,
|
threadId: mxEvent.getThread()?.id,
|
||||||
editingMxEvent: mxEvent,
|
editingMxEvent: mxEvent,
|
||||||
},
|
},
|
||||||
|
|
|
@ -111,7 +111,7 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
|
|
||||||
const pixels = decode(info[BLURHASH_FIELD], width, height);
|
const pixels = decode(info[BLURHASH_FIELD], width, height);
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext("2d")!;
|
||||||
const imgData = ctx.createImageData(width, height);
|
const imgData = ctx.createImageData(width, height);
|
||||||
imgData.data.set(pixels);
|
imgData.data.set(pixels);
|
||||||
ctx.putImageData(imgData, 0, 0);
|
ctx.putImageData(imgData, 0, 0);
|
||||||
|
@ -128,7 +128,7 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
||||||
image.onload = () => {
|
image.onload = () => {
|
||||||
this.setState({ posterLoading: false });
|
this.setState({ posterLoading: false });
|
||||||
};
|
};
|
||||||
image.src = media.thumbnailHttp;
|
image.src = media.thumbnailHttp!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,9 +185,9 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
|
||||||
const allowRender = localStorage.getItem(key) === "true";
|
const allowRender = localStorage.getItem(key) === "true";
|
||||||
|
|
||||||
if (!allowRender) {
|
if (!allowRender) {
|
||||||
const userDomain = this.props.mxEvent.getSender().split(":").slice(1).join(":");
|
const userDomain = this.props.mxEvent.getSender()?.split(":").slice(1).join(":");
|
||||||
const userBanned = Mjolnir.sharedInstance().isUserBanned(this.props.mxEvent.getSender());
|
const userBanned = Mjolnir.sharedInstance().isUserBanned(this.props.mxEvent.getSender()!);
|
||||||
const serverBanned = Mjolnir.sharedInstance().isServerBanned(userDomain);
|
const serverBanned = userDomain && Mjolnir.sharedInstance().isServerBanned(userDomain);
|
||||||
|
|
||||||
if (userBanned || serverBanned) {
|
if (userBanned || serverBanned) {
|
||||||
BodyType = MjolnirBody;
|
BodyType = MjolnirBody;
|
||||||
|
|
|
@ -34,8 +34,8 @@ const MAX_ITEMS_WHEN_LIMITED = 8;
|
||||||
const ReactButton: React.FC<IProps> = ({ mxEvent, reactions }) => {
|
const ReactButton: React.FC<IProps> = ({ mxEvent, reactions }) => {
|
||||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||||
|
|
||||||
let contextMenu;
|
let contextMenu: JSX.Element | undefined;
|
||||||
if (menuDisplayed) {
|
if (menuDisplayed && button.current) {
|
||||||
const buttonRect = button.current.getBoundingClientRect();
|
const buttonRect = button.current.getBoundingClientRect();
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu} managed={false}>
|
<ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu} managed={false}>
|
||||||
|
@ -73,7 +73,7 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
myReactions: MatrixEvent[];
|
myReactions: MatrixEvent[] | null;
|
||||||
showAll: boolean;
|
showAll: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,8 +147,9 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
|
||||||
if (!reactions) {
|
if (!reactions) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const userId = this.context.room.client.getUserId();
|
const userId = this.context.room?.client.getUserId();
|
||||||
const myReactions = reactions.getAnnotationsBySender()[userId];
|
if (!userId) return null;
|
||||||
|
const myReactions = reactions.getAnnotationsBySender()?.[userId];
|
||||||
if (!myReactions) {
|
if (!myReactions) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -171,19 +172,17 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
let items = reactions
|
let items = reactions
|
||||||
.getSortedAnnotationsByKey()
|
.getSortedAnnotationsByKey()
|
||||||
.map(([content, events]) => {
|
?.map(([content, events]) => {
|
||||||
const count = events.size;
|
const count = events.size;
|
||||||
if (!count) {
|
if (!count) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const myReactionEvent =
|
const myReactionEvent = myReactions?.find((mxEvent) => {
|
||||||
myReactions &&
|
if (mxEvent.isRedacted()) {
|
||||||
myReactions.find((mxEvent) => {
|
return false;
|
||||||
if (mxEvent.isRedacted()) {
|
}
|
||||||
return false;
|
return mxEvent.getRelation()?.key === content;
|
||||||
}
|
});
|
||||||
return mxEvent.getRelation().key === content;
|
|
||||||
});
|
|
||||||
return (
|
return (
|
||||||
<ReactionsRowButton
|
<ReactionsRowButton
|
||||||
key={content}
|
key={content}
|
||||||
|
@ -201,7 +200,7 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
|
||||||
})
|
})
|
||||||
.filter((item) => !!item);
|
.filter((item) => !!item);
|
||||||
|
|
||||||
if (!items.length) return null;
|
if (!items?.length) return null;
|
||||||
|
|
||||||
// Show the first MAX_ITEMS if there are MAX_ITEMS + 1 or more items.
|
// Show the first MAX_ITEMS if there are MAX_ITEMS + 1 or more items.
|
||||||
// The "+ 1" ensure that the "show all" reveals something that takes up
|
// The "+ 1" ensure that the "show all" reveals something that takes up
|
||||||
|
@ -216,7 +215,7 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let addReactionButton: JSX.Element;
|
let addReactionButton: JSX.Element | undefined;
|
||||||
if (this.context.canReact) {
|
if (this.context.canReact) {
|
||||||
addReactionButton = <ReactButton mxEvent={mxEvent} reactions={reactions} />;
|
addReactionButton = <ReactButton mxEvent={mxEvent} reactions={reactions} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import dis from "../../../dispatcher/dispatcher";
|
||||||
import ReactionsRowButtonTooltip from "./ReactionsRowButtonTooltip";
|
import ReactionsRowButtonTooltip from "./ReactionsRowButtonTooltip";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
// The event we're displaying reactions for
|
// The event we're displaying reactions for
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -57,9 +56,9 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
|
||||||
public onClick = (): void => {
|
public onClick = (): void => {
|
||||||
const { mxEvent, myReactionEvent, content } = this.props;
|
const { mxEvent, myReactionEvent, content } = this.props;
|
||||||
if (myReactionEvent) {
|
if (myReactionEvent) {
|
||||||
this.context.redactEvent(mxEvent.getRoomId(), myReactionEvent.getId());
|
this.context.redactEvent(mxEvent.getRoomId()!, myReactionEvent.getId());
|
||||||
} else {
|
} else {
|
||||||
this.context.sendEvent(mxEvent.getRoomId(), "m.reaction", {
|
this.context.sendEvent(mxEvent.getRoomId()!, "m.reaction", {
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
rel_type: "m.annotation",
|
rel_type: "m.annotation",
|
||||||
event_id: mxEvent.getId(),
|
event_id: mxEvent.getId(),
|
||||||
|
@ -110,8 +109,8 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
|
||||||
if (room) {
|
if (room) {
|
||||||
const senders: string[] = [];
|
const senders: string[] = [];
|
||||||
for (const reactionEvent of reactionEvents) {
|
for (const reactionEvent of reactionEvents) {
|
||||||
const member = room.getMember(reactionEvent.getSender());
|
const member = room.getMember(reactionEvent.getSender()!);
|
||||||
senders.push(member?.name || reactionEvent.getSender());
|
senders.push(member?.name || reactionEvent.getSender()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
const reactors = formatCommaSeparatedList(senders, 6);
|
const reactors = formatCommaSeparatedList(senders, 6);
|
||||||
|
|
|
@ -22,7 +22,6 @@ import { _t } from "../../../languageHandler";
|
||||||
import { formatCommaSeparatedList } from "../../../utils/FormattingUtils";
|
import { formatCommaSeparatedList } from "../../../utils/FormattingUtils";
|
||||||
import Tooltip from "../elements/Tooltip";
|
import Tooltip from "../elements/Tooltip";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
// The event we're displaying reactions for
|
// The event we're displaying reactions for
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -45,8 +44,8 @@ export default class ReactionsRowButtonTooltip extends React.PureComponent<IProp
|
||||||
if (room) {
|
if (room) {
|
||||||
const senders: string[] = [];
|
const senders: string[] = [];
|
||||||
for (const reactionEvent of reactionEvents) {
|
for (const reactionEvent of reactionEvents) {
|
||||||
const member = room.getMember(reactionEvent.getSender());
|
const member = room.getMember(reactionEvent.getSender()!);
|
||||||
const name = member ? member.name : reactionEvent.getSender();
|
const name = member?.name ?? reactionEvent.getSender()!;
|
||||||
senders.push(name);
|
senders.push(name);
|
||||||
}
|
}
|
||||||
const shortName = unicodeToShortcode(content);
|
const shortName = unicodeToShortcode(content);
|
||||||
|
|
|
@ -23,7 +23,6 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { formatFullDate } from "../../../DateUtils";
|
import { formatFullDate } from "../../../DateUtils";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import { IBodyProps } from "./IBodyProps";
|
import { IBodyProps } from "./IBodyProps";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
}
|
}
|
||||||
|
@ -40,8 +39,10 @@ const RedactedBody = React.forwardRef<any, IProps | IBodyProps>(({ mxEvent }, re
|
||||||
}
|
}
|
||||||
|
|
||||||
const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps");
|
const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps");
|
||||||
const fullDate = formatFullDate(new Date(unsigned.redacted_because.origin_server_ts), showTwelveHour);
|
const fullDate = unsigned.redacted_because
|
||||||
const titleText = _t("Message deleted on %(date)s", { date: fullDate });
|
? formatFullDate(new Date(unsigned.redacted_because.origin_server_ts), showTwelveHour)
|
||||||
|
: undefined;
|
||||||
|
const titleText = fullDate ? _t("Message deleted on %(date)s", { date: fullDate }) : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="mx_RedactedBody" ref={ref} title={titleText}>
|
<span className="mx_RedactedBody" ref={ref} title={titleText}>
|
||||||
|
|
|
@ -26,7 +26,6 @@ import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import { mediaFromMxc } from "../../../customisations/Media";
|
import { mediaFromMxc } from "../../../customisations/Media";
|
||||||
import RoomAvatar from "../avatars/RoomAvatar";
|
import RoomAvatar from "../avatars/RoomAvatar";
|
||||||
import ImageView from "../elements/ImageView";
|
import ImageView from "../elements/ImageView";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -37,6 +36,7 @@ export default class RoomAvatarEvent extends React.Component<IProps> {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const ev = this.props.mxEvent;
|
const ev = this.props.mxEvent;
|
||||||
const httpUrl = mediaFromMxc(ev.getContent().url).srcHttp;
|
const httpUrl = mediaFromMxc(ev.getContent().url).srcHttp;
|
||||||
|
if (!httpUrl) return;
|
||||||
|
|
||||||
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
||||||
const text = _t("%(senderDisplayName)s changed the avatar for %(roomName)s", {
|
const text = _t("%(senderDisplayName)s changed the avatar for %(roomName)s", {
|
||||||
|
@ -48,7 +48,7 @@ export default class RoomAvatarEvent extends React.Component<IProps> {
|
||||||
src: httpUrl,
|
src: httpUrl,
|
||||||
name: text,
|
name: text,
|
||||||
};
|
};
|
||||||
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true);
|
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", undefined, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
|
|
|
@ -34,7 +34,6 @@ import { isPermalinkHost, tryTransformPermalinkToLocalHref } from "../../../util
|
||||||
import { copyPlaintext } from "../../../utils/strings";
|
import { copyPlaintext } from "../../../utils/strings";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import UIStore from "../../../stores/UIStore";
|
import UIStore from "../../../stores/UIStore";
|
||||||
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
|
|
||||||
import { Action } from "../../../dispatcher/actions";
|
import { Action } from "../../../dispatcher/actions";
|
||||||
import GenericTextContextMenu from "../context_menus/GenericTextContextMenu";
|
import GenericTextContextMenu from "../context_menus/GenericTextContextMenu";
|
||||||
import Spoiler from "../elements/Spoiler";
|
import Spoiler from "../elements/Spoiler";
|
||||||
|
@ -109,7 +108,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
for (let i = 0; i < pres.length; i++) {
|
for (let i = 0; i < pres.length; i++) {
|
||||||
// If there already is a div wrapping the codeblock we want to skip this.
|
// If there already is a div wrapping the codeblock we want to skip this.
|
||||||
// This happens after the codeblock was edited.
|
// This happens after the codeblock was edited.
|
||||||
if (pres[i].parentElement.className == "mx_EventTile_pre_container") continue;
|
if (pres[i].parentElement?.className == "mx_EventTile_pre_container") continue;
|
||||||
// Add code element if it's missing since we depend on it
|
// Add code element if it's missing since we depend on it
|
||||||
if (pres[i].getElementsByTagName("code").length == 0) {
|
if (pres[i].getElementsByTagName("code").length == 0) {
|
||||||
this.addCodeElement(pres[i]);
|
this.addCodeElement(pres[i]);
|
||||||
|
@ -189,8 +188,8 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
if (expansionButtonExists.length > 0) button.className += "mx_EventTile_buttonBottom";
|
if (expansionButtonExists.length > 0) button.className += "mx_EventTile_buttonBottom";
|
||||||
|
|
||||||
button.onclick = async (): Promise<void> => {
|
button.onclick = async (): Promise<void> => {
|
||||||
const copyCode = button.parentElement.getElementsByTagName("code")[0];
|
const copyCode = button.parentElement?.getElementsByTagName("code")[0];
|
||||||
const successful = await copyPlaintext(copyCode.textContent);
|
const successful = copyCode?.textContent ? await copyPlaintext(copyCode.textContent) : false;
|
||||||
|
|
||||||
const buttonRect = button.getBoundingClientRect();
|
const buttonRect = button.getBoundingClientRect();
|
||||||
const { close } = ContextMenu.createMenu(GenericTextContextMenu, {
|
const { close } = ContextMenu.createMenu(GenericTextContextMenu, {
|
||||||
|
@ -209,7 +208,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
div.className = "mx_EventTile_pre_container";
|
div.className = "mx_EventTile_pre_container";
|
||||||
|
|
||||||
// Insert containing div in place of <pre> block
|
// Insert containing div in place of <pre> block
|
||||||
pre.parentNode.replaceChild(div, pre);
|
pre.parentNode?.replaceChild(div, pre);
|
||||||
// Append <pre> block and copy button to container
|
// Append <pre> block and copy button to container
|
||||||
div.appendChild(pre);
|
div.appendChild(pre);
|
||||||
|
|
||||||
|
@ -238,7 +237,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private highlightCode(code: HTMLElement): void {
|
private highlightCode(code: HTMLElement): void {
|
||||||
if (code.textContent.length > MAX_HIGHLIGHT_LENGTH) {
|
if (code.textContent && code.textContent.length > MAX_HIGHLIGHT_LENGTH) {
|
||||||
console.log(
|
console.log(
|
||||||
"Code block is bigger than highlight limit (" +
|
"Code block is bigger than highlight limit (" +
|
||||||
code.textContent.length +
|
code.textContent.length +
|
||||||
|
@ -265,7 +264,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
// We don't use highlightElement here because we can't force language detection
|
// We don't use highlightElement here because we can't force language detection
|
||||||
// off. It should use the one we've found in the CSS class but we'd rather pass
|
// off. It should use the one we've found in the CSS class but we'd rather pass
|
||||||
// it in explicitly to make sure.
|
// it in explicitly to make sure.
|
||||||
code.innerHTML = highlight.highlight(code.textContent, { language: advertisedLang }).value;
|
code.innerHTML = highlight.highlight(code.textContent ?? "", { language: advertisedLang }).value;
|
||||||
} else if (
|
} else if (
|
||||||
SettingsStore.getValue("enableSyntaxHighlightLanguageDetection") &&
|
SettingsStore.getValue("enableSyntaxHighlightLanguageDetection") &&
|
||||||
code.parentElement instanceof HTMLPreElement
|
code.parentElement instanceof HTMLPreElement
|
||||||
|
@ -277,7 +276,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
// work on the DOM with highlightElement because that also adds CSS
|
// work on the DOM with highlightElement because that also adds CSS
|
||||||
// classes to the pre/code element that we don't want (the CSS
|
// classes to the pre/code element that we don't want (the CSS
|
||||||
// conflicts with our own).
|
// conflicts with our own).
|
||||||
code.innerHTML = highlight.highlightAuto(code.textContent).value;
|
code.innerHTML = highlight.highlightAuto(code.textContent ?? "").value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,7 +316,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
private calculateUrlPreview(): void {
|
private calculateUrlPreview(): void {
|
||||||
//console.info("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
|
//console.info("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
|
||||||
|
|
||||||
if (this.props.showUrlPreview) {
|
if (this.props.showUrlPreview && this.contentRef.current) {
|
||||||
// pass only the first child which is the event tile otherwise this recurses on edited events
|
// pass only the first child which is the event tile otherwise this recurses on edited events
|
||||||
let links = this.findLinks([this.contentRef.current]);
|
let links = this.findLinks([this.contentRef.current]);
|
||||||
if (links.length) {
|
if (links.length) {
|
||||||
|
@ -347,7 +346,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
const spoiler = <Spoiler reason={reason} contentHtml={node.outerHTML} />;
|
const spoiler = <Spoiler reason={reason} contentHtml={node.outerHTML} />;
|
||||||
|
|
||||||
ReactDOM.render(spoiler, spoilerContainer);
|
ReactDOM.render(spoiler, spoilerContainer);
|
||||||
node.parentNode.replaceChild(spoilerContainer, node);
|
node.parentNode?.replaceChild(spoilerContainer, node);
|
||||||
|
|
||||||
node = spoilerContainer;
|
node = spoilerContainer;
|
||||||
}
|
}
|
||||||
|
@ -395,12 +394,12 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = node.getAttribute("href");
|
const url = node.getAttribute("href");
|
||||||
const host = url.match(/^https?:\/\/(.*?)(\/|$)/)[1];
|
const host = url?.match(/^https?:\/\/(.*?)(\/|$)/)?.[1];
|
||||||
|
|
||||||
// never preview permalinks (if anything we should give a smart
|
// never preview permalinks (if anything we should give a smart
|
||||||
// preview of the room/user they point to: nobody needs to be reminded
|
// preview of the room/user they point to: nobody needs to be reminded
|
||||||
// what the matrix.to site looks like).
|
// what the matrix.to site looks like).
|
||||||
if (isPermalinkHost(host)) return false;
|
if (!host || isPermalinkHost(host)) return false;
|
||||||
|
|
||||||
if (node.textContent?.toLowerCase().trim().startsWith(host.toLowerCase())) {
|
if (node.textContent?.toLowerCase().trim().startsWith(host.toLowerCase())) {
|
||||||
// it's a "foo.pl" style link
|
// it's a "foo.pl" style link
|
||||||
|
@ -422,7 +421,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
|
|
||||||
private onEmoteSenderClick = (): void => {
|
private onEmoteSenderClick = (): void => {
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
dis.dispatch<ComposerInsertPayload>({
|
dis.dispatch({
|
||||||
action: Action.ComposerInsert,
|
action: Action.ComposerInsert,
|
||||||
userId: mxEvent.getSender(),
|
userId: mxEvent.getSender(),
|
||||||
timelineRenderingType: this.context.timelineRenderingType,
|
timelineRenderingType: this.context.timelineRenderingType,
|
||||||
|
@ -482,10 +481,10 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
|
|
||||||
// Go fetch a scalar token
|
// Go fetch a scalar token
|
||||||
const integrationManager = managers.getPrimaryManager();
|
const integrationManager = managers.getPrimaryManager();
|
||||||
const scalarClient = integrationManager.getScalarClient();
|
const scalarClient = integrationManager?.getScalarClient();
|
||||||
scalarClient.connect().then(() => {
|
scalarClient?.connect().then(() => {
|
||||||
const completeUrl = scalarClient.getStarterLink(starterLink);
|
const completeUrl = scalarClient.getStarterLink(starterLink);
|
||||||
const integrationsUrl = integrationManager.uiUrl;
|
const integrationsUrl = integrationManager!.uiUrl;
|
||||||
Modal.createDialog(QuestionDialog, {
|
Modal.createDialog(QuestionDialog, {
|
||||||
title: _t("Add an Integration"),
|
title: _t("Add an Integration"),
|
||||||
description: (
|
description: (
|
||||||
|
@ -508,7 +507,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
const left = (window.screen.width - width) / 2;
|
const left = (window.screen.width - width) / 2;
|
||||||
const top = (window.screen.height - height) / 2;
|
const top = (window.screen.height - height) / 2;
|
||||||
const features = `height=${height}, width=${width}, top=${top}, left=${left},`;
|
const features = `height=${height}, width=${width}, top=${top}, left=${left},`;
|
||||||
const wnd = window.open(completeUrl, "_blank", features);
|
const wnd = window.open(completeUrl, "_blank", features)!;
|
||||||
wnd.opener = null;
|
wnd.opener = null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -35,7 +35,7 @@ export enum ImageSize {
|
||||||
* @param {number} maxHeight Overrides the default height limit
|
* @param {number} maxHeight Overrides the default height limit
|
||||||
* @returns {Dimensions} The suggested maximum dimensions for the image
|
* @returns {Dimensions} The suggested maximum dimensions for the image
|
||||||
*/
|
*/
|
||||||
export function suggestedSize(size: ImageSize, contentSize: Dimensions, maxHeight?: number): Dimensions {
|
export function suggestedSize(size: ImageSize, contentSize: Dimensions, maxHeight?: number): Required<Dimensions> {
|
||||||
const aspectRatio = contentSize.w! / contentSize.h!;
|
const aspectRatio = contentSize.w! / contentSize.h!;
|
||||||
const portrait = aspectRatio < 1;
|
const portrait = aspectRatio < 1;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type getIframeFn = () => HTMLIFrameElement; // eslint-disable-line @typescript-eslint/naming-convention
|
export type GetIframeFn = () => HTMLIFrameElement | null;
|
||||||
|
|
||||||
export const DEFAULT_STYLES = {
|
export const DEFAULT_STYLES = {
|
||||||
imgSrc: "",
|
imgSrc: "",
|
||||||
|
@ -75,7 +75,7 @@ export class FileDownloader {
|
||||||
* @param iframeFn Function to get a pre-configured iframe. Set to null to have the downloader
|
* @param iframeFn Function to get a pre-configured iframe. Set to null to have the downloader
|
||||||
* use a generic, hidden, iframe.
|
* use a generic, hidden, iframe.
|
||||||
*/
|
*/
|
||||||
public constructor(private iframeFn?: getIframeFn) {}
|
public constructor(private iframeFn?: GetIframeFn) {}
|
||||||
|
|
||||||
private get iframe(): HTMLIFrameElement {
|
private get iframe(): HTMLIFrameElement {
|
||||||
const iframe = this.iframeFn?.();
|
const iframe = this.iframeFn?.();
|
||||||
|
|
Loading…
Reference in a new issue