send progress dialog

This commit is contained in:
2026-04-28 15:21:30 +08:00
parent 7c884a2185
commit b1a81cd90b
7 changed files with 324 additions and 73 deletions

View File

@@ -49,6 +49,8 @@ void AppController::initialize()
this, &AppController::onPrepareUploadRequest); this, &AppController::onPrepareUploadRequest);
connect(m_server, &LocalSend::HttpServer::uploadRequest, connect(m_server, &LocalSend::HttpServer::uploadRequest,
this, &AppController::onUploadRequest); this, &AppController::onUploadRequest);
connect(m_server, &LocalSend::HttpServer::cancelRequest,
this, &AppController::onCancelRequest);
connect(m_sessions, &LocalSend::SessionManager::receiveSessionAccepted, connect(m_sessions, &LocalSend::SessionManager::receiveSessionAccepted,
this, &AppController::onSessionAccepted); this, &AppController::onSessionAccepted);
connect(m_sessions, &LocalSend::SessionManager::receiveSessionDeclined, connect(m_sessions, &LocalSend::SessionManager::receiveSessionDeclined,
@@ -185,6 +187,22 @@ void AppController::acceptReceive(const QString& sessionId)
return; return;
} }
m_currentReceiveSessionId = sessionId;
m_receiveProgressValue = 0.0;
m_receivedSize = 0;
m_currentReceiveFileIndex = 0;
m_currentReceiveFileName.clear();
m_totalReceiveFiles = session.files.size();
m_currentReceiveSenderAlias = session.sender.alias;
m_totalReceiveSize = 0;
m_receiveFileNames.clear();
for (auto it = session.files.constBegin(); it != session.files.constEnd(); ++it) {
m_totalReceiveSize += it->file.size;
m_receiveFileNames.insert(it.key(), it->file.fileName);
}
emit receivingChanged();
emit receiveProgressChanged();
QString baseDir = downloadPath(); QString baseDir = downloadPath();
QDir dir(baseDir); QDir dir(baseDir);
if (!dir.exists()) { if (!dir.exists()) {
@@ -198,6 +216,7 @@ void AppController::acceptReceive(const QString& sessionId)
qDebug() << "[AppController] File" << it.key() << "->" << filePath; qDebug() << "[AppController] File" << it.key() << "->" << filePath;
} }
m_server->addActiveSession(sessionId);
m_sessions->acceptReceiveSession(sessionId, destinationPaths); m_sessions->acceptReceiveSession(sessionId, destinationPaths);
} }
@@ -274,6 +293,8 @@ void AppController::onPrepareUploadRequest(const QString& httpSessionId,
LocalSend::Device device; LocalSend::Device device;
device.ip = sender.toString(); device.ip = sender.toString();
device.port = dto.info.port;
device.protocol = dto.info.protocol;
device.alias = dto.info.alias; device.alias = dto.info.alias;
device.fingerprint = dto.info.fingerprint; device.fingerprint = dto.info.fingerprint;
device.deviceModel = dto.info.deviceModel; device.deviceModel = dto.info.deviceModel;
@@ -337,6 +358,16 @@ void AppController::onUploadRequest(const QString& sessionId, const QString& fil
m_sessions->updateReceiveProgress(sessionId, fileId, written); m_sessions->updateReceiveProgress(sessionId, fileId, written);
if (sessionId == m_currentReceiveSessionId) {
m_currentReceiveFileName = m_receiveFileNames.value(fileId);
m_currentReceiveFileIndex++;
m_receivedSize += written;
if (m_totalReceiveSize > 0) {
m_receiveProgressValue = (static_cast<double>(m_receivedSize) / m_totalReceiveSize) * 100.0;
}
emit receiveProgressChanged();
}
if (written >= transfer.file.size) { if (written >= transfer.file.size) {
m_sessions->completeReceiveFile(sessionId, fileId); m_sessions->completeReceiveFile(sessionId, fileId);
} }
@@ -362,7 +393,16 @@ void AppController::onReceiveProgress(const QString& sessionId, const QString& f
void AppController::onReceiveCompleted(const QString& sessionId) void AppController::onReceiveCompleted(const QString& sessionId)
{ {
if (sessionId == m_currentReceiveSessionId) {
m_receiveProgressValue = 100.0;
m_currentReceiveFileIndex = m_totalReceiveFiles;
emit receiveProgressChanged();
}
emit receiveCompleted(sessionId); emit receiveCompleted(sessionId);
if (sessionId == m_currentReceiveSessionId) {
m_server->removeActiveSession(sessionId);
resetReceiveState();
}
} }
bool AppController::sending() const bool AppController::sending() const
@@ -399,6 +439,55 @@ void AppController::setReceivePin(const QString& pin)
} }
} }
bool AppController::receiving() const
{
return !m_currentReceiveSessionId.isEmpty();
}
double AppController::receiveProgress() const
{
return m_receiveProgressValue;
}
QString AppController::currentReceiveFileName() const
{
return m_currentReceiveFileName;
}
int AppController::currentReceiveFileIndex() const
{
return m_currentReceiveFileIndex;
}
int AppController::totalReceiveFiles() const
{
return m_totalReceiveFiles;
}
QString AppController::currentReceiveSenderAlias() const
{
return m_currentReceiveSenderAlias;
}
QString AppController::currentSendFileName() const
{
if (m_currentFileIndex >= 0 && m_currentFileIndex < m_pendingSendPaths.size()) {
QFileInfo info(m_pendingSendPaths[m_currentFileIndex]);
return info.fileName();
}
return QString();
}
int AppController::currentSendFileIndex() const
{
return m_currentFileIndex + 1;
}
int AppController::totalSendFiles() const
{
return m_pendingSendPaths.size();
}
void AppController::addFiles(const QStringList& filePaths) void AppController::addFiles(const QStringList& filePaths)
{ {
QMimeDatabase mimeDb; QMimeDatabase mimeDb;
@@ -470,8 +559,6 @@ void AppController::sendFiles(const QString& deviceFingerprint, const QStringLis
m_currentFileIndex = 0; m_currentFileIndex = 0;
m_sendProgress = 0.0; m_sendProgress = 0.0;
m_pinFirstAttempt = true; m_pinFirstAttempt = true;
emit sendingChanged();
emit sendProgressChanged();
qDebug() << "[AppController] sendFiles: device=" << deviceFingerprint qDebug() << "[AppController] sendFiles: device=" << deviceFingerprint
<< "files=" << filePaths.size(); << "files=" << filePaths.size();
@@ -487,8 +574,7 @@ void AppController::sendFiles(const QString& deviceFingerprint, const QStringLis
if (!info.exists()) { if (!info.exists()) {
emit sendError(QStringLiteral("File not found: ") + filePath); emit sendError(QStringLiteral("File not found: ") + filePath);
m_currentSendSessionId.clear(); resetSendState();
emit sendingChanged();
return; return;
} }
@@ -510,6 +596,9 @@ void AppController::sendFiles(const QString& deviceFingerprint, const QStringLis
}() }()
); );
emit sendingChanged();
emit sendProgressChanged();
LocalSend::PrepareUploadRequestDto request; LocalSend::PrepareUploadRequestDto request;
request.info = buildRegisterDto(); request.info = buildRegisterDto();
request.files = files; request.files = files;
@@ -525,15 +614,35 @@ void AppController::cancelSend()
} }
LocalSend::Device target = m_devices.value(m_currentSendDeviceFingerprint); LocalSend::Device target = m_devices.value(m_currentSendDeviceFingerprint);
m_httpClient->abortCurrentUpload();
m_httpClient->cancel(target, m_currentSendSessionId); m_httpClient->cancel(target, m_currentSendSessionId);
m_sessions->cancelSendSession(m_currentSendSessionId); m_sessions->cancelSendSession(m_currentSendSessionId);
m_currentSendSessionId.clear(); resetSendState();
m_pendingSendPaths.clear(); m_pendingSendPaths.clear();
m_pendingFilesList.clear(); m_pendingFilesList.clear();
m_sendProgress = 0.0; emit pendingFilesChanged();
emit sendingChanged(); }
emit sendProgressChanged();
void AppController::cancelReceive()
{
if (m_currentReceiveSessionId.isEmpty()) {
return;
}
qDebug() << "[AppController] cancelReceive, sessionId:" << m_currentReceiveSessionId;
LocalSend::ReceiveSession session = m_sessions->receiveSession(m_currentReceiveSessionId);
LocalSend::Device senderDevice = session.sender;
m_sessions->cancelReceiveSession(m_currentReceiveSessionId);
m_server->removeActiveSession(m_currentReceiveSessionId);
if (!senderDevice.ip.isEmpty() && senderDevice.port > 0) {
m_httpClient->cancel(senderDevice, m_currentReceiveSessionId);
}
resetReceiveState();
} }
void AppController::onPrepareUploadResponse(const LocalSend::PrepareUploadResponseDto& response) void AppController::onPrepareUploadResponse(const LocalSend::PrepareUploadResponseDto& response)
@@ -544,15 +653,13 @@ void AppController::onPrepareUploadResponse(const LocalSend::PrepareUploadRespon
LocalSend::SendSession session = m_sessions->sendSession(m_currentSendSessionId); LocalSend::SendSession session = m_sessions->sendSession(m_currentSendSessionId);
if (session.sessionId.isEmpty()) { if (session.sessionId.isEmpty()) {
emit sendError(QStringLiteral("Session not found")); emit sendError(QStringLiteral("Session not found"));
m_currentSendSessionId.clear(); resetSendState();
emit sendingChanged();
return; return;
} }
if (response.files.isEmpty()) { if (response.files.isEmpty()) {
emit sendError(QStringLiteral("Receiver declined the transfer")); emit sendError(QStringLiteral("Receiver declined the transfer"));
m_currentSendSessionId.clear(); resetSendState();
emit sendingChanged();
return; return;
} }
@@ -568,9 +675,8 @@ void AppController::onPrepareUploadResponse(const LocalSend::PrepareUploadRespon
void AppController::onPrepareUploadError(const QString& error) void AppController::onPrepareUploadError(const QString& error)
{ {
qWarning() << "[AppController] onPrepareUploadError:" << error; qWarning() << "[AppController] onPrepareUploadError:" << error;
resetSendState();
emit sendError(error); emit sendError(error);
m_currentSendSessionId.clear();
emit sendingChanged();
} }
void AppController::onPrepareUploadPinRequired() void AppController::onPrepareUploadPinRequired()
@@ -583,16 +689,14 @@ void AppController::onPrepareUploadPinRequired()
void AppController::onPrepareUploadTooManyAttempts() void AppController::onPrepareUploadTooManyAttempts()
{ {
qWarning() << "[AppController] onPrepareUploadTooManyAttempts"; qWarning() << "[AppController] onPrepareUploadTooManyAttempts";
resetSendState();
emit sendError(QStringLiteral("Too many PIN attempts")); emit sendError(QStringLiteral("Too many PIN attempts"));
m_currentSendSessionId.clear();
emit sendingChanged();
} }
void AppController::retryWithPin(const QString& pin) void AppController::retryWithPin(const QString& pin)
{ {
if (m_currentSendDeviceFingerprint.isEmpty() || !m_devices.contains(m_currentSendDeviceFingerprint)) { if (m_currentSendDeviceFingerprint.isEmpty() || !m_devices.contains(m_currentSendDeviceFingerprint)) {
m_currentSendSessionId.clear(); resetSendState();
emit sendingChanged();
return; return;
} }
@@ -637,6 +741,10 @@ void AppController::onUploadCompleted()
{ {
qDebug() << "[AppController] onUploadCompleted, file index:" << m_currentFileIndex; qDebug() << "[AppController] onUploadCompleted, file index:" << m_currentFileIndex;
if (m_currentSendSessionId.isEmpty()) {
return;
}
m_sessions->completeSendFile(m_currentSendSessionId, m_currentSendFileId); m_sessions->completeSendFile(m_currentSendSessionId, m_currentSendFileId);
m_currentFileIndex++; m_currentFileIndex++;
@@ -647,22 +755,32 @@ void AppController::onUploadCompleted()
qDebug() << "[AppController] All files sent successfully"; qDebug() << "[AppController] All files sent successfully";
m_sendProgress = 100.0; m_sendProgress = 100.0;
emit sendProgressChanged(); emit sendProgressChanged();
emit sendCompleted(m_currentSendSessionId); QString sessionId = m_currentSendSessionId;
m_currentSendSessionId.clear(); resetSendState();
m_pendingFilesList.clear();
m_pendingSendPaths.clear(); m_pendingSendPaths.clear();
emit sendingChanged(); m_pendingFilesList.clear();
emit pendingFilesChanged(); emit pendingFilesChanged();
emit sendCompleted(sessionId);
} }
} }
void AppController::onUploadError(const QString& error) void AppController::onUploadError(const QString& error)
{ {
if (m_currentSendSessionId.isEmpty()) {
return;
}
qWarning() << "[AppController] onUploadError:" << error; qWarning() << "[AppController] onUploadError:" << error;
emit sendError(error);
if (error == QStringLiteral("SESSION_CANCELLED")) {
m_sessions->cancelSendSession(m_currentSendSessionId);
resetSendState();
emit sendCanceled();
} else {
m_sessions->failSendFile(m_currentSendSessionId, m_currentSendFileId); m_sessions->failSendFile(m_currentSendSessionId, m_currentSendFileId);
m_currentSendSessionId.clear(); resetSendState();
emit sendingChanged(); emit sendError(error);
}
} }
void AppController::sendNextFile() void AppController::sendNextFile()
@@ -694,8 +812,7 @@ void AppController::sendNextFile()
if (token.isEmpty()) { if (token.isEmpty()) {
emit sendError(QStringLiteral("No token for file: ") + fileId); emit sendError(QStringLiteral("No token for file: ") + fileId);
m_currentSendSessionId.clear(); resetSendState();
emit sendingChanged();
return; return;
} }
@@ -715,3 +832,50 @@ LocalSend::RegisterDto AppController::buildRegisterDto() const
dto.download = false; dto.download = false;
return dto; return dto;
} }
void AppController::onCancelRequest(const QString& sessionId)
{
qDebug() << "[AppController] onCancelRequest, sessionId:" << sessionId;
if (sessionId == m_currentSendSessionId) {
m_httpClient->abortCurrentUpload();
m_sessions->cancelSendSession(m_currentSendSessionId);
resetSendState();
emit sendCanceled();
return;
}
if (sessionId == m_currentReceiveSessionId) {
m_sessions->cancelReceiveSession(m_currentReceiveSessionId);
m_server->removeActiveSession(m_currentReceiveSessionId);
resetReceiveState();
emit receiveError(sessionId, QStringLiteral("Sender canceled the transfer"));
return;
}
}
void AppController::resetSendState()
{
m_currentSendSessionId.clear();
m_currentSendFileId.clear();
m_currentSendDeviceFingerprint.clear();
m_currentFileIndex = 0;
m_sendProgress = 0.0;
emit sendingChanged();
emit sendProgressChanged();
}
void AppController::resetReceiveState()
{
m_currentReceiveSessionId.clear();
m_receiveProgressValue = 0.0;
m_currentReceiveFileName.clear();
m_currentReceiveFileIndex = 0;
m_totalReceiveFiles = 0;
m_totalReceiveSize = 0;
m_receivedSize = 0;
m_currentReceiveSenderAlias.clear();
m_receiveFileNames.clear();
emit receivingChanged();
emit receiveProgressChanged();
}

View File

@@ -21,9 +21,18 @@ 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(QString currentSendFileName READ currentSendFileName NOTIFY sendProgressChanged)
Q_PROPERTY(int currentSendFileIndex READ currentSendFileIndex NOTIFY sendProgressChanged)
Q_PROPERTY(int totalSendFiles READ totalSendFiles NOTIFY sendingChanged)
Q_PROPERTY(QVariantList pendingFiles READ pendingFiles NOTIFY pendingFilesChanged) Q_PROPERTY(QVariantList pendingFiles READ pendingFiles NOTIFY pendingFilesChanged)
Q_PROPERTY(bool hasPendingFiles READ hasPendingFiles NOTIFY pendingFilesChanged) Q_PROPERTY(bool hasPendingFiles READ hasPendingFiles NOTIFY pendingFilesChanged)
Q_PROPERTY(QString receivePin READ receivePin WRITE setReceivePin NOTIFY receivePinChanged) Q_PROPERTY(QString receivePin READ receivePin WRITE setReceivePin NOTIFY receivePinChanged)
Q_PROPERTY(bool receiving READ receiving NOTIFY receivingChanged)
Q_PROPERTY(double receiveProgress READ receiveProgress NOTIFY receiveProgressChanged)
Q_PROPERTY(QString currentReceiveFileName READ currentReceiveFileName NOTIFY receiveProgressChanged)
Q_PROPERTY(int currentReceiveFileIndex READ currentReceiveFileIndex NOTIFY receiveProgressChanged)
Q_PROPERTY(int totalReceiveFiles READ totalReceiveFiles NOTIFY receivingChanged)
Q_PROPERTY(QString currentReceiveSenderAlias READ currentReceiveSenderAlias NOTIFY receivingChanged)
public: public:
explicit AppController(QObject* parent = nullptr); explicit AppController(QObject* parent = nullptr);
@@ -48,17 +57,28 @@ public:
bool sending() const; bool sending() const;
double sendProgress() const; double sendProgress() const;
QString currentSendFileName() const;
int currentSendFileIndex() const;
int totalSendFiles() const;
QVariantList pendingFiles() const; QVariantList pendingFiles() const;
bool hasPendingFiles() const; bool hasPendingFiles() const;
QString receivePin() const; QString receivePin() const;
void setReceivePin(const QString& pin); void setReceivePin(const QString& pin);
bool receiving() const;
double receiveProgress() const;
QString currentReceiveFileName() const;
int currentReceiveFileIndex() const;
int totalReceiveFiles() const;
QString currentReceiveSenderAlias() const;
Q_INVOKABLE void startDiscovery(); Q_INVOKABLE void startDiscovery();
Q_INVOKABLE void stopDiscovery(); Q_INVOKABLE void stopDiscovery();
Q_INVOKABLE void refreshDevices(); Q_INVOKABLE void refreshDevices();
Q_INVOKABLE void acceptReceive(const QString& sessionId); Q_INVOKABLE void acceptReceive(const QString& sessionId);
Q_INVOKABLE void declineReceive(const QString& sessionId); Q_INVOKABLE void declineReceive(const QString& sessionId);
Q_INVOKABLE void cancelReceive();
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 sendTo(const QString& deviceFingerprint);
@@ -85,8 +105,11 @@ signals:
void receiveError(const QString& sessionId, const QString& error); void receiveError(const QString& sessionId, const QString& error);
void sendCompleted(const QString& sessionId); void sendCompleted(const QString& sessionId);
void sendError(const QString& error); void sendError(const QString& error);
void sendCanceled();
void pinRequired(bool firstAttempt); void pinRequired(bool firstAttempt);
void receivePinChanged(); void receivePinChanged();
void receivingChanged();
void receiveProgressChanged();
private slots: private slots:
void onDeviceDiscovered(const LocalSend::Device& device); void onDeviceDiscovered(const LocalSend::Device& device);
@@ -110,6 +133,8 @@ private slots:
void onUploadCompleted(); void onUploadCompleted();
void onUploadError(const QString& error); void onUploadError(const QString& error);
void onCancelRequest(const QString& sessionId);
private: private:
LocalSend::Settings* m_settings = nullptr; LocalSend::Settings* m_settings = nullptr;
LocalSend::SecurityContext* m_security = nullptr; LocalSend::SecurityContext* m_security = nullptr;
@@ -129,8 +154,20 @@ private:
double m_sendProgress = 0.0; double m_sendProgress = 0.0;
bool m_pinFirstAttempt = true; bool m_pinFirstAttempt = true;
QString m_currentReceiveSessionId;
double m_receiveProgressValue = 0.0;
QString m_currentReceiveFileName;
int m_currentReceiveFileIndex = 0;
int m_totalReceiveFiles = 0;
qint64 m_totalReceiveSize = 0;
qint64 m_receivedSize = 0;
QString m_currentReceiveSenderAlias;
QMap<QString, QString> m_receiveFileNames;
LocalSend::InfoDto buildInfoDto() const; LocalSend::InfoDto buildInfoDto() const;
QString generateUniqueFilePath(const QString& baseDir, const QString& fileName) const; QString generateUniqueFilePath(const QString& baseDir, const QString& fileName) const;
void sendNextFile(); void sendNextFile();
LocalSend::RegisterDto buildRegisterDto() const; LocalSend::RegisterDto buildRegisterDto() const;
void resetSendState();
void resetReceiveState();
}; };

View File

@@ -14,7 +14,6 @@ ApplicationWindow {
property var currentFiles: [] property var currentFiles: []
property string currentSenderAlias: "" property string currentSenderAlias: ""
property string currentSenderIp: "" property string currentSenderIp: ""
property var receiveProgress: ({})
DropArea { DropArea {
id: dropArea id: dropArea
@@ -107,7 +106,7 @@ ApplicationWindow {
onAccepted: { onAccepted: {
appController.acceptReceive(currentSessionId) appController.acceptReceive(currentSessionId)
currentSessionId = "" receiveProgressDialog.open()
} }
onRejected: { onRejected: {
@@ -169,23 +168,43 @@ ApplicationWindow {
spacing: 12 spacing: 12
Label { Label {
text: qsTr("Receiving from %1...").arg(currentSenderAlias) text: qsTr("From: %1").arg(appController.currentReceiveSenderAlias)
font.bold: true
}
Label {
text: appController.currentReceiveFileName
? qsTr("%1 (%2/%3)").arg(appController.currentReceiveFileName)
.arg(appController.currentReceiveFileIndex)
.arg(appController.totalReceiveFiles)
: qsTr("Waiting for data...")
elide: Text.ElideMiddle
Layout.maximumWidth: 400
} }
ProgressBar { ProgressBar {
Layout.fillWidth: true Layout.fillWidth: true
from: 0 from: 0
to: 100 to: 100
value: calculateTotalProgress() value: appController.receiveProgress
} }
Label { Label {
text: qsTr("%1% complete").arg(Math.round(calculateTotalProgress())) text: qsTr("%1% complete").arg(Math.round(appController.receiveProgress))
color: palette.mid color: palette.mid
} }
} }
property var progressData: ({}) footer: DialogButtonBox {
Button {
text: qsTr("Cancel")
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
onClicked: {
appController.cancelReceive()
receiveProgressDialog.close()
}
}
}
} }
Dialog { Dialog {
@@ -199,7 +218,13 @@ ApplicationWindow {
spacing: 12 spacing: 12
Label { Label {
text: qsTr("Sending files...") text: appController.currentSendFileName
? qsTr("%1 (%2/%3)").arg(appController.currentSendFileName)
.arg(appController.currentSendFileIndex)
.arg(appController.totalSendFiles)
: qsTr("Preparing...")
elide: Text.ElideMiddle
Layout.maximumWidth: 400
} }
ProgressBar { ProgressBar {
@@ -244,36 +269,18 @@ ApplicationWindow {
} }
} }
function onReceiveProgress(sessionId, fileId, progress) {
if (sessionId === currentSessionId) {
receiveProgress[fileId] = progress
receiveProgress = Object.assign({}, receiveProgress)
receiveProgressDialog.progressData = receiveProgress
if (!receiveProgressDialog.visible) {
receiveProgressDialog.open()
}
}
}
function onReceiveCompleted(sessionId) { function onReceiveCompleted(sessionId) {
if (sessionId === currentSessionId) {
receiveProgressDialog.close() receiveProgressDialog.close()
receiveProgress = {}
currentSessionId = ""
}
} }
function onReceiveError(sessionId, error) { function onReceiveError(sessionId, error) {
if (sessionId === currentSessionId) {
receiveProgressDialog.close() receiveProgressDialog.close()
errorDialog.text = error errorDialog.text = error
errorDialog.open() errorDialog.open()
} }
}
function onSendProgress(progress) { function onSendingChanged() {
if (!sendProgressDialog.visible) { if (appController.sending && !sendProgressDialog.visible) {
sendProgressDialog.open() sendProgressDialog.open()
} }
} }
@@ -291,6 +298,12 @@ ApplicationWindow {
errorDialog.open() errorDialog.open()
} }
function onSendCanceled() {
sendProgressDialog.close()
errorDialog.text = qsTr("Transfer canceled by receiver")
errorDialog.open()
}
function onPinRequired(firstAttempt) { function onPinRequired(firstAttempt) {
pinDialog.isFirstAttempt = firstAttempt pinDialog.isFirstAttempt = firstAttempt
pinDialog.open() pinDialog.open()
@@ -369,6 +382,7 @@ ApplicationWindow {
onAccepted: submitPin() onAccepted: submitPin()
onRejected: { onRejected: {
appController.cancelSend() appController.cancelSend()
sendProgressDialog.close()
close() close()
} }
@@ -436,20 +450,6 @@ ApplicationWindow {
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB" return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB"
} }
function calculateTotalProgress() {
if (!currentFiles || currentFiles.length === 0) return 0
var total = 0
var count = 0
for (var i = 0; i < currentFiles.length; i++) {
var fileId = currentFiles[i].id
if (receiveProgress[fileId] !== undefined) {
total += receiveProgress[fileId] * 100
count++
}
}
return count > 0 ? total / currentFiles.length : 0
}
Component { Component {
id: homePageComponent id: homePageComponent
Page { Page {

View File

@@ -26,6 +26,7 @@ public:
void uploadFile(const Device& device, const QString& sessionId, const QString& fileId, void uploadFile(const Device& device, const QString& sessionId, const QString& fileId,
const QString& token, const QString& filePath); const QString& token, const QString& filePath);
void cancel(const Device& device, const QString& sessionId); void cancel(const Device& device, const QString& sessionId);
void abortCurrentUpload();
signals: signals:
void infoReceived(const InfoDto& info); void infoReceived(const InfoDto& info);
@@ -43,6 +44,7 @@ signals:
private: private:
QNetworkAccessManager* m_manager = nullptr; QNetworkAccessManager* m_manager = nullptr;
QSslConfiguration m_sslConfig; QSslConfiguration m_sslConfig;
QNetworkReply* m_currentUploadReply = nullptr;
QNetworkReply* sendGet(const QUrl& url); QNetworkReply* sendGet(const QUrl& url);
QNetworkReply* sendPost(const QUrl& url, const QByteArray& data); QNetworkReply* sendPost(const QUrl& url, const QByteArray& data);

View File

@@ -40,6 +40,9 @@ public:
void respondToPrepareUpload(const QString& sessionId, bool accepted, void respondToPrepareUpload(const QString& sessionId, bool accepted,
const QMap<QString, QString>& tokens = {}); const QMap<QString, QString>& tokens = {});
void addActiveSession(const QString& sessionId);
void removeActiveSession(const QString& sessionId);
signals: signals:
void registerRequest(const RegisterDto& dto, const QHostAddress& sender); void registerRequest(const RegisterDto& dto, const QHostAddress& sender);
void prepareUploadRequest(const QString& sessionId, const PrepareUploadRequestDto& dto, void prepareUploadRequest(const QString& sessionId, const PrepareUploadRequestDto& dto,
@@ -59,6 +62,7 @@ private:
std::shared_ptr<QPromise<QHttpServerResponse>> promise; std::shared_ptr<QPromise<QHttpServerResponse>> promise;
}; };
QMap<QString, PendingPrepareUpload> m_pendingPrepareUploads; QMap<QString, PendingPrepareUpload> m_pendingPrepareUploads;
QSet<QString> m_activeSessions;
#endif #endif
InfoDto m_localInfo; InfoDto m_localInfo;

View File

@@ -185,6 +185,7 @@ void HttpClient::uploadFile(const Device& device, const QString& sessionId,
} }
QNetworkReply* reply = m_manager->post(request, file); QNetworkReply* reply = m_manager->post(request, file);
m_currentUploadReply = reply;
file->setParent(reply); file->setParent(reply);
connect(reply, &QNetworkReply::uploadProgress, this, [this, fileSize](qint64 sent, qint64) { connect(reply, &QNetworkReply::uploadProgress, this, [this, fileSize](qint64 sent, qint64) {
@@ -192,9 +193,16 @@ void HttpClient::uploadFile(const Device& device, const QString& sessionId,
}); });
connect(reply, &QNetworkReply::finished, this, [this, reply, filePath]() { connect(reply, &QNetworkReply::finished, this, [this, reply, filePath]() {
m_currentUploadReply = nullptr;
reply->deleteLater(); reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode == 410) {
qWarning() << "[HttpClient] uploadFile: session cancelled (410)";
emit uploadError(QStringLiteral("SESSION_CANCELLED"));
return;
}
qWarning() << "[HttpClient] uploadFile error:" << reply->errorString(); qWarning() << "[HttpClient] uploadFile error:" << reply->errorString();
emit uploadError(reply->errorString()); emit uploadError(reply->errorString());
return; return;
@@ -217,5 +225,13 @@ void HttpClient::cancel(const Device& device, const QString& sessionId)
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
} }
void HttpClient::abortCurrentUpload()
{
if (m_currentUploadReply) {
m_currentUploadReply->abort();
m_currentUploadReply = nullptr;
}
}
} }

View File

@@ -39,6 +39,16 @@ void HttpServer::setReceivePin(const QString& pin)
m_pinAttempts.clear(); m_pinAttempts.clear();
} }
void HttpServer::addActiveSession(const QString& sessionId)
{
m_activeSessions.insert(sessionId);
}
void HttpServer::removeActiveSession(const QString& sessionId)
{
m_activeSessions.remove(sessionId);
}
bool HttpServer::checkPin(const QHttpServerRequest& request, const QHostAddress& peer) bool HttpServer::checkPin(const QHttpServerRequest& request, const QHostAddress& peer)
{ {
if (m_receivePin.isEmpty()) { if (m_receivePin.isEmpty()) {
@@ -289,6 +299,14 @@ QHttpServerResponse HttpServer::handleUploadRequest(const QHttpServerRequest& re
return QHttpServerResponse(QHttpServerResponse::StatusCode::BadRequest); return QHttpServerResponse(QHttpServerResponse::StatusCode::BadRequest);
} }
if (!m_activeSessions.contains(sessionId)) {
QJsonObject errorObj;
errorObj[QStringLiteral("error")] = QStringLiteral("session_not_found");
errorObj[QStringLiteral("message")] = QStringLiteral("Session not found or has been cancelled");
return QHttpServerResponse(QJsonDocument(errorObj).toJson(QJsonDocument::Compact),
QHttpServerResponse::StatusCode::Gone);
}
emit uploadRequest(sessionId, fileId, token, request.body()); emit uploadRequest(sessionId, fileId, token, request.body());
return QHttpServerResponse(QHttpServerResponse::StatusCode::Ok); return QHttpServerResponse(QHttpServerResponse::StatusCode::Ok);
@@ -360,6 +378,16 @@ bool HttpServer::isRunning() const
return false; return false;
} }
void HttpServer::addActiveSession(const QString& sessionId)
{
Q_UNUSED(sessionId)
}
void HttpServer::removeActiveSession(const QString& sessionId)
{
Q_UNUSED(sessionId)
}
} }