Upload UI/code improvements
This commit is contained in:
parent
54df551b08
commit
4ada039384
3
src/icons/thin/uploading.svg
Normal file
3
src/icons/thin/uploading.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m16 16h-3v5h-2v-5h-3l4-4zm3.479-5.908c-.212-3.951-3.473-7.092-7.479-7.092s-7.267 3.141-7.479 7.092c-2.57.463-4.521 2.706-4.521 5.408 0 3.037 2.463 5.5 5.5 5.5h3.5v-2h-3.5c-1.93 0-3.5-1.57-3.5-3.5 0-2.797 2.479-3.833 4.433-3.72-.167-4.218 2.208-6.78 5.567-6.78 3.453 0 5.891 2.797 5.567 6.78 1.745-.046 4.433.751 4.433 3.72 0 1.93-1.57 3.5-3.5 3.5h-3.5v2h3.5c3.037 0 5.5-2.463 5.5-5.5 0-2.702-1.951-4.945-4.521-5.408z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 524 B |
|
@ -305,7 +305,11 @@ class MatrixClient(nio.AsyncClient):
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
log.warning(f"Failed thumbnailing {path}: {err}")
|
log.warning(f"Failed thumbnailing {path}: {err}")
|
||||||
else:
|
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:
|
try:
|
||||||
thumb_url, _, thumb_crypt_dict = await self.upload(
|
thumb_url, _, thumb_crypt_dict = await self.upload(
|
||||||
|
@ -317,7 +321,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
except MatrixError as err:
|
except MatrixError as err:
|
||||||
log.warning(f"Failed uploading thumbnail {path}: {err}")
|
log.warning(f"Failed uploading thumbnail {path}: {err}")
|
||||||
else:
|
else:
|
||||||
upload_item.status = UploadStatus.CachingThumbnail
|
upload_item.status = UploadStatus.Caching
|
||||||
|
|
||||||
await Thumbnail.from_bytes(
|
await Thumbnail.from_bytes(
|
||||||
self.backend.media_cache,
|
self.backend.media_cache,
|
||||||
|
|
|
@ -105,11 +105,9 @@ class Member(ModelItem):
|
||||||
|
|
||||||
|
|
||||||
class UploadStatus(AutoStrEnum):
|
class UploadStatus(AutoStrEnum):
|
||||||
Uploading = auto()
|
Uploading = auto()
|
||||||
Caching = auto()
|
Caching = auto()
|
||||||
UploadingThumbnail = auto()
|
Error = auto()
|
||||||
CachingThumbnail = auto()
|
|
||||||
Error = auto()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
|
@ -29,7 +29,7 @@ HSplitView {
|
||||||
TransferList {
|
TransferList {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumHeight: implicitHeight
|
Layout.minimumHeight: implicitHeight
|
||||||
Layout.preferredHeight: implicitHeight * uploadsCount
|
Layout.preferredHeight: implicitHeight * transferCount
|
||||||
Layout.maximumHeight: chatSplitView.height / 6
|
Layout.maximumHeight: chatSplitView.height / 6
|
||||||
|
|
||||||
Behavior on Layout.preferredHeight { HNumberAnimation {} }
|
Behavior on Layout.preferredHeight { HNumberAnimation {} }
|
||||||
|
|
|
@ -5,29 +5,29 @@ import "../../utils.js" as Utils
|
||||||
|
|
||||||
HColumnLayout {
|
HColumnLayout {
|
||||||
id: transfer
|
id: transfer
|
||||||
width: uploadsList.width
|
|
||||||
|
|
||||||
|
|
||||||
property bool guiPaused: false
|
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 {} }
|
Behavior on height { HNumberAnimation {} }
|
||||||
|
|
||||||
HRowLayout {
|
HRowLayout {
|
||||||
HButton {
|
HIcon {
|
||||||
icon.name: "upload-cancel"
|
svgName: "uploading"
|
||||||
icon.color: theme.colors.negativeBackground
|
colorize:
|
||||||
padded: false
|
status === "Error" ? theme.colors.negativeBackground :
|
||||||
|
status === "Paused" ? theme.colors.middleBackground :
|
||||||
onClicked: {
|
theme.icons.colorize
|
||||||
// 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.preferredWidth: theme.baseElementsHeight
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
@ -38,51 +38,33 @@ HColumnLayout {
|
||||||
elide: expand ? Text.ElideNone : Text.ElideRight
|
elide: expand ? Text.ElideNone : Text.ElideRight
|
||||||
wrapMode: expand ? Text.Wrap : Text.NoWrap
|
wrapMode: expand ? Text.Wrap : Text.NoWrap
|
||||||
|
|
||||||
color: model.status === "Error" ?
|
color: status === "Error" ?
|
||||||
theme.colors.errorText : theme.colors.text
|
theme.colors.errorText : theme.colors.text
|
||||||
|
|
||||||
text:
|
text:
|
||||||
model.status === "Uploading" ?
|
status === "Uploading" ? fileName :
|
||||||
qsTr("Uploading %1...").arg(fileName) :
|
|
||||||
|
|
||||||
model.status === "Caching" ?
|
status === "Caching" ?
|
||||||
qsTr("Caching %1...").arg(fileName) :
|
qsTr("Caching %1...").arg(fileName) :
|
||||||
|
|
||||||
model.status === "UploadingThumbnail" ?
|
model.error === "MatrixForbidden" ?
|
||||||
qsTr("Uploading thumbnail for %1...").arg(fileName) :
|
qsTr("Forbidden file type or quota exceeded: %1")
|
||||||
|
.arg(fileName) :
|
||||||
|
|
||||||
model.status === "CachingThumbnail" ?
|
model.error === "MatrixTooLarge" ?
|
||||||
qsTr("Caching thumbnail for %1...").arg(fileName) :
|
qsTr("Too large for this server: %1").arg(fileName) :
|
||||||
|
|
||||||
model.status === "Error" ? (
|
model.error === "IsADirectoryError" ?
|
||||||
model.error === "MatrixForbidden" ?
|
qsTr("Can't upload folders, need a file: %1").arg(filePath) :
|
||||||
qsTr("Forbidden file type or quota exceeded: %1")
|
|
||||||
.arg(fileName) :
|
|
||||||
|
|
||||||
model.error === "MatrixTooLarge" ?
|
model.error === "FileNotFoundError" ?
|
||||||
qsTr("Too large for this server: %1")
|
qsTr("Non-existant file: %1").arg(filePath) :
|
||||||
.arg(fileName) :
|
|
||||||
|
|
||||||
model.error === "IsADirectoryError" ?
|
model.error === "PermissionError" ?
|
||||||
qsTr("Can't upload folders, need a file: %1")
|
qsTr("No permission to read this file: %1").arg(filePath) :
|
||||||
.arg(filePath) :
|
|
||||||
|
|
||||||
model.error === "FileNotFoundError" ?
|
qsTr("Unknown error for %1: %2 - %3")
|
||||||
qsTr("Non-existant file: %1")
|
.arg(filePath).arg(model.error).arg(model.error_args)
|
||||||
.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)
|
|
||||||
|
|
||||||
topPadding: theme.spacing / 2
|
topPadding: theme.spacing / 2
|
||||||
bottomPadding: topPadding
|
bottomPadding: topPadding
|
||||||
|
@ -92,7 +74,7 @@ HColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
|
||||||
property bool expand: model.status === "Error"
|
property bool expand: status === "Error"
|
||||||
|
|
||||||
readonly property string fileName:
|
readonly property string fileName:
|
||||||
model.filepath.split("/").slice(-1)[0]
|
model.filepath.split("/").slice(-1)[0]
|
||||||
|
@ -111,31 +93,27 @@ HColumnLayout {
|
||||||
|
|
||||||
HSpacer {}
|
HSpacer {}
|
||||||
|
|
||||||
HLabel {
|
Repeater {
|
||||||
id: uploadCountLabel
|
model: [
|
||||||
visible: Layout.preferredWidth > 0
|
msLeft ? qsTr("-%1").arg(Utils.formatDuration(msLeft)) : "",
|
||||||
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))
|
|
||||||
|
|
||||||
topPadding: theme.spacing / 2
|
speed ? qsTr("%1/s").arg(CppUtils.formattedBytes(speed)) : "",
|
||||||
bottomPadding: topPadding
|
|
||||||
leftPadding: theme.spacing / 1.5
|
|
||||||
rightPadding: leftPadding
|
|
||||||
|
|
||||||
Layout.preferredWidth:
|
qsTr("%1/%2").arg(CppUtils.formattedBytes(uploaded))
|
||||||
model.status === "Uploading" ? implicitWidth : 0
|
.arg(CppUtils.formattedBytes(totalSize)),
|
||||||
|
]
|
||||||
|
|
||||||
property int msLeft: model.time_left || -1
|
HLabel {
|
||||||
property int uploaded: model.uploaded
|
text: modelData
|
||||||
|
visible: text && Layout.preferredWidth > 0
|
||||||
|
leftPadding: theme.spacing / 1.5
|
||||||
|
rightPadding: leftPadding
|
||||||
|
|
||||||
Behavior on msLeft { HNumberAnimation { duration: 1000 } }
|
Layout.preferredWidth:
|
||||||
Behavior on uploaded { HNumberAnimation { duration: 1000 }}
|
status === "Uploading" ? implicitWidth : 0
|
||||||
|
|
||||||
Behavior on Layout.preferredWidth { HNumberAnimation {} }
|
Behavior on Layout.preferredWidth { HNumberAnimation {} }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HButton {
|
HButton {
|
||||||
|
@ -160,7 +138,7 @@ HColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.preferredWidth:
|
Layout.preferredWidth:
|
||||||
model.status === "Uploading" ?
|
status === "Uploading" ?
|
||||||
theme.baseElementsHeight : 0
|
theme.baseElementsHeight : 0
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
@ -168,8 +146,24 @@ HColumnLayout {
|
||||||
Behavior on Layout.preferredWidth { HNumberAnimation {} }
|
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 {
|
TapHandler {
|
||||||
onTapped: if (model.status !== "Error")
|
onTapped: if (status !== "Error")
|
||||||
statusLabel.expand = ! statusLabel.expand
|
statusLabel.expand = ! statusLabel.expand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,13 +171,13 @@ HColumnLayout {
|
||||||
HProgressBar {
|
HProgressBar {
|
||||||
id: progressBar
|
id: progressBar
|
||||||
visible: Layout.maximumHeight !== 0
|
visible: Layout.maximumHeight !== 0
|
||||||
indeterminate: model.status !== "Uploading"
|
indeterminate: status !== "Uploading"
|
||||||
value: model.uploaded
|
value: uploaded
|
||||||
to: model.total_size
|
to: totalSize
|
||||||
|
|
||||||
// TODO: bake this in hprogressbar
|
// TODO: bake this in hprogressbar
|
||||||
foregroundColor:
|
foregroundColor:
|
||||||
model.status === "Error" ?
|
status === "Error" ?
|
||||||
theme.controls.progressBar.errorForeground :
|
theme.controls.progressBar.errorForeground :
|
||||||
|
|
||||||
transfer.paused ?
|
transfer.paused ?
|
||||||
|
@ -193,9 +187,8 @@ HColumnLayout {
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight:
|
Layout.maximumHeight:
|
||||||
model.status === "Error" && indeterminate ? 0 : -1
|
status === "Error" && indeterminate ? 0 : -1
|
||||||
|
|
||||||
Behavior on value { HNumberAnimation { duration: 1000 } }
|
|
||||||
Behavior on Layout.maximumHeight { HNumberAnimation {} }
|
Behavior on Layout.maximumHeight { HNumberAnimation {} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,15 +12,15 @@ Rectangle {
|
||||||
property int delegateHeight: 0
|
property int delegateHeight: 0
|
||||||
|
|
||||||
readonly property var firstDelegate:
|
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 {} }
|
Behavior on implicitHeight { HNumberAnimation {} }
|
||||||
|
|
||||||
HListView {
|
HListView {
|
||||||
id: uploadsList
|
id: transferList
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
model: HListModel {
|
model: HListModel {
|
||||||
|
@ -28,6 +28,6 @@ Rectangle {
|
||||||
source: modelSources[["Upload", chatPage.roomId]] || []
|
source: modelSources[["Upload", chatPage.roomId]] || []
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Transfer { width: uploadsList.width }
|
delegate: Transfer { width: transferList.width }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user