diff --git a/res/css/views/rooms/_EventBubbleTile.scss b/res/css/views/rooms/_EventBubbleTile.scss index b8ff41fcc0..7667b7e556 100644 --- a/res/css/views/rooms/_EventBubbleTile.scss +++ b/res/css/views/rooms/_EventBubbleTile.scss @@ -215,13 +215,23 @@ limitations under the License. } //noinspection CssReplaceWithShorthandSafely - .mx_MImageBody .mx_MImageBody_thumbnail { - // Note: This is intentionally not compressed because the browser gets confused - // when it is all combined. We're effectively unsetting the border radius then - // setting the two corners we care about manually. - border-radius: unset; - border-top-left-radius: var(--cornerRadius); - border-top-right-radius: var(--cornerRadius); + .mx_MImageBody { + width: 100%; + height: 100%; + + //noinspection CssReplaceWithShorthandSafely + .mx_MImageBody_thumbnail { + // Note: This is intentionally not compressed because the browser gets confused + // when it is all combined. We're effectively unsetting the border radius then + // setting the two corners we care about manually. + border-radius: unset; + border-top-left-radius: var(--cornerRadius); + border-top-right-radius: var(--cornerRadius); + + &.mx_MImageBody_thumbnail--blurhash { + position: unset; + } + } } .mx_EventTile_e2eIcon { @@ -434,7 +444,6 @@ limitations under the License. } &[aria-expanded=true] { text-align: right; - margin-right: 100px; } } @@ -457,11 +466,26 @@ limitations under the License. padding: 0 49px; } +// ideally we'd use display=contents here for the layout to all work regardless of the *ELS but +// that breaks ScrollPanel's reliance upon offsetTop so we have to have a bit more finesse. .mx_EventListSummary[data-expanded=true][data-layout=bubble] { - display: contents; + margin: 0; .mx_EventTile { padding: 2px 0; + margin-right: 0; + + .mx_MessageActionBar { + right: 127px; // align with that of right-column bubbles + } + + .mx_EventTile_readAvatars { + right: -18px; // match alignment to RRs of chat bubbles + } + + &::before { + right: 0; // match alignment of the hover background to that of chat bubbles + } } } diff --git a/src/components/structures/ScrollPanel.tsx b/src/components/structures/ScrollPanel.tsx index e58a60b2aa..d2b386f653 100644 --- a/src/components/structures/ScrollPanel.tsx +++ b/src/components/structures/ScrollPanel.tsx @@ -89,7 +89,7 @@ interface IProps { * The promise should resolve to true if there is more data to be * retrieved in this direction (in which case onFillRequest may be * called again immediately), or false if there is no more data in this - * directon (at this time) - which will stop the pagination cycle until + * direction (at this time) - which will stop the pagination cycle until * the user scrolls again. */ onFillRequest?(backwards: boolean): Promise; @@ -683,7 +683,7 @@ export default class ScrollPanel extends React.Component { return; } const scrollToken = node.dataset.scrollTokens.split(',')[0]; - debuglog("saving anchored scroll state to message", node && node.innerText, scrollToken); + debuglog("saving anchored scroll state to message", node.innerText, scrollToken); const bottomOffset = this.topFromBottom(node); this.scrollState = { stuckAtBottom: false, @@ -791,17 +791,16 @@ export default class ScrollPanel extends React.Component { const scrollState = this.scrollState; const trackedNode = scrollState.trackedNode; - if (!trackedNode || !trackedNode.parentElement) { - let node; + if (!trackedNode?.parentElement) { + let node: HTMLElement; const messages = this.itemlist.current.children; const scrollToken = scrollState.trackedScrollToken; - for (let i = messages.length-1; i >= 0; --i) { + for (let i = messages.length - 1; i >= 0; --i) { const m = messages[i] as HTMLElement; // 'data-scroll-tokens' is a DOMString of comma-separated scroll tokens // There might only be one scroll token - if (m.dataset.scrollTokens && - m.dataset.scrollTokens.split(',').indexOf(scrollToken) !== -1) { + if (m.dataset.scrollTokens?.split(',').includes(scrollToken)) { node = m; break; } diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 1f93fc89f7..f44125964b 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -63,7 +63,7 @@ const DEBUG = false; let debuglog = function(...s: any[]) {}; if (DEBUG) { // using bind means that we get to keep useful line numbers in the console - debuglog = logger.log.bind(console); + debuglog = logger.log.bind(console, "TimelinePanel debuglog:"); } interface IProps { @@ -240,7 +240,7 @@ class TimelinePanel extends React.Component { constructor(props, context) { super(props, context); - debuglog("TimelinePanel: mounting"); + debuglog("mounting"); // XXX: we could track RM per TimelineSet rather than per Room. // but for now we just do it per room for simplicity. @@ -369,7 +369,7 @@ class TimelinePanel extends React.Component { private onMessageListUnfillRequest = (backwards: boolean, scrollToken: string): void => { // If backwards, unpaginate from the back (i.e. the start of the timeline) const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS; - debuglog("TimelinePanel: unpaginating events in direction", dir); + debuglog("unpaginating events in direction", dir); // All tiles are inserted by MessagePanel to have a scrollToken === eventId, and // this particular event should be the first or last to be unpaginated. @@ -384,7 +384,7 @@ class TimelinePanel extends React.Component { const count = backwards ? marker + 1 : this.state.events.length - marker; if (count > 0) { - debuglog("TimelinePanel: Unpaginating", count, "in direction", dir); + debuglog("Unpaginating", count, "in direction", dir); this.timelineWindow.unpaginate(count, backwards); const { events, liveEvents, firstVisibleEventIndex } = this.getEvents(); @@ -425,28 +425,28 @@ class TimelinePanel extends React.Component { const paginatingKey = backwards ? 'backPaginating' : 'forwardPaginating'; if (!this.state[canPaginateKey]) { - debuglog("TimelinePanel: have given up", dir, "paginating this timeline"); + debuglog("have given up", dir, "paginating this timeline"); return Promise.resolve(false); } if (!this.timelineWindow.canPaginate(dir)) { - debuglog("TimelinePanel: can't", dir, "paginate any further"); + debuglog("can't", dir, "paginate any further"); this.setState({ [canPaginateKey]: false }); return Promise.resolve(false); } if (backwards && this.state.firstVisibleEventIndex !== 0) { - debuglog("TimelinePanel: won't", dir, "paginate past first visible event"); + debuglog("won't", dir, "paginate past first visible event"); return Promise.resolve(false); } - debuglog("TimelinePanel: Initiating paginate; backwards:"+backwards); + debuglog("Initiating paginate; backwards:"+backwards); this.setState({ [paginatingKey]: true }); return this.onPaginationRequest(this.timelineWindow, dir, PAGINATE_SIZE).then((r) => { if (this.unmounted) { return; } - debuglog("TimelinePanel: paginate complete backwards:"+backwards+"; success:"+r); + debuglog("paginate complete backwards:"+backwards+"; success:"+r); const { events, liveEvents, firstVisibleEventIndex } = this.getEvents(); const newState: Partial = { @@ -463,7 +463,7 @@ class TimelinePanel extends React.Component { const canPaginateOtherWayKey = backwards ? 'canForwardPaginate' : 'canBackPaginate'; if (!this.state[canPaginateOtherWayKey] && this.timelineWindow.canPaginate(otherDirection)) { - debuglog('TimelinePanel: can now', otherDirection, 'paginate again'); + debuglog('can now', otherDirection, 'paginate again'); newState[canPaginateOtherWayKey] = true; } @@ -833,7 +833,7 @@ class TimelinePanel extends React.Component { const roomId = this.props.timelineSet.room.roomId; const hiddenRR = SettingsStore.getValue("feature_hidden_read_receipts", roomId); - debuglog('TimelinePanel: Sending Read Markers for ', + debuglog('Sending Read Markers for ', this.props.timelineSet.room.roomId, 'rm', this.state.readMarkerEventId, lastReadEvent ? 'rr ' + lastReadEvent.getId() : '', diff --git a/src/components/views/messages/MImageBody.tsx b/src/components/views/messages/MImageBody.tsx index 86e895090d..809b44d06b 100644 --- a/src/components/views/messages/MImageBody.tsx +++ b/src/components/views/messages/MImageBody.tsx @@ -338,8 +338,8 @@ export default class MImageBody extends React.Component { content: IMediaEventContent, forcedHeight?: number, ): JSX.Element { - let infoWidth; - let infoHeight; + let infoWidth: number; + let infoHeight: number; if (content && content.info && content.info.w && content.info.h) { infoWidth = content.info.w; @@ -382,8 +382,8 @@ export default class MImageBody extends React.Component { const suggestedAndPossibleHeight = Math.min(suggestedImageSize(imageSize, isPortrait).h, infoHeight); const aspectRatio = infoWidth / infoHeight; - let maxWidth; - let maxHeight; + let maxWidth: number; + let maxHeight: number; const maxHeightConstraint = forcedHeight || this.props.maxImageHeight || suggestedAndPossibleHeight; if (maxHeightConstraint * aspectRatio < suggestedAndPossibleWidth || imageSize === ImageSize.Large) { // The width is dictated by the maximum height that was defined by the props or the function param `forcedHeight` @@ -451,7 +451,7 @@ export default class MImageBody extends React.Component { // This has incredibly broken types. const C = CSSTransition as any; const thumbnail = ( -
+
{ className={classes} style={{ // Constrain width here so that spinner appears central to the loaded thumbnail - maxWidth: `min(100%, ${infoWidth}px)`, - maxHeight: maxHeight, + maxWidth, + maxHeight, aspectRatio: `${infoWidth}/${infoHeight}`, }} >