send progress dialog
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user