diff --git a/src/DateUtils.ts b/src/DateUtils.ts index 5973a7c5f2e1..c1aa69aacd6d 100644 --- a/src/DateUtils.ts +++ b/src/DateUtils.ts @@ -175,7 +175,7 @@ function withinCurrentYear(prevDate: Date, nextDate: Date): boolean { return prevDate.getFullYear() === nextDate.getFullYear(); } -export function wantsDateSeparator(prevEventDate: Date, nextEventDate: Date): boolean { +export function wantsDateSeparator(prevEventDate: Date | undefined, nextEventDate: Date | undefined): boolean { if (!nextEventDate || !prevEventDate) { return false; } diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 98e8f79ec722..2dd432cb9282 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -72,7 +72,7 @@ const groupedStateEvents = [ // check if there is a previous event and it has the same sender as this event // and the types are the same/is in continuedTypes and the time between them is <= CONTINUATION_MAX_INTERVAL export function shouldFormContinuation( - prevEvent: MatrixEvent, + prevEvent: MatrixEvent | null, mxEvent: MatrixEvent, showHiddenEvents: boolean, threadsEnabled: boolean, @@ -821,7 +821,7 @@ export default class MessagePanel extends React.Component { // here. return !this.props.canBackPaginate; } - return wantsDateSeparator(prevEvent.getDate(), nextEventDate); + return wantsDateSeparator(prevEvent.getDate() || undefined, nextEventDate); } // Get a list of read receipts that should be shown next to this event diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.tsx b/src/components/views/dialogs/MessageEditHistoryDialog.tsx index 943e7f58d2d6..8775b4eb5c33 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.tsx +++ b/src/components/views/dialogs/MessageEditHistoryDialog.tsx @@ -130,7 +130,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent { - if (!lastEvent || wantsDateSeparator(lastEvent.getDate(), e.getDate())) { + if (!lastEvent || wantsDateSeparator(lastEvent.getDate() || undefined, e.getDate() || undefined)) { nodes.push(
  • diff --git a/src/components/views/rooms/SearchResultTile.tsx b/src/components/views/rooms/SearchResultTile.tsx index 067cbaee3836..3ec68b989f4c 100644 --- a/src/components/views/rooms/SearchResultTile.tsx +++ b/src/components/views/rooms/SearchResultTile.tsx @@ -84,7 +84,7 @@ export default class SearchResultTile extends React.Component { // is this a continuation of the previous message? const continuation = prevEv && - !wantsDateSeparator(prevEv.getDate(), mxEv.getDate()) && + !wantsDateSeparator(prevEv.getDate() || undefined, mxEv.getDate() || undefined) && shouldFormContinuation( prevEv, mxEv, @@ -96,7 +96,10 @@ export default class SearchResultTile extends React.Component { let lastInSection = true; const nextEv = timeline[j + 1]; if (nextEv) { - const willWantDateSeparator = wantsDateSeparator(mxEv.getDate(), nextEv.getDate()); + const willWantDateSeparator = wantsDateSeparator( + mxEv.getDate() || undefined, + nextEv.getDate() || undefined, + ); lastInSection = willWantDateSeparator || mxEv.getSender() !== nextEv.getSender() || diff --git a/src/utils/exportUtils/HtmlExport.tsx b/src/utils/exportUtils/HtmlExport.tsx index 6b4240375c6a..8232a15dceb2 100644 --- a/src/utils/exportUtils/HtmlExport.tsx +++ b/src/utils/exportUtils/HtmlExport.tsx @@ -1,5 +1,5 @@ /* -Copyright 2021 The Matrix.org Foundation C.I.C. +Copyright 2021, 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -66,7 +66,7 @@ export default class HTMLExporter extends Exporter { } protected async getRoomAvatar(): Promise { - let blob: Blob; + let blob: Blob | undefined = undefined; const avatarUrl = Avatar.avatarUrlForRoom(this.room, 32, 32, "crop"); const avatarPath = "room.png"; if (avatarUrl) { @@ -85,7 +85,7 @@ export default class HTMLExporter extends Exporter { height={32} name={this.room.name} title={this.room.name} - url={blob ? avatarPath : null} + url={blob ? avatarPath : ""} resizeMethod="crop" /> ); @@ -96,9 +96,9 @@ export default class HTMLExporter extends Exporter { const roomAvatar = await this.getRoomAvatar(); const exportDate = formatFullDateNoDayNoTime(new Date()); const creator = this.room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender(); - const creatorName = this.room?.getMember(creator)?.rawDisplayName || creator; - const exporter = this.client.getUserId(); - const exporterName = this.room?.getMember(exporter)?.rawDisplayName; + const creatorName = (creator ? this.room.getMember(creator)?.rawDisplayName : creator) || creator; + const exporter = this.client.getUserId()!; + const exporterName = this.room.getMember(exporter)?.rawDisplayName; const topic = this.room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic || ""; const createdText = _t("%(creatorName)s created this room.", { creatorName, @@ -217,20 +217,19 @@ export default class HTMLExporter extends Exporter { `; } - protected getAvatarURL(event: MatrixEvent): string { + protected getAvatarURL(event: MatrixEvent): string | undefined { const member = event.sender; - return ( - member.getMxcAvatarUrl() && mediaFromMxc(member.getMxcAvatarUrl()).getThumbnailOfSourceHttp(30, 30, "crop") - ); + const avatarUrl = member?.getMxcAvatarUrl(); + return avatarUrl ? mediaFromMxc(avatarUrl).getThumbnailOfSourceHttp(30, 30, "crop") : undefined; } protected async saveAvatarIfNeeded(event: MatrixEvent): Promise { - const member = event.sender; + const member = event.sender!; if (!this.avatars.has(member.userId)) { try { const avatarUrl = this.getAvatarURL(event); this.avatars.set(member.userId, true); - const image = await fetch(avatarUrl); + const image = await fetch(avatarUrl!); const blob = await image.blob(); this.addFile(`users/${member.userId.replace(/:/g, "-")}.png`, blob); } catch (err) { @@ -243,15 +242,15 @@ export default class HTMLExporter extends Exporter { const ts = event.getTs(); const dateSeparator = (
  • - +
  • ); return renderToStaticMarkup(dateSeparator); } - protected async needsDateSeparator(event: MatrixEvent, prevEvent: MatrixEvent): Promise { - if (prevEvent == null) return true; - return wantsDateSeparator(prevEvent.getDate(), event.getDate()); + protected async needsDateSeparator(event: MatrixEvent, prevEvent: MatrixEvent | null): Promise { + if (!prevEvent) return true; + return wantsDateSeparator(prevEvent.getDate() || undefined, event.getDate() || undefined); } public getEventTile(mxEv: MatrixEvent, continuation: boolean): JSX.Element { @@ -264,9 +263,7 @@ export default class HTMLExporter extends Exporter { isRedacted={mxEv.isRedacted()} replacingEventId={mxEv.replacingEventId()} forExport={true} - readReceipts={null} alwaysShowTimestamps={true} - readReceiptMap={null} showUrlPreview={false} checkUnmounting={() => false} isTwelveHour={false} @@ -275,7 +272,6 @@ export default class HTMLExporter extends Exporter { permalinkCreator={this.permalinkCreator} lastSuccessful={false} isSelectedEvent={false} - getRelationsForEvent={null} showReactions={false} layout={Layout.Group} showReadReceipts={false} @@ -286,7 +282,8 @@ export default class HTMLExporter extends Exporter { } protected async getEventTileMarkup(mxEv: MatrixEvent, continuation: boolean, filePath?: string): Promise { - const hasAvatar = !!this.getAvatarURL(mxEv); + const avatarUrl = this.getAvatarURL(mxEv); + const hasAvatar = !!avatarUrl; if (hasAvatar) await this.saveAvatarIfNeeded(mxEv); const EventTile = this.getEventTile(mxEv, continuation); let eventTileMarkup: string; @@ -312,8 +309,8 @@ export default class HTMLExporter extends Exporter { eventTileMarkup = eventTileMarkup.replace(/.*?<\/span>/, ""); if (hasAvatar) { eventTileMarkup = eventTileMarkup.replace( - encodeURI(this.getAvatarURL(mxEv)).replace(/&/g, "&"), - `users/${mxEv.sender.userId.replace(/:/g, "-")}.png`, + encodeURI(avatarUrl).replace(/&/g, "&"), + `users/${mxEv.sender!.userId.replace(/:/g, "-")}.png`, ); } return eventTileMarkup; @@ -403,7 +400,7 @@ export default class HTMLExporter extends Exporter { if (this.cancelled) return this.cleanUp(); if (!haveRendererForEvent(event, false)) continue; - content += this.needsDateSeparator(event, prevEvent) ? this.getDateSeparator(event) : ""; + content += (await this.needsDateSeparator(event, prevEvent)) ? this.getDateSeparator(event) : ""; const shouldBeJoined = !this.needsDateSeparator(event, prevEvent) && shouldFormContinuation(prevEvent, event, false, this.threadsEnabled);