From 4ada039384e79a0c5caa0b227d9ec54341cedecd Mon Sep 17 00:00:00 2001 From: miruka Date: Fri, 6 Dec 2019 16:44:25 -0400 Subject: [PATCH] Upload UI/code improvements --- src/icons/thin/uploading.svg | 3 + src/python/matrix_client.py | 8 +- src/python/models/items.py | 8 +- src/qml/Chat/ChatSplitView.qml | 2 +- src/qml/Chat/FileTransfer/Transfer.qml | 147 ++++++++++----------- src/qml/Chat/FileTransfer/TransferList.qml | 8 +- 6 files changed, 87 insertions(+), 89 deletions(-) create mode 100644 src/icons/thin/uploading.svg diff --git a/src/icons/thin/uploading.svg b/src/icons/thin/uploading.svg new file mode 100644 index 00000000..c4cde5b5 --- /dev/null +++ b/src/icons/thin/uploading.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/python/matrix_client.py b/src/python/matrix_client.py index df22a1e3..d33d5cbd 100644 --- a/src/python/matrix_client.py +++ b/src/python/matrix_client.py @@ -305,7 +305,11 @@ class MatrixClient(nio.AsyncClient): except OSError as err: log.warning(f"Failed thumbnailing {path}: {err}") else: - upload_item.status = UploadStatus.UploadingThumbnail + thumb_name = f"{path.stem}_thumbnail{''.join(path.suffixes)}" + + upload_item.status = UploadStatus.Uploading + upload_item.filepath = Path(thumb_name) + upload_item.total_size = len(thumb_data) try: thumb_url, _, thumb_crypt_dict = await self.upload( @@ -317,7 +321,7 @@ class MatrixClient(nio.AsyncClient): except MatrixError as err: log.warning(f"Failed uploading thumbnail {path}: {err}") else: - upload_item.status = UploadStatus.CachingThumbnail + upload_item.status = UploadStatus.Caching await Thumbnail.from_bytes( self.backend.media_cache, diff --git a/src/python/models/items.py b/src/python/models/items.py index 9eafe58b..f811f1a6 100644 --- a/src/python/models/items.py +++ b/src/python/models/items.py @@ -105,11 +105,9 @@ class Member(ModelItem): class UploadStatus(AutoStrEnum): - Uploading = auto() - Caching = auto() - UploadingThumbnail = auto() - CachingThumbnail = auto() - Error = auto() + Uploading = auto() + Caching = auto() + Error = auto() @dataclass diff --git a/src/qml/Chat/ChatSplitView.qml b/src/qml/Chat/ChatSplitView.qml index 92bf79c7..3705c56c 100644 --- a/src/qml/Chat/ChatSplitView.qml +++ b/src/qml/Chat/ChatSplitView.qml @@ -29,7 +29,7 @@ HSplitView { TransferList { Layout.fillWidth: true Layout.minimumHeight: implicitHeight - Layout.preferredHeight: implicitHeight * uploadsCount + Layout.preferredHeight: implicitHeight * transferCount Layout.maximumHeight: chatSplitView.height / 6 Behavior on Layout.preferredHeight { HNumberAnimation {} } diff --git a/src/qml/Chat/FileTransfer/Transfer.qml b/src/qml/Chat/FileTransfer/Transfer.qml index c20317bb..8d1fa334 100644 --- a/src/qml/Chat/FileTransfer/Transfer.qml +++ b/src/qml/Chat/FileTransfer/Transfer.qml @@ -5,29 +5,29 @@ import "../../utils.js" as Utils HColumnLayout { id: transfer - width: uploadsList.width property bool guiPaused: false - readonly property bool paused: model.status === "Paused" || guiPaused + property int msLeft: model.time_left || 0 + property int uploaded: model.uploaded + readonly property int speed: model.speed + readonly property int totalSize: model.total_size + readonly property string status: model.status + readonly property bool paused: status === "Paused" || guiPaused + Behavior on msLeft { HNumberAnimation { duration: 1000 } } + Behavior on uploaded { HNumberAnimation { duration: 1000 }} Behavior on height { HNumberAnimation {} } HRowLayout { - HButton { - icon.name: "upload-cancel" - icon.color: theme.colors.negativeBackground - padded: false - - onClicked: { - // Python might take a sec to cancel, but we want - // immediate visual feedback - transfer.height = 0 - // Python will delete this model item on cancel - py.call(py.getattr(model.task, "cancel")) - } + HIcon { + svgName: "uploading" + colorize: + status === "Error" ? theme.colors.negativeBackground : + status === "Paused" ? theme.colors.middleBackground : + theme.icons.colorize Layout.preferredWidth: theme.baseElementsHeight Layout.fillHeight: true @@ -38,51 +38,33 @@ HColumnLayout { elide: expand ? Text.ElideNone : Text.ElideRight wrapMode: expand ? Text.Wrap : Text.NoWrap - color: model.status === "Error" ? + color: status === "Error" ? theme.colors.errorText : theme.colors.text text: - model.status === "Uploading" ? - qsTr("Uploading %1...").arg(fileName) : + status === "Uploading" ? fileName : - model.status === "Caching" ? + status === "Caching" ? qsTr("Caching %1...").arg(fileName) : - model.status === "UploadingThumbnail" ? - qsTr("Uploading thumbnail for %1...").arg(fileName) : + model.error === "MatrixForbidden" ? + qsTr("Forbidden file type or quota exceeded: %1") + .arg(fileName) : - model.status === "CachingThumbnail" ? - qsTr("Caching thumbnail for %1...").arg(fileName) : + model.error === "MatrixTooLarge" ? + qsTr("Too large for this server: %1").arg(fileName) : - model.status === "Error" ? ( - model.error === "MatrixForbidden" ? - qsTr("Forbidden file type or quota exceeded: %1") - .arg(fileName) : + model.error === "IsADirectoryError" ? + qsTr("Can't upload folders, need a file: %1").arg(filePath) : - model.error === "MatrixTooLarge" ? - qsTr("Too large for this server: %1") - .arg(fileName) : + model.error === "FileNotFoundError" ? + qsTr("Non-existant file: %1").arg(filePath) : - model.error === "IsADirectoryError" ? - qsTr("Can't upload folders, need a file: %1") - .arg(filePath) : + model.error === "PermissionError" ? + qsTr("No permission to read this file: %1").arg(filePath) : - model.error === "FileNotFoundError" ? - qsTr("Non-existant file: %1") - .arg(filePath) : - - model.error === "PermissionError" ? - qsTr("No permission to read this file: %1") - .arg(filePath) : - - qsTr("Unknown error for %1: %2 - %3") - .arg(filePath) - .arg(model.error) - .arg(model.error_args) - ) : - - qsTr("Invalid status for %1: %2") - .arg(fileName).arg(model.status) + qsTr("Unknown error for %1: %2 - %3") + .arg(filePath).arg(model.error).arg(model.error_args) topPadding: theme.spacing / 2 bottomPadding: topPadding @@ -92,7 +74,7 @@ HColumnLayout { Layout.fillWidth: true - property bool expand: model.status === "Error" + property bool expand: status === "Error" readonly property string fileName: model.filepath.split("/").slice(-1)[0] @@ -111,31 +93,27 @@ HColumnLayout { HSpacer {} - HLabel { - id: uploadCountLabel - visible: Layout.preferredWidth > 0 - text: qsTr("-%1 %2/s %3/%4") - .arg(model.time_left ? - Utils.formatDuration(msLeft) : "∞") - .arg(CppUtils.formattedBytes(model.speed)) - .arg(CppUtils.formattedBytes(uploaded)) - .arg(CppUtils.formattedBytes(model.total_size)) + Repeater { + model: [ + msLeft ? qsTr("-%1").arg(Utils.formatDuration(msLeft)) : "", - topPadding: theme.spacing / 2 - bottomPadding: topPadding - leftPadding: theme.spacing / 1.5 - rightPadding: leftPadding + speed ? qsTr("%1/s").arg(CppUtils.formattedBytes(speed)) : "", - Layout.preferredWidth: - model.status === "Uploading" ? implicitWidth : 0 + qsTr("%1/%2").arg(CppUtils.formattedBytes(uploaded)) + .arg(CppUtils.formattedBytes(totalSize)), + ] - property int msLeft: model.time_left || -1 - property int uploaded: model.uploaded + HLabel { + text: modelData + visible: text && Layout.preferredWidth > 0 + leftPadding: theme.spacing / 1.5 + rightPadding: leftPadding - Behavior on msLeft { HNumberAnimation { duration: 1000 } } - Behavior on uploaded { HNumberAnimation { duration: 1000 }} + Layout.preferredWidth: + status === "Uploading" ? implicitWidth : 0 - Behavior on Layout.preferredWidth { HNumberAnimation {} } + Behavior on Layout.preferredWidth { HNumberAnimation {} } + } } HButton { @@ -160,7 +138,7 @@ HColumnLayout { } Layout.preferredWidth: - model.status === "Uploading" ? + status === "Uploading" ? theme.baseElementsHeight : 0 Layout.fillHeight: true @@ -168,8 +146,24 @@ HColumnLayout { Behavior on Layout.preferredWidth { HNumberAnimation {} } } + HButton { + icon.name: "upload-cancel" + icon.color: theme.colors.negativeBackground + padded: false + + onClicked: { + // Python might take a sec to cancel, but we want + // immediate visual feedback + transfer.height = 0 + // Python will delete this model item on cancel + py.call(py.getattr(model.task, "cancel")) + } + + Layout.preferredWidth: theme.baseElementsHeight + Layout.fillHeight: true + } TapHandler { - onTapped: if (model.status !== "Error") + onTapped: if (status !== "Error") statusLabel.expand = ! statusLabel.expand } } @@ -177,13 +171,13 @@ HColumnLayout { HProgressBar { id: progressBar visible: Layout.maximumHeight !== 0 - indeterminate: model.status !== "Uploading" - value: model.uploaded - to: model.total_size + indeterminate: status !== "Uploading" + value: uploaded + to: totalSize // TODO: bake this in hprogressbar foregroundColor: - model.status === "Error" ? + status === "Error" ? theme.controls.progressBar.errorForeground : transfer.paused ? @@ -193,9 +187,8 @@ HColumnLayout { Layout.fillWidth: true Layout.maximumHeight: - model.status === "Error" && indeterminate ? 0 : -1 + status === "Error" && indeterminate ? 0 : -1 - Behavior on value { HNumberAnimation { duration: 1000 } } Behavior on Layout.maximumHeight { HNumberAnimation {} } } } diff --git a/src/qml/Chat/FileTransfer/TransferList.qml b/src/qml/Chat/FileTransfer/TransferList.qml index cba0f5d3..1809c243 100644 --- a/src/qml/Chat/FileTransfer/TransferList.qml +++ b/src/qml/Chat/FileTransfer/TransferList.qml @@ -12,15 +12,15 @@ Rectangle { property int delegateHeight: 0 readonly property var firstDelegate: - uploadsList.contentItem.visibleChildren[0] + transferList.contentItem.visibleChildren[0] - readonly property alias uploadsCount: uploadsList.count + readonly property alias transferCount: transferList.count Behavior on implicitHeight { HNumberAnimation {} } HListView { - id: uploadsList + id: transferList anchors.fill: parent model: HListModel { @@ -28,6 +28,6 @@ Rectangle { source: modelSources[["Upload", chatPage.roomId]] || [] } - delegate: Transfer { width: uploadsList.width } + delegate: Transfer { width: transferList.width } } }