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() }