Fix loading m.image events in encrypted rooms

Fix bug introduced in 11fb32:

When loading an encrypted thumbnail, QML lacks the decryption dict for
half a second at first.

When calling python like this, python calls the wrong matrix API
for fetching the encrypted thumbnail, and the added retry code
would be forever stuck.

The retry code has been moved to QML, and now works for all HImage.
This commit is contained in:
miruka 2020-07-19 14:49:41 -04:00
parent 26a4d76fc2
commit f1055ce5b9
5 changed files with 34 additions and 24 deletions

View File

@ -1,14 +1,17 @@
# TODO # TODO
- Image viewer: - Image viewer:
- support http urls
- open externally in context menu in timeline thumbnail - open externally in context menu in timeline thumbnail
- fix: gif always closes? - fix: gif always closes?
- hflickable support kinetic scrolling disabler and speed/decel settings - hflickable support kinetic scrolling disabler and speed/decel settings
- buttons - buttons
- keyboard controls - keyboard controls
- clipboard preview doesn't update when copied image changes until second time
- Avatar tooltip can get displayed in front of presence menu - Avatar tooltip can get displayed in front of presence menu
- Use loading cursorShape - Use loading cursorShape
- Increase keyboard page scroll speed
- global presence control - global presence control

View File

@ -12,7 +12,6 @@ from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Any, DefaultDict, Dict, Optional from typing import TYPE_CHECKING, Any, DefaultDict, Dict, Optional
from urllib.parse import urlparse from urllib.parse import urlparse
from .errors import MatrixError
from PIL import Image as PILImage from PIL import Image as PILImage
@ -134,20 +133,8 @@ class Media:
async def create(self) -> Path: async def create(self) -> Path:
"""Download and cache the media file to disk.""" """Download and cache the media file to disk."""
retries = 0
while True:
try:
async with CONCURRENT_DOWNLOADS_LIMIT: async with CONCURRENT_DOWNLOADS_LIMIT:
data = await self._get_remote_data() data = await self._get_remote_data()
except MatrixError as err:
if err.http_code != 404 and err.http_code < 500:
raise
else:
break
await asyncio.sleep(min(30, 0.2 * (2 ** (min(1000, retries) - 1))))
retries += 1
self.local_path.parent.mkdir(parents=True, exist_ok=True) self.local_path.parent.mkdir(parents=True, exist_ok=True)

View File

@ -83,7 +83,6 @@ Rectangle {
) )
visible: ! avatarImage.broken && visible: ! avatarImage.broken &&
avatarImage.status !== Image.Error &&
avatarImage.width < dimension * 0.75 && avatarImage.width < dimension * 0.75 &&
(toolTipSourceOverride || toolTipMxc) && (toolTipSourceOverride || toolTipMxc) &&
hoverHandler.hovered hoverHandler.hovered

View File

@ -7,7 +7,7 @@ Image {
id: image id: image
property bool circle: radius === circleRadius property bool circle: radius === circleRadius
property bool broken: false property bool broken: image.status === Image.Error
property bool animate: true property bool animate: true
property bool animated: property bool animated:
utils.urlExtension(image.source).toLowerCase() === "gif" utils.urlExtension(image.source).toLowerCase() === "gif"
@ -20,6 +20,13 @@ Image {
readonly property int circleRadius: readonly property int circleRadius:
Math.ceil(Math.max(image.width, image.height)) Math.ceil(Math.max(image.width, image.height))
function reload() {
// Can be reimplemented in components inheriting HImage
const oldSource = source
source = ""
source = oldSource
}
autoTransform: true autoTransform: true
asynchronous: true asynchronous: true
@ -122,7 +129,7 @@ Image {
HIcon { HIcon {
anchors.centerIn: parent anchors.centerIn: parent
visible: broken || image.status === Image.Error visible: image.broken
svgName: "broken-image" svgName: "broken-image"
colorize: theme.colors.negativeBackground colorize: theme.colors.negativeBackground
} }
@ -132,4 +139,18 @@ Image {
anchors.fill: parent anchors.fill: parent
visible: false visible: false
} }
Timer {
property int retries: 0
running: image.broken
repeat: true
interval:
Math.min(60, 0.2 * Math.pow(2, Math.min(1000, retries) - 1)) * 1000
onTriggered: {
image.reload()
retries += 1
}
}
} }

View File

@ -20,7 +20,7 @@ HImage {
readonly property bool isMxc: mxc.startsWith("mxc://") readonly property bool isMxc: mxc.startsWith("mxc://")
function update() { function reload() {
if (! py || ! canUpdate) return // component was destroyed if (! py || ! canUpdate) return // component was destroyed
const w = sourceSize.width || width const w = sourceSize.width || width
@ -46,7 +46,7 @@ HImage {
if (! image) return if (! image) return
if (image.cachedPath !== path) image.cachedPath = path if (image.cachedPath !== path) image.cachedPath = path
image.broken = false image.broken = Qt.binding(() => image.status === Image.Error)
image.show = image.visible image.show = image.visible
}, (type, args, error, traceback) => { }, (type, args, error, traceback) => {
@ -61,9 +61,9 @@ HImage {
showProgressBar: showProgressBar:
(isMxc && status === Image.Null) || status === Image.Loading (isMxc && status === Image.Null) || status === Image.Loading
onWidthChanged: Qt.callLater(update) onWidthChanged: Qt.callLater(reload)
onHeightChanged: Qt.callLater(update) onHeightChanged: Qt.callLater(reload)
onVisibleChanged: Qt.callLater(update) onVisibleChanged: Qt.callLater(reload)
onMxcChanged: Qt.callLater(update) onMxcChanged: Qt.callLater(reload)
Component.onDestruction: if (getFuture) getFuture.cancel() Component.onDestruction: if (getFuture) getFuture.cancel()
} }