From f1055ce5b9c40e1feec11e89e50ac1c2fb248ba5 Mon Sep 17 00:00:00 2001 From: miruka Date: Sun, 19 Jul 2020 14:49:41 -0400 Subject: [PATCH] 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. --- TODO.md | 3 +++ src/backend/media_cache.py | 17 ++--------------- src/gui/Base/HAvatar.qml | 1 - src/gui/Base/HImage.qml | 25 +++++++++++++++++++++++-- src/gui/Base/HMxcImage.qml | 12 ++++++------ 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/TODO.md b/TODO.md index c6afad2d..d7662644 100644 --- a/TODO.md +++ b/TODO.md @@ -1,14 +1,17 @@ # TODO - Image viewer: + - support http urls - open externally in context menu in timeline thumbnail - fix: gif always closes? - hflickable support kinetic scrolling disabler and speed/decel settings - buttons - keyboard controls +- clipboard preview doesn't update when copied image changes until second time - Avatar tooltip can get displayed in front of presence menu - Use loading cursorShape +- Increase keyboard page scroll speed - global presence control diff --git a/src/backend/media_cache.py b/src/backend/media_cache.py index c33c961f..e2e49c23 100644 --- a/src/backend/media_cache.py +++ b/src/backend/media_cache.py @@ -12,7 +12,6 @@ from dataclasses import dataclass, field from pathlib import Path from typing import TYPE_CHECKING, Any, DefaultDict, Dict, Optional from urllib.parse import urlparse -from .errors import MatrixError from PIL import Image as PILImage @@ -134,20 +133,8 @@ class Media: async def create(self) -> Path: """Download and cache the media file to disk.""" - retries = 0 - - while True: - try: - async with CONCURRENT_DOWNLOADS_LIMIT: - 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 + async with CONCURRENT_DOWNLOADS_LIMIT: + data = await self._get_remote_data() self.local_path.parent.mkdir(parents=True, exist_ok=True) diff --git a/src/gui/Base/HAvatar.qml b/src/gui/Base/HAvatar.qml index dc00aeef..cebcb662 100644 --- a/src/gui/Base/HAvatar.qml +++ b/src/gui/Base/HAvatar.qml @@ -83,7 +83,6 @@ Rectangle { ) visible: ! avatarImage.broken && - avatarImage.status !== Image.Error && avatarImage.width < dimension * 0.75 && (toolTipSourceOverride || toolTipMxc) && hoverHandler.hovered diff --git a/src/gui/Base/HImage.qml b/src/gui/Base/HImage.qml index 62f8d372..6bc0ddd1 100644 --- a/src/gui/Base/HImage.qml +++ b/src/gui/Base/HImage.qml @@ -7,7 +7,7 @@ Image { id: image property bool circle: radius === circleRadius - property bool broken: false + property bool broken: image.status === Image.Error property bool animate: true property bool animated: utils.urlExtension(image.source).toLowerCase() === "gif" @@ -20,6 +20,13 @@ Image { readonly property int circleRadius: 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 asynchronous: true @@ -122,7 +129,7 @@ Image { HIcon { anchors.centerIn: parent - visible: broken || image.status === Image.Error + visible: image.broken svgName: "broken-image" colorize: theme.colors.negativeBackground } @@ -132,4 +139,18 @@ Image { anchors.fill: parent 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 + } + } } diff --git a/src/gui/Base/HMxcImage.qml b/src/gui/Base/HMxcImage.qml index b38b96f9..48be678a 100644 --- a/src/gui/Base/HMxcImage.qml +++ b/src/gui/Base/HMxcImage.qml @@ -20,7 +20,7 @@ HImage { readonly property bool isMxc: mxc.startsWith("mxc://") - function update() { + function reload() { if (! py || ! canUpdate) return // component was destroyed const w = sourceSize.width || width @@ -46,7 +46,7 @@ HImage { if (! image) return if (image.cachedPath !== path) image.cachedPath = path - image.broken = false + image.broken = Qt.binding(() => image.status === Image.Error) image.show = image.visible }, (type, args, error, traceback) => { @@ -61,9 +61,9 @@ HImage { showProgressBar: (isMxc && status === Image.Null) || status === Image.Loading - onWidthChanged: Qt.callLater(update) - onHeightChanged: Qt.callLater(update) - onVisibleChanged: Qt.callLater(update) - onMxcChanged: Qt.callLater(update) + onWidthChanged: Qt.callLater(reload) + onHeightChanged: Qt.callLater(reload) + onVisibleChanged: Qt.callLater(reload) + onMxcChanged: Qt.callLater(reload) Component.onDestruction: if (getFuture) getFuture.cancel() }