file queue

This commit is contained in:
2026-04-27 17:38:50 +08:00
parent bfe271550e
commit 2556c2db83
3 changed files with 226 additions and 15 deletions

View File

@@ -369,6 +369,65 @@ double AppController::sendProgress() const
return m_sendProgress; return m_sendProgress;
} }
QVariantList AppController::pendingFiles() const
{
return m_pendingFilesList;
}
bool AppController::hasPendingFiles() const
{
return !m_pendingFilesList.isEmpty();
}
void AppController::addFiles(const QStringList& filePaths)
{
QMimeDatabase mimeDb;
for (const QString& rawPath : filePaths) {
QString filePath = rawPath;
if (filePath.startsWith(QStringLiteral("file://"))) {
filePath = filePath.mid(7);
}
QFileInfo info(filePath);
if (!info.exists() || info.isDir()) {
continue;
}
QVariantMap file;
file[QStringLiteral("path")] = info.absoluteFilePath();
file[QStringLiteral("fileName")] = info.fileName();
file[QStringLiteral("size")] = info.size();
file[QStringLiteral("fileType")] = mimeDb.mimeTypeForFile(info).name();
m_pendingFilesList.append(file);
m_pendingSendPaths.append(info.absoluteFilePath());
}
emit pendingFilesChanged();
}
void AppController::removePendingFile(int index)
{
if (index < 0 || index >= m_pendingFilesList.size()) {
return;
}
m_pendingFilesList.removeAt(index);
m_pendingSendPaths.removeAt(index);
emit pendingFilesChanged();
}
void AppController::clearPendingFiles()
{
m_pendingFilesList.clear();
m_pendingSendPaths.clear();
emit pendingFilesChanged();
}
void AppController::sendTo(const QString& deviceFingerprint)
{
if (m_pendingSendPaths.isEmpty()) {
emit sendError(QStringLiteral("No files selected"));
return;
}
sendFiles(deviceFingerprint, m_pendingSendPaths);
}
void AppController::sendFiles(const QString& deviceFingerprint, const QStringList& filePaths) void AppController::sendFiles(const QString& deviceFingerprint, const QStringList& filePaths)
{ {
if (!m_devices.contains(deviceFingerprint)) { if (!m_devices.contains(deviceFingerprint)) {
@@ -387,7 +446,7 @@ void AppController::sendFiles(const QString& deviceFingerprint, const QStringLis
} }
m_currentSendDeviceFingerprint = deviceFingerprint; m_currentSendDeviceFingerprint = deviceFingerprint;
m_pendingFiles = filePaths; m_pendingSendPaths = filePaths;
m_currentFileIndex = 0; m_currentFileIndex = 0;
m_sendProgress = 0.0; m_sendProgress = 0.0;
emit sendingChanged(); emit sendingChanged();
@@ -449,7 +508,8 @@ void AppController::cancelSend()
m_sessions->cancelSendSession(m_currentSendSessionId); m_sessions->cancelSendSession(m_currentSendSessionId);
m_currentSendSessionId.clear(); m_currentSendSessionId.clear();
m_pendingFiles.clear(); m_pendingSendPaths.clear();
m_pendingFilesList.clear();
m_sendProgress = 0.0; m_sendProgress = 0.0;
emit sendingChanged(); emit sendingChanged();
emit sendProgressChanged(); emit sendProgressChanged();
@@ -496,7 +556,7 @@ void AppController::onUploadProgress(qint64 sent, qint64 total)
{ {
if (total > 0) { if (total > 0) {
double fileProgress = static_cast<double>(sent) / total; double fileProgress = static_cast<double>(sent) / total;
int totalFiles = m_pendingFiles.size(); int totalFiles = m_pendingSendPaths.size();
double overallProgress = (m_currentFileIndex + fileProgress) / totalFiles; double overallProgress = (m_currentFileIndex + fileProgress) / totalFiles;
m_sendProgress = overallProgress * 100.0; m_sendProgress = overallProgress * 100.0;
emit sendProgressChanged(); emit sendProgressChanged();
@@ -511,7 +571,7 @@ void AppController::onUploadCompleted()
m_currentFileIndex++; m_currentFileIndex++;
if (m_currentFileIndex < m_pendingFiles.size()) { if (m_currentFileIndex < m_pendingSendPaths.size()) {
sendNextFile(); sendNextFile();
} else { } else {
qDebug() << "[AppController] All files sent successfully"; qDebug() << "[AppController] All files sent successfully";
@@ -519,7 +579,10 @@ void AppController::onUploadCompleted()
emit sendProgressChanged(); emit sendProgressChanged();
emit sendCompleted(m_currentSendSessionId); emit sendCompleted(m_currentSendSessionId);
m_currentSendSessionId.clear(); m_currentSendSessionId.clear();
m_pendingFilesList.clear();
m_pendingSendPaths.clear();
emit sendingChanged(); emit sendingChanged();
emit pendingFilesChanged();
} }
} }
@@ -534,7 +597,7 @@ void AppController::onUploadError(const QString& error)
void AppController::sendNextFile() void AppController::sendNextFile()
{ {
if (m_currentFileIndex >= m_pendingFiles.size()) { if (m_currentFileIndex >= m_pendingSendPaths.size()) {
return; return;
} }
@@ -551,7 +614,7 @@ void AppController::sendNextFile()
} }
m_currentSendFileId = fileId; m_currentSendFileId = fileId;
QString filePath = m_pendingFiles[m_currentFileIndex]; QString filePath = m_pendingSendPaths[m_currentFileIndex];
QString token = session.files[fileId].token; QString token = session.files[fileId].token;
LocalSend::Device target = m_devices.value(m_currentSendDeviceFingerprint); LocalSend::Device target = m_devices.value(m_currentSendDeviceFingerprint);

View File

@@ -21,6 +21,8 @@ class AppController : public QObject
Q_PROPERTY(bool quickSave READ quickSave WRITE setQuickSave NOTIFY quickSaveChanged) Q_PROPERTY(bool quickSave READ quickSave WRITE setQuickSave NOTIFY quickSaveChanged)
Q_PROPERTY(bool sending READ sending NOTIFY sendingChanged) Q_PROPERTY(bool sending READ sending NOTIFY sendingChanged)
Q_PROPERTY(double sendProgress READ sendProgress NOTIFY sendProgressChanged) Q_PROPERTY(double sendProgress READ sendProgress NOTIFY sendProgressChanged)
Q_PROPERTY(QVariantList pendingFiles READ pendingFiles NOTIFY pendingFilesChanged)
Q_PROPERTY(bool hasPendingFiles READ hasPendingFiles NOTIFY pendingFilesChanged)
public: public:
explicit AppController(QObject* parent = nullptr); explicit AppController(QObject* parent = nullptr);
@@ -45,6 +47,8 @@ public:
bool sending() const; bool sending() const;
double sendProgress() const; double sendProgress() const;
QVariantList pendingFiles() const;
bool hasPendingFiles() const;
Q_INVOKABLE void startDiscovery(); Q_INVOKABLE void startDiscovery();
Q_INVOKABLE void stopDiscovery(); Q_INVOKABLE void stopDiscovery();
@@ -54,7 +58,11 @@ public:
Q_INVOKABLE void declineReceive(const QString& sessionId); Q_INVOKABLE void declineReceive(const QString& sessionId);
Q_INVOKABLE void sendFiles(const QString& deviceFingerprint, const QStringList& filePaths); Q_INVOKABLE void sendFiles(const QString& deviceFingerprint, const QStringList& filePaths);
Q_INVOKABLE void sendTo(const QString& deviceFingerprint);
Q_INVOKABLE void cancelSend(); Q_INVOKABLE void cancelSend();
Q_INVOKABLE void addFiles(const QStringList& filePaths);
Q_INVOKABLE void removePendingFile(int index);
Q_INVOKABLE void clearPendingFiles();
signals: signals:
void aliasChanged(); void aliasChanged();
@@ -65,6 +73,7 @@ signals:
void serverRunningChanged(); void serverRunningChanged();
void sendingChanged(); void sendingChanged();
void sendProgressChanged(); void sendProgressChanged();
void pendingFilesChanged();
void receiveRequest(const QString& sessionId, const QString& senderAlias, void receiveRequest(const QString& sessionId, const QString& senderAlias,
const QString& senderIp, const QVariantList& files); const QString& senderIp, const QVariantList& files);
void receiveProgress(const QString& sessionId, const QString& fileId, double progress); void receiveProgress(const QString& sessionId, const QString& fileId, double progress);
@@ -106,7 +115,8 @@ private:
QString m_currentSendSessionId; QString m_currentSendSessionId;
QString m_currentSendFileId; QString m_currentSendFileId;
QString m_currentSendDeviceFingerprint; QString m_currentSendDeviceFingerprint;
QStringList m_pendingFiles; QStringList m_pendingSendPaths;
QVariantList m_pendingFilesList;
int m_currentFileIndex = 0; int m_currentFileIndex = 0;
double m_sendProgress = 0.0; double m_sendProgress = 0.0;

View File

@@ -15,7 +15,63 @@ ApplicationWindow {
property string currentSenderAlias: "" property string currentSenderAlias: ""
property string currentSenderIp: "" property string currentSenderIp: ""
property var receiveProgress: ({}) property var receiveProgress: ({})
property string selectedDeviceFingerprint: ""
DropArea {
id: dropArea
anchors.fill: parent
z: 9999
onEntered: function(drag) {
if (drag.hasUrls) {
drag.accepted = true
dropOverlay.visible = true
} else {
drag.accepted = false
}
}
onDropped: function(drop) {
dropOverlay.visible = false
if (drop.hasUrls) {
var paths = []
for (var i = 0; i < drop.urls.length; i++) {
paths.push(drop.urls[i].toString())
}
if (paths.length > 0) {
appController.addFiles(paths)
}
}
}
onExited: {
dropOverlay.visible = false
}
Rectangle {
id: dropOverlay
visible: false
anchors.fill: parent
color: "#200080FF"
z: 10000
Label {
anchors.centerIn: parent
text: qsTr("Drop files here to add them")
font.pixelSize: 24
font.bold: true
color: "#0080FF"
}
Rectangle {
anchors.fill: parent
anchors.margins: 8
color: "transparent"
radius: 12
border.color: "#0080FF"
border.width: 3
}
}
}
StackView { StackView {
id: stackView id: stackView
@@ -28,15 +84,15 @@ ApplicationWindow {
FileDialog { FileDialog {
id: fileDialog id: fileDialog
title: qsTr("Select Files to Send") title: qsTr("Select Files")
fileMode: FileDialog.OpenFiles fileMode: FileDialog.OpenFiles
onAccepted: { onAccepted: {
var paths = [] var paths = []
for (var i = 0; i < selectedFiles.length; i++) { for (var i = 0; i < selectedFiles.length; i++) {
paths.push(selectedFiles[i].toString().replace("file://", "")) paths.push(selectedFiles[i].toString())
} }
if (paths.length > 0 && selectedDeviceFingerprint !== "") { if (paths.length > 0) {
appController.sendFiles(selectedDeviceFingerprint, paths) appController.addFiles(paths)
} }
} }
} }
@@ -311,6 +367,86 @@ ApplicationWindow {
anchors.margins: 16 anchors.margins: 16
spacing: 16 spacing: 16
ColumnLayout {
Layout.fillWidth: true
spacing: 8
RowLayout {
Layout.fillWidth: true
Label {
text: qsTr("Selected Files")
font.bold: true
font.pixelSize: 16
}
Item { Layout.fillWidth: true }
Button {
text: qsTr("Add Files")
onClicked: fileDialog.open()
}
Button {
text: qsTr("Clear All")
visible: appController.hasPendingFiles
onClicked: appController.clearPendingFiles()
}
}
ListView {
id: pendingFilesList
Layout.fillWidth: true
Layout.preferredHeight: Math.min(180, contentHeight)
model: appController.pendingFiles
spacing: 4
visible: appController.hasPendingFiles
clip: true
delegate: Pane {
width: ListView.view.width
padding: 8
background: Rectangle {
color: "transparent"
radius: 6
border.color: palette.mid
border.width: 1
}
RowLayout {
anchors.fill: parent
spacing: 8
Label {
text: modelData.fileName
Layout.fillWidth: true
elide: Text.ElideMiddle
}
Label {
text: formatSize(modelData.size)
color: palette.mid
font.pixelSize: 12
}
ToolButton {
text: "\u2715"
font.pixelSize: 14
onClicked: appController.removePendingFile(index)
}
}
}
}
Label {
visible: !appController.hasPendingFiles
text: qsTr("No files selected. Click \"Add Files\" or drag and drop files here.")
color: palette.mid
Layout.fillWidth: true
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
}
Label { Label {
text: qsTr("Nearby Devices") text: qsTr("Nearby Devices")
font.bold: true font.bold: true
@@ -345,6 +481,7 @@ ApplicationWindow {
Label { Label {
text: modelData.alias || modelData.ip text: modelData.alias || modelData.ip
font.bold: true font.bold: true
color: palette.text
} }
Label { Label {
text: "%1:%2".arg(modelData.ip).arg(modelData.port) text: "%1:%2".arg(modelData.ip).arg(modelData.port)
@@ -352,13 +489,14 @@ ApplicationWindow {
font.pixelSize: 12 font.pixelSize: 12
} }
} }
Button { Button {
visible: appController.hasPendingFiles && !appController.sending
text: qsTr("Send") text: qsTr("Send")
enabled: !appController.sending enabled: !appController.sending
onClicked: { onClicked: {
selectedDeviceFingerprint = modelData.fingerprint if (appController.hasPendingFiles && !appController.sending) {
fileDialog.open() appController.sendTo(modelData.fingerprint)
}
} }
} }
} }