file queue
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user