Notify user when file is locked when local editing begins
authorClaudio Cambra <claudio.cambra@nextcloud.com>
Tue, 29 Nov 2022 18:07:00 +0000 (19:07 +0100)
committerClaudio Cambra <claudio.cambra@nextcloud.com>
Wed, 7 Dec 2022 11:45:31 +0000 (12:45 +0100)
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
src/gui/editlocallyjob.cpp
src/gui/editlocallyjob.h
src/gui/editlocallymanager.cpp

index 67c8cbdc05a7219ea82751ed6f143078ee16eef0..489b027edfb73626d1f4dc19774bfaa8f58e85d4 100644 (file)
@@ -136,6 +136,7 @@ void EditLocallyJob::proceedWithSetup()
     }
 
     _fileName = relPathSplit.last();
+    _folderRelativePath = _localFilePath.mid(_folderForFile->cleanPath().length() + 1);
 
     _folderForFile = findFolderForFile(_relPath, _userId);
 
@@ -542,17 +543,91 @@ void EditLocallyJob::openFile()
         return;
     }
 
-    const auto localFilePath = _localFilePath;
+    const auto localFilePathUrl = QUrl::fromLocalFile(_localFilePath);
     // In case the VFS mode is enabled and a file is not yet hydrated, we must call QDesktopServices::openUrl
     // from a separate thread, or, there will be a freeze. To avoid searching for a specific folder and checking
     // if the VFS is enabled - we just always call it from a separate thread.
-    QtConcurrent::run([localFilePath, this]() {
-        _accountState->account()->setLockFileState(_relPath, _folderForFile->journalDb(), SyncFileItem::LockStatus::LockedItem);
-        QDesktopServices::openUrl(QUrl::fromLocalFile(localFilePath));
+    QtConcurrent::run([localFilePathUrl, this]() {
+        if(QDesktopServices::openUrl(localFilePathUrl)) {
+            lockFile();
+        }
         Systray::instance()->destroyEditFileLocallyLoadingDialog();
     });
+}
+
+void EditLocallyJob::lockFile()
+{
+    Q_ASSERT(_accountState);
+    Q_ASSERT(_accountState->account());
+    Q_ASSERT(_folderForFile);
+
+    if (_accountState->account()->fileLockStatus(_folderForFile->journalDb(), _folderRelativePath) == SyncFileItem::LockStatus::LockedItem) {
+        fileLockSuccess(true);
+        return;
+    }
+
+    _folderConnections.append(connect(_accountState->account().data(), &Account::lockFileSuccess,
+                                      this, [this] {
+        _folderForFile->journalDb()->schedulePathForRemoteDiscovery(_relPath);
+        _folderForFile->scheduleThisFolderSoon();
+    }));
+    _folderConnections.append(connect(_folderForFile, &Folder::syncFinished,
+                                      this, [this](const OCC::SyncResult &result) {
+        Q_UNUSED(result)
+        fileLockSuccess();
+    }));
+    _folderConnections.append(connect(_accountState->account().data(), &Account::lockFileError,
+                                      this, &EditLocallyJob::fileLockError));
+
+    _folderForFile->accountState()->account()->setLockFileState(_relPath,
+                                                                _folderForFile->journalDb(),
+                                                                SyncFileItem::LockStatus::LockedItem);
+}
+
+void EditLocallyJob::disconnectFolderSignals()
+{
+    for (const auto &connection : qAsConst(_folderConnections)) {
+        disconnect(connection);
+    }
+}
+
+void EditLocallyJob::fileLockSuccess(const bool existingLock)
+{
+    qCDebug(lcEditLocallyJob()) << "File lock succeeded, showing notification" << _relPath;
+
+    SyncJournalFileRecord rec;
+    Q_ASSERT(_folderForFile->journalDb()->getFileRecord(_folderRelativePath, &rec));
+    Q_ASSERT(rec.isValid());
+    Q_ASSERT(rec._lockstate._locked);
+
+    const auto lockExpirationTime = rec._lockstate._lockTime + rec._lockstate._lockTimeout;
+    const auto remainingTime = QDateTime::currentDateTime().secsTo(QDateTime::fromSecsSinceEpoch(lockExpirationTime));
+
+    static constexpr auto SECONDS_PER_MINUTE = 60;
+    const auto remainingTimeInMinutes = static_cast<int>(remainingTime > 0 ? remainingTime / SECONDS_PER_MINUTE : 0);
+
+    const auto notificationTitle = existingLock ? tr("File %1 already locked.") :
+                                                  tr("File %1 now locked.");
+
+    Systray::instance()->showMessage(notificationTitle.arg(_fileName),
+                         tr("Lock will last for %1 minutes. "
+                            "You can also unlock this file manually once you are finished editing.").arg(remainingTimeInMinutes),
+                         QSystemTrayIcon::Information);
+
+    disconnectFolderSignals();
+    Q_EMIT finished();
+}
+
+void EditLocallyJob::fileLockError(const QString &errorMessage)
+{
+    qCWarning(lcEditLocallyJob()) << "File lock failed, showing notification" << _relPath << errorMessage;
+
+    Systray::instance()->showMessage(tr("File %1 could not be locked."),
+                                     errorMessage,
+                                     QSystemTrayIcon::Warning);
 
-    Q_EMIT fileOpened();
+    disconnectFolderSignals();
+    Q_EMIT finished();
 }
 
 }
index a2293bffcd7b3b40295b13fc9285cb7f128c7e5b..9099804ad6b5076db52b23152064c2a8104dd8c1 100644 (file)
@@ -45,7 +45,7 @@ public:
 signals:
     void setupFinished();
     void error(const QString &message, const QString &informativeText);
-    void fileOpened();
+    void finished();
 
 public slots:
     void startSetup();
@@ -72,6 +72,11 @@ private slots:
     void slotDirectoryListingIterated(const QString &name, const QMap<QString, QString> &properties);
 
     void openFile();
+    void lockFile();
+
+    void fileLockSuccess(const bool existingLock = false);
+    void fileLockError(const QString &errorMessage);
+    void disconnectFolderSignals();
 
 private:
     [[nodiscard]] bool checkIfFileParentSyncIsNeeded(); // returns true if sync will be needed, false otherwise
@@ -90,9 +95,11 @@ private:
 
     QString _fileName;
     QString _localFilePath;
+    QString _folderRelativePath;
     Folder *_folderForFile = nullptr;
     std::unique_ptr<SimpleApiJob> _checkTokenJob;
     QMetaObject::Connection _syncTerminatedConnection = {};
+    QVector<QMetaObject::Connection> _folderConnections;
 };
 
 }
index 09776b31af89da454f97ac7deb0da3214dc0e767..c301d4b42f0118cef7a9b6f12d8b089c70594cdb 100644 (file)
@@ -83,7 +83,7 @@ void EditLocallyManager::createJob(const QString &userId,
 
     connect(job.data(), &EditLocallyJob::error,
             this, removeJob);
-    connect(job.data(), &EditLocallyJob::fileOpened,
+    connect(job.data(), &EditLocallyJob::finished,
             this, removeJob);
     connect(job.data(), &EditLocallyJob::setupFinished,
             job.data(), setupJob);