MessageEventModel media info improvements

Create a `messageeventmodel` role for media info and reply media info that is a QMap with all the required data.

This replaces the MediaUrlRole, FileMimeTypeRole and the ContentTypeRole. The reply role no longer needs the content role.

This also ensures mxc urls are now generated for replies. All the media parameters will now have default values assigned in the model so the QML no longer needs to do this.
This commit is contained in:
James Graham
2023-05-03 17:50:48 +00:00
parent a6f108d3b8
commit 10794628ed
9 changed files with 164 additions and 107 deletions

View File

@@ -40,16 +40,16 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[SectionRole] = "section"; roles[SectionRole] = "section";
roles[AuthorRole] = "author"; roles[AuthorRole] = "author";
roles[ContentRole] = "content"; roles[ContentRole] = "content";
roles[ContentTypeRole] = "contentType";
roles[HighlightRole] = "isHighlighted"; roles[HighlightRole] = "isHighlighted";
roles[SpecialMarksRole] = "marks"; roles[SpecialMarksRole] = "marks";
roles[LongOperationRole] = "progressInfo"; roles[LongOperationRole] = "progressInfo";
roles[FileMimetypeIcon] = "fileMimetypeIcon";
roles[EventResolvedTypeRole] = "eventResolvedType"; roles[EventResolvedTypeRole] = "eventResolvedType";
roles[MediaInfoRole] = "mediaInfo";
roles[IsReplyRole] = "isReply"; roles[IsReplyRole] = "isReply";
roles[ReplyAuthor] = "replyAuthor"; roles[ReplyAuthor] = "replyAuthor";
roles[ReplyRole] = "reply"; roles[ReplyRole] = "reply";
roles[ReplyIdRole] = "replyId"; roles[ReplyIdRole] = "replyId";
roles[ReplyMediaInfoRole] = "replyMediaInfo";
roles[ShowAuthorRole] = "showAuthor"; roles[ShowAuthorRole] = "showAuthor";
roles[ShowSectionRole] = "showSection"; roles[ShowSectionRole] = "showSection";
roles[ReadMarkersRole] = "readMarkers"; roles[ReadMarkersRole] = "readMarkers";
@@ -60,7 +60,6 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[MimeTypeRole] = "mimeType"; roles[MimeTypeRole] = "mimeType";
roles[FormattedBodyRole] = "formattedBody"; roles[FormattedBodyRole] = "formattedBody";
roles[AuthorIdRole] = "authorId"; roles[AuthorIdRole] = "authorId";
roles[MediaUrlRole] = "mediaUrl";
roles[VerifiedRole] = "verified"; roles[VerifiedRole] = "verified";
roles[DisplayNameForInitialsRole] = "displayNameForInitials"; roles[DisplayNameForInitialsRole] = "displayNameForInitials";
roles[AuthorDisplayNameRole] = "authorDisplayName"; roles[AuthorDisplayNameRole] = "authorDisplayName";
@@ -567,14 +566,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
return userInContext(author, m_currentRoom); return userInContext(author, m_currentRoom);
} }
if (role == ContentTypeRole) {
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
const auto &contentType = e->mimeType().name();
return contentType == "text/plain" ? QStringLiteral("text/html") : contentType;
}
return QStringLiteral("text/plain");
}
if (role == ContentRole) { if (role == ContentRole) {
if (evt.isRedacted()) { if (evt.isRedacted()) {
auto reason = evt.redactedBecause()->reason(); auto reason = evt.redactedBecause()->reason();
@@ -601,15 +592,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
return !m_currentRoom->isDirectChat() && m_currentRoom->isEventHighlighted(&evt); return !m_currentRoom->isDirectChat() && m_currentRoom->isEventHighlighted(&evt);
} }
if (role == FileMimetypeIcon) {
auto e = eventCast<const RoomMessageEvent>(&evt);
if (!e || !e->hasFileContent()) {
return QVariant();
}
return e->content()->fileInfo()->mimeType.iconName();
}
if (role == MimeTypeRole) { if (role == MimeTypeRole) {
if (auto e = eventCast<const RoomMessageEvent>(&evt)) { if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
if (!e || !e->hasFileContent()) { if (!e || !e->hasFileContent()) {
@@ -695,6 +677,10 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
return role == TimeRole ? QVariant(ts) : renderDate(ts); return role == TimeRole ? QVariant(ts) : renderDate(ts);
} }
if (role == MediaInfoRole) {
return getMediaInfoForEvent(evt);
}
if (role == IsReplyRole) { if (role == IsReplyRole) {
return !evt.contentJson()["m.relates_to"].toObject()["m.in_reply_to"].toObject()["event_id"].toString().isEmpty(); return !evt.contentJson()["m.relates_to"].toObject()["m.in_reply_to"].toObject()["event_id"].toString().isEmpty();
} }
@@ -714,6 +700,14 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
} }
} }
if (role == ReplyMediaInfoRole) {
auto replyPtr = getReplyForEvent(evt);
if (!replyPtr) {
return {};
}
return getMediaInfoForEvent(*replyPtr);
}
if (role == ReplyRole) { if (role == ReplyRole) {
auto replyPtr = getReplyForEvent(evt); auto replyPtr = getReplyForEvent(evt);
if (!replyPtr) { if (!replyPtr) {
@@ -752,22 +746,8 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
type = DelegateType::Other; type = DelegateType::Other;
} }
QVariant content;
if (auto e = eventCast<const RoomMessageEvent>(replyPtr)) {
// Cannot use e.contentJson() here because some
// EventContent classes inject values into the copy of the
// content JSON stored in EventContent::Base
content = e->hasFileContent() ? QVariant::fromValue(e->content()->originalJson) : QVariant();
};
if (auto e = eventCast<const StickerEvent>(replyPtr)) {
content = QVariant::fromValue(e->image().originalJson);
}
return QVariantMap{ return QVariantMap{
{"eventId", replyPtr->id()},
{"display", m_currentRoom->eventToString(*replyPtr, Qt::RichText)}, {"display", m_currentRoom->eventToString(*replyPtr, Qt::RichText)},
{"content", content},
{"type", type}, {"type", type},
}; };
} }
@@ -926,38 +906,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
return evt.senderId(); return evt.senderId();
} }
if (role == MediaUrlRole) {
#ifdef QUOTIENT_07
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
if (!e->hasFileContent()) {
return QVariant();
}
if (e->content()->originalJson.contains(QStringLiteral("file")) && e->content()->originalJson["file"].toObject().contains(QStringLiteral("url"))) {
return m_currentRoom->makeMediaUrl(e->id(), e->content()->originalJson["file"]["url"].toString());
}
if (e->content()->originalJson.contains(QStringLiteral("url"))) {
return m_currentRoom->makeMediaUrl(e->id(), e->content()->originalJson["url"].toString());
}
}
// Requires https://github.com/quotient-im/libQuotient/pull/570
// if (auto e = eventCast<const StickerEvent>(&evt)) {
// return m_currentRoom->makeMediaUrl(e->id(), e->url());
// }
#endif
// Construct link in the same form as urlToDownload as that function doesn't work for stickers
if (auto e = eventCast<const StickerEvent>(&evt)) {
auto url = QUrl(m_currentRoom->connection()->homeserver().toString() + "/_matrix/media/r0/download/" + e->url().toString().remove("mxc://"));
QUrlQuery q(url.query());
q.addQueryItem("allow_remote", "true");
url.setQuery(q);
return url;
}
return m_currentRoom->urlToDownload(evt.id());
}
if (role == VerifiedRole) { if (role == VerifiedRole) {
#ifdef QUOTIENT_07 #ifdef QUOTIENT_07
#ifdef Quotient_E2EE_ENABLED #ifdef Quotient_E2EE_ENABLED
@@ -1120,3 +1068,104 @@ const RoomEvent *MessageEventModel::getReplyForEvent(const RoomEvent &event) con
} }
return replyPtr; return replyPtr;
} }
QVariantMap MessageEventModel::getMediaInfoForEvent(const RoomEvent &event) const
{
QVariantMap mediaInfo;
QString eventId = event.id();
// Get the file info for the event.
const EventContent::FileInfo *fileInfo;
#ifdef QUOTIENT_07
if (event.is<RoomMessageEvent>()) {
auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event);
#else
if (auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event)) {
#endif
if (!roomMessageEvent->hasFileContent()) {
return {};
}
fileInfo = roomMessageEvent->content()->fileInfo();
#ifdef QUOTIENT_07
} else if (event.is<StickerEvent>()) {
auto stickerEvent = eventCast<const StickerEvent>(&event);
#else
} else if (auto stickerEvent = eventCast<const StickerEvent>(&event)) {
#endif
fileInfo = &stickerEvent->image();
} else {
return {};
}
return getMediaInfoFromFileInfo(fileInfo, eventId);
}
QVariantMap MessageEventModel::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail) const
{
QVariantMap mediaInfo;
// Get the mxc URL for the media.
#ifdef QUOTIENT_07
QUrl source = m_currentRoom->makeMediaUrl(eventId, fileInfo->url());
if (source.isValid() && source.scheme() == QStringLiteral("mxc")) {
mediaInfo["source"] = source;
} else {
mediaInfo["source"] = QUrl();
}
#else
auto url = QUrl(m_currentRoom->connection()->homeserver().toString() + "/_matrix/media/r0/download/" + fileInfo->url.toString().remove("mxc://"));
QUrlQuery q(url.query());
q.addQueryItem("allow_remote", "true");
url.setQuery(q);
mediaInfo["source"] = url;
#endif
auto mimeType = fileInfo->mimeType;
// Add the MIME type for the media if available.
mediaInfo["mimeType"] = mimeType.name();
// Add the MIME type icon if available.
mediaInfo["mimeIcon"] = mimeType.iconName();
// Add media size if available.
mediaInfo["size"] = fileInfo->payloadSize;
// Add parameter depending on media type.
if (mimeType.name().contains(QStringLiteral("image"))) {
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileInfo)) {
mediaInfo["width"] = castInfo->imageSize.width();
mediaInfo["height"] = castInfo->imageSize.height();
if (!isThumbnail) {
mediaInfo["thumbnailInfo"] = getMediaInfoFromFileInfo(castInfo->thumbnailInfo(), eventId, true);
}
QString blurhash = castInfo->originalInfoJson["xyz.amorgan.blurhash"].toString();
if (blurhash.isEmpty()) {
mediaInfo["blurhash"] = QUrl();
} else {
mediaInfo["blurhash"] = QUrl("image://blurhash/" + blurhash);
}
}
}
if (mimeType.name().contains(QStringLiteral("video"))) {
if (auto castInfo = static_cast<const EventContent::VideoContent *>(fileInfo)) {
mediaInfo["width"] = castInfo->imageSize.width();
mediaInfo["height"] = castInfo->imageSize.height();
mediaInfo["duration"] = castInfo->duration;
if (!isThumbnail) {
mediaInfo["thumbnailInfo"] = getMediaInfoFromFileInfo(castInfo->thumbnailInfo(), eventId, true);
}
}
}
if (mimeType.name().contains(QStringLiteral("audio"))) {
if (auto castInfo = static_cast<const EventContent::AudioContent *>(fileInfo)) {
mediaInfo["duration"] = castInfo->duration;
}
}
return mediaInfo;
}

View File

@@ -64,20 +64,20 @@ public:
SectionRole, /**< The date of the event as a string. */ SectionRole, /**< The date of the event as a string. */
AuthorRole, /**< The author of the event. */ AuthorRole, /**< The author of the event. */
ContentRole, /**< The full message content. */ ContentRole, /**< The full message content. */
ContentTypeRole, /**< The content mime type. */
HighlightRole, /**< Whether the event should be highlighted. */ HighlightRole, /**< Whether the event should be highlighted. */
SpecialMarksRole, /**< Whether the event is hidden or not. */ SpecialMarksRole, /**< Whether the event is hidden or not. */
LongOperationRole, /**< Progress info when downloading files. */ LongOperationRole, /**< Progress info when downloading files. */
FormattedBodyRole, /**< The formatted body of a rich message. */ FormattedBodyRole, /**< The formatted body of a rich message. */
GenericDisplayRole, /**< A generic string based upon the message type. */ GenericDisplayRole, /**< A generic string based upon the message type. */
MediaInfoRole, /**< The media info for the event. */
MimeTypeRole, /**< The mime type of the message's file or media. */ MimeTypeRole, /**< The mime type of the message's file or media. */
FileMimetypeIcon, /**< The icon name for the mime type of a file. */
IsReplyRole, /**< Is the message a reply to another event. */ IsReplyRole, /**< Is the message a reply to another event. */
ReplyAuthor, /**< The author of the event that was replied to. */ ReplyAuthor, /**< The author of the event that was replied to. */
ReplyRole, /**< The content data of the message that was replied to. */
ReplyIdRole, /**< The matrix ID of the message that was replied to. */ ReplyIdRole, /**< The matrix ID of the message that was replied to. */
ReplyMediaInfoRole, /**< The media info of the message that was replied to. */
ReplyRole, /**< The content data of the message that was replied to. */
ShowAuthorRole, /**< Whether the author's name should be shown. */ ShowAuthorRole, /**< Whether the author's name should be shown. */
ShowSectionRole, /**< Whether the section header should be shown. */ ShowSectionRole, /**< Whether the section header should be shown. */
@@ -87,7 +87,6 @@ public:
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */ ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
ReactionRole, /**< List of reactions to this event. */ ReactionRole, /**< List of reactions to this event. */
SourceRole, /**< The full message source JSON. */ SourceRole, /**< The full message source JSON. */
MediaUrlRole, /**< The source URL for any media in the message. */
// For debugging // For debugging
EventResolvedTypeRole, /**< The event type the message. */ EventResolvedTypeRole, /**< The event type the message. */
@@ -194,6 +193,8 @@ private:
void moveReadMarker(const QString &toEventId); void moveReadMarker(const QString &toEventId);
const Quotient::RoomEvent *getReplyForEvent(const Quotient::RoomEvent &event) const; const Quotient::RoomEvent *getReplyForEvent(const Quotient::RoomEvent &event) const;
QVariantMap getMediaInfoForEvent(const Quotient::RoomEvent &event) const;
QVariantMap getMediaInfoFromFileInfo(const Quotient::EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail = false) const;
std::vector<Quotient::event_ptr_tt<Quotient::RoomEvent>> m_extraEvents; std::vector<Quotient::event_ptr_tt<Quotient::RoomEvent>> m_extraEvents;

View File

@@ -18,9 +18,9 @@ Components.AlbumMaximizeComponent {
property list<Components.AlbumModelItem> items: [ property list<Components.AlbumModelItem> items: [
Components.AlbumModelItem { Components.AlbumModelItem {
type: root.modelData.delegateType === MessageEventModel.Image ? Components.AlbumModelItem.Image : Components.AlbumModelItem.Video type: root.modelData.delegateType === MessageEventModel.Image || root.modelData.delegateType === MessageEventModel.Sticker ? Components.AlbumModelItem.Image : Components.AlbumModelItem.Video
source: root.modelData.delegateType === MessageEventModel.Video ? modelData.progressInfo.localPath : modelData.mediaUrl source: root.modelData.delegateType === MessageEventModel.Video ? modelData.progressInfo.localPath : modelData.mediaInfo.source
tempSource: modelData.content.info["xyz.amorgan.blurhash"] ? ("image://blurhash/" + modelData.content.info["xyz.amorgan.blurhash"]) : "" tempSource: modelData.mediaInfo.blurhash
caption: modelData.display caption: modelData.display
} }
] ]

View File

@@ -94,7 +94,7 @@ TimelineContainer {
visible: false visible: false
Layout.fillWidth: true Layout.fillWidth: true
from: 0 from: 0
to: model.content.info.size to: model.mediaInfo.size
value: model.progressInfo.progress value: model.progressInfo.progress
} }
RowLayout { RowLayout {

View File

@@ -103,7 +103,7 @@ TimelineContainer {
] ]
Kirigami.Icon { Kirigami.Icon {
source: model.fileMimetypeIcon source: model.mediaInfo.mimeIcon
fallback: "unknown" fallback: "unknown"
} }
@@ -118,7 +118,7 @@ TimelineContainer {
QQC2.Label { QQC2.Label {
id: sizeLabel id: sizeLabel
Layout.fillWidth: true Layout.fillWidth: true
text: Controller.formatByteSize(content.info ? content.info.size : 0) text: Controller.formatByteSize(model.mediaInfo.size)
opacity: 0.7 opacity: 0.7
elide: Text.ElideRight elide: Text.ElideRight
maximumLineCount: 1 maximumLineCount: 1

View File

@@ -16,17 +16,9 @@ TimelineContainer {
onOpenContextMenu: openFileContext(model, imageDelegate) onOpenContextMenu: openFileContext(model, imageDelegate)
property var content: model.content
readonly property bool isAnimated: contentType === "image/gif"
property bool openOnFinished: false property bool openOnFinished: false
readonly property bool downloaded: progressInfo && progressInfo.completed readonly property bool downloaded: progressInfo && progressInfo.completed
readonly property bool isThumbnail: !(content.info.thumbnail_info == null || content.thumbnailMediaId == null)
// readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info
readonly property var info: content.info
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId
readonly property var maxWidth: Kirigami.Units.gridUnit * 30 readonly property var maxWidth: Kirigami.Units.gridUnit * 30
readonly property var maxHeight: Kirigami.Units.gridUnit * 30 readonly property var maxHeight: Kirigami.Units.gridUnit * 30
@@ -34,8 +26,8 @@ TimelineContainer {
id: img id: img
property var imageWidth: { property var imageWidth: {
if (imageDelegate.info && imageDelegate.info.w && imageDelegate.info.w > 0) { if (model.mediaInfo.width > 0) {
return imageDelegate.info.w; return model.mediaInfo.width;
} else if (sourceSize.width && sourceSize.width > 0) { } else if (sourceSize.width && sourceSize.width > 0) {
return sourceSize.width; return sourceSize.width;
} else { } else {
@@ -43,8 +35,8 @@ TimelineContainer {
} }
} }
property var imageHeight: { property var imageHeight: {
if (imageDelegate.info && imageDelegate.info.h && imageDelegate.info.h > 0) { if (model.mediaInfo.height > 0) {
return imageDelegate.info.h; return model.mediaInfo.height;
} else if (sourceSize.height && sourceSize.height > 0) { } else if (sourceSize.height && sourceSize.height > 0) {
return sourceSize.height; return sourceSize.height;
} else { } else {
@@ -78,11 +70,11 @@ TimelineContainer {
Layout.maximumHeight: maxSize.height Layout.maximumHeight: maxSize.height
Layout.preferredWidth: imageWidth Layout.preferredWidth: imageWidth
Layout.preferredHeight: imageHeight Layout.preferredHeight: imageHeight
source: model.mediaUrl source: model.mediaInfo.source
Image { Image {
anchors.fill: parent anchors.fill: parent
source: content.info["xyz.amorgan.blurhash"] ? ("image://blurhash/" + content.info["xyz.amorgan.blurhash"]) : "" source: model.mediaInfo.blurhash
visible: parent.status !== Image.Ready visible: parent.status !== Image.Ready
} }

View File

@@ -18,6 +18,7 @@ Item {
property var name property var name
property alias avatar: replyAvatar.source property alias avatar: replyAvatar.source
property var color property var color
property var mediaInfo
implicitWidth: mainLayout.implicitWidth implicitWidth: mainLayout.implicitWidth
implicitHeight: mainLayout.implicitHeight implicitHeight: mainLayout.implicitHeight
@@ -61,6 +62,7 @@ Item {
id: loader id: loader
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumHeight: loader.item && (reply.type == MessageEventModel.Image || reply.type == MessageEventModel.Sticker) ? loader.item.height : -1
Layout.columnSpan: 2 Layout.columnSpan: 2
sourceComponent: { sourceComponent: {
@@ -112,19 +114,35 @@ Item {
id: imageComponent id: imageComponent
Image { Image {
id: image id: image
readonly property var content: reply.content
readonly property bool isThumbnail: !(content.info.thumbnail_info == null || content.thumbnailMediaId == null) property var imageWidth: {
readonly property var info: content.info if (replyComponent.mediaInfo.width > 0) {
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId return replyComponent.mediaInfo.width;
source: "image://mxc/" + mediaId } else {
return sourceSize.width;
}
}
property var imageHeight: {
if (replyComponent.mediaInfo.height > 0) {
return replyComponent.mediaInfo.height;
} else {
return sourceSize.height;
}
}
readonly property var aspectRatio: imageWidth / imageHeight
height: width / aspectRatio
fillMode: Image.PreserveAspectFit
source: mediaInfo.source
} }
} }
Component { Component {
id: mimeComponent id: mimeComponent
MimeComponent { MimeComponent {
mimeIconSource: reply.content.info.mimetype.replace("/", "-") mimeIconSource: replyComponent.mediaInfo.mimeIcon
label: reply.display label: reply.display
subLabel: reply.type === MessageEventModel.File ? Controller.formatByteSize(reply.content.info ? reply.content.info.size : 0) : Controller.formatDuration(reply.content.info.duration) subLabel: reply.type === MessageEventModel.File ? Controller.formatByteSize(replyComponent.mediaInfo.size) : Controller.formatDuration(replyComponent.mediaInfo.duration)
} }
} }
Component { Component {

View File

@@ -253,13 +253,14 @@ ColumnLayout {
Layout.maximumWidth: contentMaxWidth Layout.maximumWidth: contentMaxWidth
active: model.reply !== undefined active: model.isReply
visible: active visible: active
sourceComponent: ReplyComponent { sourceComponent: ReplyComponent {
name: currentRoom.htmlSafeMemberName(model.replyAuthor.id) name: currentRoom.htmlSafeMemberName(model.replyAuthor.id)
avatar: model.replyAuthor.avatarSource avatar: model.replyAuthor.avatarSource
color: model.replyAuthor.color color: model.replyAuthor.color
mediaInfo: model.replyMediaInfo
} }
Connections { Connections {

View File

@@ -22,8 +22,6 @@ TimelineContainer {
readonly property var maxWidth: Kirigami.Units.gridUnit * 30 readonly property var maxWidth: Kirigami.Units.gridUnit * 30
readonly property var maxHeight: Kirigami.Units.gridUnit * 30 readonly property var maxHeight: Kirigami.Units.gridUnit * 30
readonly property var info: model.content.info
onOpenContextMenu: openFileContext(model, vid) onOpenContextMenu: openFileContext(model, vid)
onDownloadedChanged: { onDownloadedChanged: {
@@ -41,8 +39,8 @@ TimelineContainer {
id: vid id: vid
property var videoWidth: { property var videoWidth: {
if (videoDelegate.info && videoDelegate.info.w && videoDelegate.info.w > 0) { if (model.mediaInfo.width > 0) {
return videoDelegate.info.w; return model.mediaInfo.width;
} else if (metaData.resolution && metaData.resolution.width) { } else if (metaData.resolution && metaData.resolution.width) {
return metaData.resolution.width; return metaData.resolution.width;
} else { } else {
@@ -50,8 +48,8 @@ TimelineContainer {
} }
} }
property var videoHeight: { property var videoHeight: {
if (videoDelegate.info && videoDelegate.info.h && videoDelegate.info.h > 0) { if (model.mediaInfo.height > 0) {
return videoDelegate.info.h; return model.mediaInfo.height;
} else if (metaData.resolution && metaData.resolution.height) { } else if (metaData.resolution && metaData.resolution.height) {
return metaData.resolution.height; return metaData.resolution.height;
} else { } else {
@@ -154,11 +152,9 @@ TimelineContainer {
Image { Image {
id: mediaThumbnail id: mediaThumbnail
anchors.fill: parent anchors.fill: parent
visible: false visible: false
source: model.content.thumbnailMediaId ? "image://mxc/" + model.content.thumbnailMediaId : "" source: model.mediaInfo.thumbnailInfo.source
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
} }