Files

235 lines
7.5 KiB
QML

import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
Item {
id: root
implicitWidth: 700
implicitHeight: compact ? 156 : 212
property bool compact: false
property var shareTargetId: 0
property string direction: "send"
property string filename: ""
property string targetname: "Unknown device"
property real progressValue: 0
property string status: "kUnknown"
property bool isFinalStatus: false
property bool cancelEnabled: true
property int totalAttachmentsCount: 0
property int transferredAttachmentsCount: 0
property var totalBytes: 0
property string finalActionText: ""
signal cancelRequested()
signal finalActionRequested()
function baseName(path) {
if (!path || path.length === 0) {
return direction === "send" ? "Selected file" : transferSummary()
}
const normalized = decodeURIComponent(path).replace("file://", "")
const parts = normalized.split("/")
return parts.length === 0 ? normalized : parts[parts.length - 1]
}
function transferSummary() {
if (totalAttachmentsCount > 0) {
return totalAttachmentsCount === 1 ? "1 item" : totalAttachmentsCount + " items"
}
if (totalBytes > 0) {
return formatBytes(totalBytes)
}
return "Shared items"
}
function formatBytes(bytes) {
let value = Number(bytes)
if (!isFinite(value) || value <= 0) {
return ""
}
const units = ["B", "KB", "MB", "GB", "TB"]
let unitIndex = 0
while (value >= 1024 && unitIndex < units.length - 1) {
value /= 1024
unitIndex += 1
}
return (unitIndex === 0 ? Math.round(value) : value.toFixed(value >= 10 ? 1 : 2)) + " " + units[unitIndex]
}
function statusText() {
if (status === "kStartFailed") {
return "Transfer failed"
}
if (status === "kConnecting") {
return direction === "send" ? "Connecting" : "Preparing transfer"
}
if (status === "kAwaitingRemoteAcceptance") {
return "Waiting for receiver"
}
if (status === "kInProgress") {
const verb = direction === "send" ? "Sending" : "Receiving"
if (totalAttachmentsCount > 0) {
return verb + " " + Math.min(transferredAttachmentsCount + 1, totalAttachmentsCount) + " of " + totalAttachmentsCount + " items"
}
return verb
}
if (status === "kComplete") {
return direction === "send" ? "Sent" : "Received"
}
if (status === "kReject") {
return "Rejected"
}
if (status === "kCancelled") {
return "Cancelled"
}
if (status === "kTimedOut") {
return "Timed out"
}
if (isFinalStatus) {
return "Transfer failed"
}
return direction === "send" ? "Starting send" : "Preparing transfer"
}
Rectangle {
anchors.fill: parent
radius: 24
color: "#FFFFFF"
border.color: "#BCE5F5"
border.width: 1
}
RowLayout {
anchors.fill: parent
anchors.margins: root.compact ? 16 : 28
spacing: root.compact ? 16 : 24
Rectangle {
Layout.preferredWidth: root.compact ? 72 : 96
Layout.preferredHeight: root.compact ? 72 : 96
radius: root.compact ? 18 : 24
color: "#E8F7FC"
Image {
anchors.centerIn: parent
width: root.compact ? 44 : 60
height: root.compact ? 44 : 60
source: "qrc:/icons/file.svg"
fillMode: Image.PreserveAspectFit
sourceSize.width: width
sourceSize.height: height
smooth: true
antialiasing: true
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: root.compact ? 5 : 10
Text {
text: root.targetname
font.pointSize: root.compact ? 14 : 17
font.weight: 700
color: "#1A1C1E"
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: root.baseName(root.filename)
color: "#57707A"
font.pointSize: root.compact ? 10 : 12
elide: Text.ElideMiddle
Layout.fillWidth: true
}
ProgressBar {
id: transferProgress
Layout.fillWidth: true
from: 0
to: 1
value: Math.max(0, Math.min(1, root.progressValue))
background: Rectangle {
implicitHeight: root.compact ? 8 : 10
radius: root.compact ? 4 : 5
color: "#DDEEF5"
}
contentItem: Item {
Rectangle {
width: transferProgress.visualPosition * parent.width
height: root.compact ? 8 : 10
radius: root.compact ? 4 : 5
color: "#0D6D90"
}
}
}
RowLayout {
Layout.fillWidth: true
Text {
text: root.statusText()
color: "#57707A"
font.pointSize: root.compact ? 10 : 12
Layout.fillWidth: true
elide: Text.ElideRight
}
Text {
text: Math.round(Math.max(0, Math.min(1, root.progressValue)) * 100) + "%"
color: "#0D6D90"
font.pointSize: root.compact ? 11 : 13
font.weight: 700
}
}
RowLayout {
Layout.fillWidth: true
Item {
Layout.fillWidth: true
}
Button {
id: actionButton
visible: !root.isFinalStatus || root.finalActionText.length > 0
enabled: root.isFinalStatus || root.cancelEnabled
text: root.isFinalStatus ? root.finalActionText : "Cancel"
opacity: enabled ? 1 : 0.55
onClicked: {
if (root.isFinalStatus) {
root.finalActionRequested()
} else {
root.cancelRequested()
}
}
contentItem: Text {
text: actionButton.text
font.pointSize: root.compact ? 12 : 14
font.weight: 600
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
implicitWidth: root.compact ? 100 : 118
implicitHeight: root.compact ? 36 : 46
radius: root.compact ? 10 : 12
color: root.isFinalStatus
? (actionButton.hovered ? "#195871" : "#06384C")
: (actionButton.hovered ? "#B62828" : "#D94C4C")
}
}
}
}
}
}