*
* Note that this is only about *intent*. The file could still be out of date,
* or not have been synced for other reasons, like errors.
+ *
+ * NOTE: The numerical values and ordering of this enum are relevant.
*/
enum class VfsItemAvailability {
/** The item and all its subitems are hydrated and pinned AlwaysLocal.
*
* This guarantees that all contents will be kept in sync.
*/
- AlwaysLocal,
+ AlwaysLocal = 0,
/** The item and all its subitems are hydrated.
*
*
* A folder with no file contents will have this availability.
*/
- AllHydrated,
+ AllHydrated = 1,
- /** There are dehydrated items but the pin state isn't all OnlineOnly.
+ /** There are dehydrated and hydrated items.
*
* This would happen if a dehydration happens to a Unspecified item that
* used to be hydrated.
*/
- SomeDehydrated,
+ Mixed = 2,
+
+ /** There are only dehydrated items but the pin state isn't all OnlineOnly.
+ */
+ AllDehydrated = 3,
/** The item and all its subitems are dehydrated and OnlineOnly.
*
* This guarantees that contents will not take up space.
*/
- OnlineOnly,
+ OnlineOnly = 4,
};
}
return _setFileRecordLocalMetadataQuery.exec();
}
-Optional<bool> SyncJournalDb::hasDehydratedFiles(const QByteArray &filename)
+Optional<SyncJournalDb::HasHydratedDehydrated> SyncJournalDb::hasHydratedOrDehydratedFiles(const QByteArray &filename)
{
QMutexLocker locker(&_mutex);
if (!checkConnect())
return {};
auto &query = _countDehydratedFilesQuery;
- static_assert(ItemTypeVirtualFile == 4 && ItemTypeVirtualFileDownload == 5, "");
if (!query.initOrReset(QByteArrayLiteral(
- "SELECT count(*) FROM metadata"
- " WHERE (" IS_PREFIX_PATH_OR_EQUAL("?1", "path") " OR ?1 == '')"
- " AND (type == 4 OR type == 5);"), _db)) {
+ "SELECT DISTINCT type FROM metadata"
+ " WHERE (" IS_PREFIX_PATH_OR_EQUAL("?1", "path") " OR ?1 == '');"), _db)) {
return {};
}
if (!query.exec())
return {};
- if (!query.next().hasData)
- return {};
+ HasHydratedDehydrated result;
+ forever {
+ auto next = query.next();
+ if (!next.ok)
+ return {};
+ if (!next.hasData)
+ break;
+ auto type = static_cast<ItemType>(query.intValue(0));
+ if (type == ItemTypeFile || type == ItemTypeVirtualFileDehydration)
+ result.hasHydrated = true;
+ if (type == ItemTypeVirtualFile || type == ItemTypeVirtualFileDownload)
+ result.hasDehydrated = true;
+ }
- return query.intValue(0) > 0;
+ return result;
}
static void toDownloadInfo(SqlQuery &query, SyncJournalDb::DownloadInfo *res)
bool updateLocalMetadata(const QString &filename,
qint64 modtime, qint64 size, quint64 inode);
+ /// Return value for hasHydratedOrDehydratedFiles()
+ struct HasHydratedDehydrated
+ {
+ bool hasHydrated = false;
+ bool hasDehydrated = false;
+ };
+
/** Returns whether the item or any subitems are dehydrated */
- Optional<bool> hasDehydratedFiles(const QByteArray &filename);
+ Optional<HasHydratedDehydrated> hasHydratedOrDehydratedFiles(const QByteArray &filename);
bool exists();
void walCheckpoint();
{
auto pin = _setupParams.journal->internalPinStates().effectiveForPathRecursive(pinPath.toUtf8());
// not being able to retrieve the pin state isn't too bad
- Optional<bool> hasDehydrated = _setupParams.journal->hasDehydratedFiles(folderPath.toUtf8());
- if (!hasDehydrated)
+ auto hydrationStatus = _setupParams.journal->hasHydratedOrDehydratedFiles(folderPath.toUtf8());
+ if (!hydrationStatus)
return {};
- if (*hasDehydrated) {
+ if (hydrationStatus->hasDehydrated) {
+ if (hydrationStatus->hasHydrated)
+ return VfsItemAvailability::Mixed;
if (pin && *pin == PinState::OnlineOnly)
return VfsItemAvailability::OnlineOnly;
else
- return VfsItemAvailability::SomeDehydrated;
+ return VfsItemAvailability::AllDehydrated;
} else {
if (pin && *pin == PinState::AlwaysLocal)
return VfsItemAvailability::AlwaysLocal;
auto availabilityMenu = menu->addMenu(tr("Availability"));
auto availability = folder->vfs().availability(QString());
if (availability) {
- ac = availabilityMenu->addAction(Utility::vfsCurrentAvailabilityText(*availability, true));
+ ac = availabilityMenu->addAction(Utility::vfsCurrentAvailabilityText(*availability));
ac->setEnabled(false);
}
connect(ac, &QAction::triggered, this, [this]() { slotSetCurrentFolderAvailability(PinState::AlwaysLocal); });
ac = availabilityMenu->addAction(Utility::vfsFreeSpaceActionText());
- ac->setEnabled(!availability || *availability != VfsItemAvailability::OnlineOnly);
+ ac->setEnabled(!availability
+ || !(*availability == VfsItemAvailability::OnlineOnly
+ || *availability == VfsItemAvailability::AllDehydrated));
connect(ac, &QAction::triggered, this, [this]() { slotSetCurrentFolderAvailability(PinState::OnlineOnly); });
ac = menu->addAction(tr("Disable virtual file support..."));
return true;
}
-QString Utility::vfsCurrentAvailabilityText(VfsItemAvailability availability, bool forFolder)
+QString Utility::vfsCurrentAvailabilityText(VfsItemAvailability availability)
{
switch(availability) {
case VfsItemAvailability::AlwaysLocal:
return QCoreApplication::translate("utility", "Currently always available locally");
case VfsItemAvailability::AllHydrated:
return QCoreApplication::translate("utility", "Currently available locally");
- case VfsItemAvailability::SomeDehydrated:
- if (forFolder) {
- return QCoreApplication::translate("utility", "Currently some available online only");
- } else {
- return QCoreApplication::translate("utility", "Currently available online only");
- }
+ case VfsItemAvailability::Mixed:
+ return QCoreApplication::translate("utility", "Currently some available online only");
+ case VfsItemAvailability::AllDehydrated:
+ return QCoreApplication::translate("utility", "Currently available online only");
case VfsItemAvailability::OnlineOnly:
return QCoreApplication::translate("utility", "Currently available online only");
}
*
* This will be used in context menus to describe the current state.
*/
- QString vfsCurrentAvailabilityText(VfsItemAvailability availability, bool forFolder);
+ QString vfsCurrentAvailabilityText(VfsItemAvailability availability);
/** Translated text for "making items always available locally" */
QString vfsPinActionText();
auto merge = [](VfsItemAvailability lhs, VfsItemAvailability rhs) {
if (lhs == rhs)
return lhs;
- if (lhs == VfsItemAvailability::SomeDehydrated || rhs == VfsItemAvailability::SomeDehydrated
- || lhs == VfsItemAvailability::OnlineOnly || rhs == VfsItemAvailability::OnlineOnly) {
- return VfsItemAvailability::SomeDehydrated;
- }
- return VfsItemAvailability::AllHydrated;
+ auto l = int(lhs) < int(rhs) ? lhs : rhs; // reduce cases by sorting
+ auto r = int(lhs) < int(rhs) ? rhs : lhs;
+ if (l == VfsItemAvailability::AlwaysLocal && r == VfsItemAvailability::AllHydrated)
+ return VfsItemAvailability::AllHydrated;
+ if (l == VfsItemAvailability::AllDehydrated && r == VfsItemAvailability::OnlineOnly)
+ return VfsItemAvailability::AllDehydrated;
+ return VfsItemAvailability::Mixed;
};
- bool isFolderOrMultiple = false;
for (const auto &file : files) {
auto fileData = FileData::get(file);
- isFolderOrMultiple = QFileInfo(fileData.localPath).isDir();
auto availability = syncFolder->vfs().availability(fileData.folderRelativePath);
if (!availability)
- availability = VfsItemAvailability::SomeDehydrated; // db error
+ availability = VfsItemAvailability::Mixed; // db error
if (!combined) {
combined = availability;
} else {
}
}
ENFORCE(combined);
- if (files.size() > 1)
- isFolderOrMultiple = true;
// TODO: Should be a submenu, should use icons
auto makePinContextMenu = [&](bool makeAvailableLocally, bool freeSpace) {
listener->sendMessage(QLatin1String("MENU_ITEM:CURRENT_PIN:d:")
- + Utility::vfsCurrentAvailabilityText(*combined, isFolderOrMultiple));
+ + Utility::vfsCurrentAvailabilityText(*combined));
listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_AVAILABLE_LOCALLY:")
+ (makeAvailableLocally ? QLatin1String(":") : QLatin1String("d:"))
+ Utility::vfsPinActionText());
makePinContextMenu(false, true);
break;
case VfsItemAvailability::AllHydrated:
+ case VfsItemAvailability::Mixed:
makePinContextMenu(true, true);
break;
- case VfsItemAvailability::SomeDehydrated:
- makePinContextMenu(true, true);
- break;
+ case VfsItemAvailability::AllDehydrated:
case VfsItemAvailability::OnlineOnly:
makePinContextMenu(true, false);
break;
QCOMPARE(*vfs->availability("local/file1"), VfsItemAvailability::AlwaysLocal);
QCOMPARE(*vfs->availability("online"), VfsItemAvailability::OnlineOnly);
QCOMPARE(*vfs->availability("online/file1.nextcloud"), VfsItemAvailability::OnlineOnly);
- QCOMPARE(*vfs->availability("unspec"), VfsItemAvailability::SomeDehydrated);
- QCOMPARE(*vfs->availability("unspec/file1.nextcloud"), VfsItemAvailability::SomeDehydrated);
+ QCOMPARE(*vfs->availability("unspec"), VfsItemAvailability::AllDehydrated);
+ QCOMPARE(*vfs->availability("unspec/file1.nextcloud"), VfsItemAvailability::AllDehydrated);
// Subitem pin states can ruin "pure" availabilities
setPin("local/sub", PinState::OnlineOnly);
QCOMPARE(*vfs->availability("local"), VfsItemAvailability::AllHydrated);
setPin("online/sub", PinState::Unspecified);
- QCOMPARE(*vfs->availability("online"), VfsItemAvailability::SomeDehydrated);
+ QCOMPARE(*vfs->availability("online"), VfsItemAvailability::AllDehydrated);
triggerDownload(fakeFolder, "unspec/file1");
setPin("local/file2", PinState::OnlineOnly);
+ setPin("online/file2", PinState::AlwaysLocal);
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(*vfs->availability("unspec"), VfsItemAvailability::AllHydrated);
- QCOMPARE(*vfs->availability("local"), VfsItemAvailability::SomeDehydrated);
+ QCOMPARE(*vfs->availability("local"), VfsItemAvailability::Mixed);
+ QCOMPARE(*vfs->availability("online"), VfsItemAvailability::Mixed);
vfs->setPinState("local", PinState::AlwaysLocal);
vfs->setPinState("online", PinState::OnlineOnly);