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:
@@ -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
|
||||
|
40
src/qml/Base/HMxcImage.qml
Normal file
40
src/qml/Base/HMxcImage.qml
Normal 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
|
||||
})
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ Rectangle {
|
||||
|
||||
HUserAvatar {
|
||||
id: bannerAvatar
|
||||
clientUserId: chatPage.userId
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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: [
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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 {} }
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user