Rewrite media caching (old image provider)

- Doesn't use pyotherside's image provider feature, for more flexibility
  and simplicity
- Suitable for supporting matrix media events and more later
- Avoid a lot of duplicate files that the old cache created due to
  server not returning what we expect, mistakes in Python/QML code, etc
- Changed file structure
  (e.g. thumbnails/32x32/<mxc id> instead of
   thumbnails/<mxc id>.32.32.crop)

- Backend.wait_until_account_exist: start issuing warnings if the
  function runs for more than 10s, which means in most case a bad user
  ID was passed

- New HMxcImage QML component, used in H(User/Room)Avatar
This commit is contained in:
miruka
2019-11-03 13:48:12 -04:00
parent 55d4035f60
commit 2f19ff493b
20 changed files with 291 additions and 261 deletions

View File

@@ -8,16 +8,6 @@ Rectangle {
implicitWidth: theme.controls.avatar.size
implicitHeight: theme.controls.avatar.size
property string name: ""
property var imageUrl: ""
property var toolTipImageUrl: imageUrl
property alias fillMode: avatarImage.fillMode
property alias animate: avatarImage.animate
readonly property alias hovered: hoverHandler.hovered
readonly property var params: Utils.thumbnailParametersFor(width, height)
color: avatarImage.visible ? "transparent" : Utils.hsluv(
name ? Utils.hueFrom(name) : 0,
name ? theme.controls.avatar.background.saturation : 0,
@@ -25,6 +15,18 @@ Rectangle {
theme.controls.avatar.background.opacity
)
property string clientUserId
property string name
property alias mxc: avatarImage.mxc
property alias toolTipMxc: avatarToolTipImage.mxc
property alias sourceOverride: avatarImage.sourceOverride
property alias toolTipSourceOverride: avatarToolTipImage.sourceOverride
property alias fillMode: avatarImage.fillMode
property alias animate: avatarImage.animate
readonly property alias hovered: hoverHandler.hovered
HLabel {
z: 1
anchors.centerIn: parent
@@ -41,23 +43,24 @@ Rectangle {
)
}
HImage {
HMxcImage {
id: avatarImage
anchors.fill: parent
visible: imageUrl
visible: Boolean(sourceOverride || mxc)
z: 2
sourceSize.width: params.width
sourceSize.height: params.height
sourceSize.width: parent.width
sourceSize.height: parent.height
fillMode: Image.PreserveAspectCrop
source: Qt.resolvedUrl(imageUrl)
animate: false
clientUserId: avatar.clientUserId
loadingLabel.font.pixelSize: theme.fontSize.small
HoverHandler { id: hoverHandler }
HToolTip {
id: avatarToolTip
visible: toolTipImageUrl && hoverHandler.hovered
visible: (toolTipSourceOverride || toolTipMxc) &&
hoverHandler.hovered
delay: 1000
backgroundColor: theme.controls.avatar.hoveredImage.background
@@ -68,10 +71,11 @@ Rectangle {
background.border.width * 2,
)
contentItem: HImage {
contentItem: HMxcImage {
id: avatarToolTipImage
fillMode: Image.PreserveAspectCrop
source: Qt.resolvedUrl(toolTipImageUrl)
clientUserId: avatar.clientUserId
mxc: avatarImage.mxc
sourceSize.width: avatarToolTip.dimension
sourceSize.height: avatarToolTip.dimension

View File

@@ -0,0 +1,40 @@
import QtQuick 2.12
import "../utils.js" as Utils
HImage {
id: image
source: sourceOverride || (show ? cachedPath : "")
onMxcChanged: Qt.callLater(update)
onWidthChanged: Qt.callLater(update)
onHeightChanged: Qt.callLater(update)
onVisibleChanged: Qt.callLater(update)
property string clientUserId
property string mxc
property string sourceOverride: ""
property bool show: false
property string cachedPath: ""
function update() {
let w = sourceSize.width || width
let h = sourceSize.height || height
if (! image.mxc || w < 1 || h < 1 ) {
show = false
return
}
let arg = [image.mxc, w, h]
if (! image) return // if it was destroyed
py.callClientCoro(clientUserId, "media_cache.thumbnail", arg, path => {
if (! image) return
image.cachedPath = path
show = image.visible
})
}
}

View File

@@ -1,13 +1,10 @@
import QtQuick 2.12
HAvatar {
property string displayName: ""
property string avatarUrl: ""
name: displayName[0] == "#" && displayName.length > 1 ?
displayName.substring(1) :
displayName
imageUrl: avatarUrl ? ("image://python/" + avatarUrl) : null
toolTipImageUrl: avatarUrl ? ("image://python/" + avatarUrl) : null
property string displayName
}

View File

@@ -1,17 +1,9 @@
import QtQuick 2.12
HAvatar {
property string userId: ""
property string displayName: ""
property string avatarUrl: ""
readonly property var defaultImageUrl:
avatarUrl ? ("image://python/" + avatarUrl) : null
readonly property var defaultToolTipImageUrl:
avatarUrl ? ("image://python/" + avatarUrl) : null
name: displayName || userId.substring(1) // no leading @
imageUrl: defaultImageUrl
toolTipImageUrl:defaultToolTipImageUrl
property string userId
property string displayName
}

View File

@@ -36,6 +36,7 @@ Rectangle {
HUserAvatar {
id: bannerAvatar
clientUserId: chatPage.userId
anchors.centerIn: parent
}
}

View File

@@ -11,7 +11,7 @@ Banner {
avatar.userId: inviterId
avatar.displayName: inviterName
avatar.avatarUrl: inviterAvatar
avatar.mxc: inviterAvatar
labelText: qsTr("%1 invited you to this room.").arg(
Utils.coloredNameHtml(inviterName, inviterId)

View File

@@ -8,7 +8,7 @@ Banner {
// TODO: avatar func auto
avatar.userId: chatPage.userId
avatar.displayName: chatPage.userInfo.display_name
avatar.avatarUrl: chatPage.userInfo.avatar_url
avatar.mxc: chatPage.userInfo.avatar_url
labelText: qsTr("You are not part of this room anymore.")
buttonModel: [

View File

@@ -58,9 +58,10 @@ Rectangle {
HUserAvatar {
id: avatar
clientUserId: chatPage.userId
userId: writingUserId
displayName: writingUserInfo.display_name
avatarUrl: writingUserInfo.avatar_url
mxc: writingUserInfo.avatar_url
}
HScrollableTextArea {

View File

@@ -23,8 +23,9 @@ Rectangle {
HRoomAvatar {
id: avatar
clientUserId: chatPage.userId
displayName: chatPage.roomInfo.display_name
avatarUrl: chatPage.roomInfo.avatar_url
mxc: chatPage.roomInfo.avatar_url
Layout.alignment: Qt.AlignTop
}

View File

@@ -7,9 +7,12 @@ HTileDelegate {
backgroundColor: theme.chat.roomSidePane.member.background
image: HUserAvatar {
clientUserId: chatPage.userId
userId: model.user_id
displayName: model.display_name
avatarUrl: model.avatar_url
mxc: model.avatar_url
width: height
height: memberDelegate.height
}
title.text: model.display_name || model.user_id

View File

@@ -52,9 +52,10 @@ HRowLayout {
HUserAvatar {
id: avatar
clientUserId: chatPage.userId
userId: model.sender_id
displayName: model.sender_name
avatarUrl: model.sender_avatar
mxc: model.sender_avatar
width: parent.width
height: collapseAvatar ? 1 : 58
}

View File

@@ -26,7 +26,9 @@ HGridLayout {
if (avatar.changed) {
saveButton.avatarChangeRunning = true
let path = Qt.resolvedUrl(avatar.imageUrl).replace(/^file:/, "")
let path =
Qt.resolvedUrl(avatar.sourceOverride).replace(/^file:/, "")
py.callClientCoro(userId, "set_avatar_from_file", [path], () => {
saveButton.avatarChangeRunning = false
@@ -53,14 +55,15 @@ HGridLayout {
Component.onCompleted: nameField.field.forceActiveFocus()
HUserAvatar {
property bool changed: avatar.imageUrl != avatar.defaultImageUrl
property bool changed: Boolean(sourceOverride)
id: avatar
clientUserId: editAccount.userId
userId: editAccount.userId
displayName: nameField.field.text
avatarUrl: accountInfo.avatar_url
imageUrl: fileDialog.selectedFile || fileDialog.file || defaultImageUrl
toolTipImageUrl: ""
mxc: accountInfo.avatar_url
toolTipMxc: ""
sourceOverride: fileDialog.selectedFile || fileDialog.file
Layout.alignment: Qt.AlignHCenter
@@ -71,11 +74,11 @@ HGridLayout {
z: 10
visible: opacity > 0
opacity: ! fileDialog.dialog.visible &&
(! avatar.imageUrl || avatar.hovered) ? 1 : 0
(! avatar.mxc || avatar.hovered) ? 1 : 0
anchors.fill: parent
color: Utils.hsluv(0, 0, 0,
(! avatar.imageUrl && overlayHover.hovered) ? 0.8 : 0.7
(! avatar.mxc && overlayHover.hovered) ? 0.8 : 0.7
)
Behavior on opacity { HNumberAnimation {} }
@@ -90,7 +93,7 @@ HGridLayout {
HIcon {
svgName: "upload-avatar"
colorize: (! avatar.imageUrl && overlayHover.hovered) ?
colorize: (! avatar.mxc && overlayHover.hovered) ?
theme.colors.accentText : theme.icons.colorize
dimension: 64
@@ -100,11 +103,11 @@ HGridLayout {
Item { Layout.preferredHeight: theme.spacing }
HLabel {
text: avatar.imageUrl ?
text: avatar.mxc ?
qsTr("Change profile picture") :
qsTr("Upload profile picture")
color: (! avatar.imageUrl && overlayHover.hovered) ?
color: (! avatar.mxc && overlayHover.hovered) ?
theme.colors.accentText : theme.colors.brightText
Behavior on color { HColorAnimation {} }

View File

@@ -46,9 +46,10 @@ HTileDelegate {
image: HUserAvatar {
clientUserId: model.data.user_id
userId: model.data.user_id
displayName: model.data.display_name
avatarUrl: model.data.avatar_url
mxc: model.data.avatar_url
}
title.color: theme.sidePane.account.name

View File

@@ -32,8 +32,9 @@ HTileDelegate {
image: HRoomAvatar {
clientUserId: model.user_id
displayName: model.data.display_name
avatarUrl: model.data.avatar_url
mxc: model.data.avatar_url
}
title.color: theme.sidePane.room.name

View File

@@ -154,25 +154,6 @@ function filterModelSource(source, filter_text, property="filter_string") {
}
function thumbnailParametersFor(width, height) {
// https://matrix.org/docs/spec/client_server/latest#thumbnails
if (width > 640 || height > 480)
return {width: 800, height: 600, fillMode: Image.PreserveAspectFit}
if (width > 320 || height > 240)
return {width: 640, height: 480, fillMode: Image.PreserveAspectFit}
if (width > 96 || height > 96)
return {width: 320, height: 240, fillMode: Image.PreserveAspectFit}
if (width > 32 || height > 32)
return {width: 96, height: 96, fillMode: Image.PreserveAspectCrop}
return {width: 32, height: 32, fillMode: Image.PreserveAspectCrop}
}
function fitSize(width, height, max) {
if (width >= height) {
let new_width = Math.min(width, max)