mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-20 18:43:41 -05:00
web/timeline: ensure images don't change size when loaded
This commit is contained in:
parent
947ce07d1f
commit
33f67b65a8
5 changed files with 89 additions and 17 deletions
|
@ -2,9 +2,11 @@ div.timeline-event {
|
|||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 50%;
|
||||
max-height: 25vh;
|
||||
div.media-container {
|
||||
> img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ export interface TimelineEventProps {
|
|||
function getBodyType(evt: DBEvent): React.FunctionComponent<EventContentProps> {
|
||||
switch (evt.type) {
|
||||
case "m.room.message":
|
||||
return MessageBody
|
||||
case "m.sticker":
|
||||
return MessageBody
|
||||
}
|
||||
return HiddenEvent
|
||||
}
|
||||
|
|
|
@ -13,12 +13,13 @@
|
|||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import { CSSProperties } from "react"
|
||||
import sanitizeHtml from "sanitize-html"
|
||||
import { getMediaURL } from "../../../api/media.ts"
|
||||
import { ContentURI } from "../../../api/types"
|
||||
import { sanitizeHtmlParams } from "../../../util/html.ts"
|
||||
import { EventContentProps } from "./props.ts"
|
||||
import { calculateMediaSize } from "../../../util/mediasize.ts"
|
||||
|
||||
interface BaseMessageEventContent {
|
||||
msgtype: string
|
||||
|
@ -61,6 +62,9 @@ type MessageEventContent = TextMessageEventContent | MediaMessageEventContent |
|
|||
|
||||
const MessageBody = ({ event }: EventContentProps) => {
|
||||
const content = event.content as MessageEventContent
|
||||
if (event.type === "m.sticker") {
|
||||
content.msgtype = "m.image"
|
||||
}
|
||||
switch (content.msgtype) {
|
||||
case "m.text":
|
||||
case "m.emote":
|
||||
|
@ -71,13 +75,19 @@ const MessageBody = ({ event }: EventContentProps) => {
|
|||
}}/>
|
||||
}
|
||||
return content.body
|
||||
case "m.image":
|
||||
case "m.image": {
|
||||
const style = calculateMediaSize(content.info?.w, content.info?.h)
|
||||
if (content.url) {
|
||||
return <img src={getMediaURL(content.url)} alt={content.body}/>
|
||||
return <div className="media-container" style={style.container}>
|
||||
<img style={style.media} src={getMediaURL(content.url)} alt={content.body}/>
|
||||
</div>
|
||||
} else if (content.file) {
|
||||
return <img src={getMediaURL(content.file.url)} alt={content.body}/>
|
||||
return <div className="media-container" style={style.container}>
|
||||
<img style={style.media} src={getMediaURL(content.file.url)} alt={content.body}/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
return <code>{`{ "type": "${event.type}" }`}</code>
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
// https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/Linkify.tsx#L245
|
||||
import sanitizeHtml from "sanitize-html"
|
||||
import { getMediaURL } from "../api/media.ts"
|
||||
import { calculateMediaSize } from "./mediasize.ts"
|
||||
|
||||
const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/
|
||||
|
||||
|
@ -73,16 +74,14 @@ export const transformTags: NonNullable<sanitizeHtml.IOptions["transformTags"]>
|
|||
|
||||
const requestedWidth = Number(attribs.width)
|
||||
const requestedHeight = Number(attribs.height)
|
||||
const width = Math.min(requestedWidth || 800, 800)
|
||||
const height = Math.min(requestedHeight || 600, 600)
|
||||
// specify width/height as max values instead of absolute ones to allow object-fit to do its thing
|
||||
// we only allow our own styles for this tag so overwrite the attribute
|
||||
attribs.style = `max-width: ${width}px; max-height: ${height}px;`
|
||||
if (requestedWidth) {
|
||||
attribs.style += "width: 100%;"
|
||||
if (requestedHeight && requestedHeight <= 48) {
|
||||
attribs.style = `height: ${requestedHeight}px; width: auto; max-width: ${2 * requestedHeight}px;`
|
||||
}
|
||||
if (requestedHeight) {
|
||||
attribs.style += "height: 100%;"
|
||||
const style = calculateMediaSize(requestedWidth, requestedHeight)
|
||||
if (style.media.aspectRatio) {
|
||||
attribs.style = `width: ${style.container.width}; height: ${style.container.height};`
|
||||
} else {
|
||||
attribs.style = `height: 24px; width: auto; max-width: 48px;`
|
||||
}
|
||||
|
||||
attribs.src = getMediaURL(src)!
|
||||
|
|
61
web/src/util/mediasize.ts
Normal file
61
web/src/util/mediasize.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
// gomuks - A Matrix client written in Go.
|
||||
// Copyright (C) 2024 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import { CSSProperties } from "react"
|
||||
|
||||
const imageContainerWidth = 320
|
||||
const imageContainerHeight = 240
|
||||
const imageContainerAspectRatio = imageContainerWidth / imageContainerHeight
|
||||
|
||||
export interface CalculatedMediaSize {
|
||||
container: CSSProperties
|
||||
media: CSSProperties
|
||||
}
|
||||
|
||||
export function calculateMediaSize(width?: number, height?: number): CalculatedMediaSize {
|
||||
if (!width || !height) {
|
||||
return {
|
||||
container: {
|
||||
width: `${imageContainerWidth}px`,
|
||||
height: `${imageContainerHeight}px`,
|
||||
},
|
||||
media: {},
|
||||
}
|
||||
}
|
||||
const origWidth = width
|
||||
const origHeight = height
|
||||
if (width > imageContainerWidth || height > imageContainerHeight) {
|
||||
const aspectRatio = width / height
|
||||
if (aspectRatio > imageContainerAspectRatio) {
|
||||
width = imageContainerWidth
|
||||
height = imageContainerWidth / aspectRatio
|
||||
} else if (aspectRatio < imageContainerAspectRatio) {
|
||||
width = imageContainerHeight * aspectRatio
|
||||
height = imageContainerHeight
|
||||
} else {
|
||||
width = imageContainerWidth
|
||||
height = imageContainerHeight
|
||||
}
|
||||
}
|
||||
return {
|
||||
container: {
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
},
|
||||
media: {
|
||||
aspectRatio: `${origWidth} / ${origHeight}`,
|
||||
},
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue