소스 검색

Show only filenames in tray activity items, with full path in tooltip

Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
Claudio Cambra 4 년 전
부모
커밋
2a27882307
5개의 변경된 파일111개의 추가작업 그리고 58개의 파일을 삭제
  1. 4 0
      src/gui/tray/ActivityItem.qml
  2. 11 0
      src/gui/tray/activitydata.h
  3. 88 53
      src/gui/tray/activitylistmodel.cpp
  4. 1 0
      src/gui/tray/activitylistmodel.h
  5. 7 5
      src/gui/tray/usermodel.cpp

+ 4 - 0
src/gui/tray/ActivityItem.qml

@@ -20,6 +20,10 @@ MouseArea {
         anchors.fill: parent
         color: (parent.containsMouse ? Style.lightHover : "transparent")
     }
+
+    ToolTip.visible: containsMouse && displayLocation !== ""
+    ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
+    ToolTip.text: qsTr("In %1").arg(displayLocation)
         
     RowLayout {
         id: activityItem

+ 11 - 0
src/gui/tray/activitydata.h

@@ -61,11 +61,22 @@ public:
         SyncFileItemType
     };
 
+    struct RichSubjectParameter {
+        QString type;    // Required
+        QString id;      // Required
+        QString name;    // Required
+        QString path;    // Required (for files only)
+        QUrl link;    // Optional (files only)
+    };
+
     Type _type;
     qlonglong _id;
     QString _fileAction;
     QString _objectType;
     QString _subject;
+    QString _subjectRich;
+    QHash<QString, RichSubjectParameter> _subjectRichParameters;
+    QString _subjectDisplay;
     QString _message;
     QString _folder;
     QString _file;

+ 88 - 53
src/gui/tray/activitylistmodel.cpp

@@ -58,6 +58,7 @@ QHash<int, QByteArray> ActivityListModel::roleNames() const
     roles[DisplayPathRole] = "displayPath";
     roles[PathRole] = "path";
     roles[AbsolutePathRole] = "absolutePath";
+    roles[DisplayLocationRole] = "displayLocation";
     roles[LinkRole] = "link";
     roles[MessageRole] = "message";
     roles[ActionRole] = "type";
@@ -114,42 +115,19 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
     if (!ast && _accountState != ast.data())
         return QVariant();
 
-    switch (role) {
-    case DisplayPathRole:
-        if (!a._file.isEmpty()) {
-            auto folder = FolderMan::instance()->folder(a._folder);
-            QString relPath(a._file);
-            if (folder) {
-                relPath.prepend(folder->remotePath());
-            }
-            const auto localFiles = FolderMan::instance()->findFileInLocalFolders(relPath, ast->account());
-            if (localFiles.count() > 0) {
-                if (relPath.startsWith('/') || relPath.startsWith('\\')) {
-                    return relPath.remove(0, 1);
-                } else {
-                    return relPath;
-                }
-            }
-        }
-        return QString();
-    case PathRole:
+    const auto getFilePath = [&]() {
         if (!a._file.isEmpty()) {
             const auto folder = FolderMan::instance()->folder(a._folder);
 
-            QString relPath(a._file);
-            if (folder) {
-                relPath.prepend(folder->remotePath());
-            }
+            const QString relPath = folder ? folder->remotePath() + a._file : a._file;
 
-            // get relative path to the file so we can open it in the file manager
-            const auto localFiles = FolderMan::instance()->findFileInLocalFolders(QFileInfo(relPath).path(), ast->account());
+            const auto localFiles = FolderMan::instance()->findFileInLocalFolders(relPath, ast->account());
 
             if (localFiles.isEmpty()) {
                 return QString();
             }
 
-            // If this is an E2EE file or folder, pretend we got no path, this leads to
-            // hiding the share button which is what we want
+            // If this is an E2EE file or folder, pretend we got no path, hiding the share button which is what we want
             if (folder) {
                 SyncJournalFileRecord rec;
                 folder->journalDb()->getFileRecord(a._file.mid(1), &rec);
@@ -158,28 +136,44 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
                 }
             }
 
-            return QUrl::fromLocalFile(localFiles.constFirst());
+            return localFiles.constFirst();
         }
         return QString();
-    case AbsolutePathRole: {
-        const auto folder = FolderMan::instance()->folder(a._folder);
-        QString relPath(a._file);
+    };
+
+    const auto getDisplayPath = [&a, &ast]() {
         if (!a._file.isEmpty()) {
-            if (folder) {
-                relPath.prepend(folder->remotePath());
-            }
+            const auto folder = FolderMan::instance()->folder(a._folder);
+
+            QString relPath = folder ? folder->remotePath() + a._file : a._file;
+
             const auto localFiles = FolderMan::instance()->findFileInLocalFolders(relPath, ast->account());
-            if (!localFiles.empty()) {
-                return localFiles.constFirst();
-            } else {
-                qWarning("File not local folders while processing absolute path request.");
-                return QString();
+
+            if (localFiles.count() > 0) {
+                if (relPath.startsWith('/') || relPath.startsWith('\\')) {
+                    return relPath.remove(0, 1);
+                } else {
+                    return relPath;
+                }
             }
-        } else {
-            qWarning("Received an absolute path request for an activity without a file path.");
-            return QString();
         }
-    }
+        return QString();
+    };
+
+    const auto displayLocation = [&]() {
+        const auto displayPath = QFileInfo(getDisplayPath()).path();
+        return displayPath == "." || displayPath == "/" ? QString() : displayPath;
+    };
+
+    switch (role) {
+    case DisplayPathRole:
+        return getDisplayPath();
+    case PathRole:
+        return QUrl::fromLocalFile(QFileInfo(getFilePath()).path());
+    case AbsolutePathRole:
+        return getFilePath();
+    case DisplayLocationRole:
+        return displayLocation();
     case ActionsLinksRole: {
         QList<QVariant> customList;
         foreach (ActivityLink activityLink, a._links) {
@@ -242,7 +236,11 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
         }
     }
     case ActionTextRole:
-        return a._subject;
+        if(a._subjectDisplay.isEmpty()) {
+            return a._subject;
+        }
+
+        return a._subjectDisplay;
     case ActionTextColorRole:
         return a._id == -1 ? QLatin1String("#808080") : QLatin1String("#222");   // FIXME: This is a temporary workaround for _showMoreActivitiesAvailableEntry
     case MessageRole:
@@ -332,16 +330,53 @@ void ActivityListModel::activitiesReceived(const QJsonDocument &json, int status
 
         Activity a;
         a._type = Activity::ActivityType;
-        a._objectType = json.value("object_type").toString();
+        a._objectType = json.value(QStringLiteral("object_type")).toString();
         a._accName = ast->account()->displayName();
-        a._id = json.value("activity_id").toInt();
-        a._fileAction = json.value("type").toString();
-        a._subject = json.value("subject").toString();
-        a._message = json.value("message").toString();
-        a._file = json.value("object_name").toString();
-        a._link = QUrl(json.value("link").toString());
-        a._dateTime = QDateTime::fromString(json.value("datetime").toString(), Qt::ISODate);
-        a._icon = json.value("icon").toString();
+        a._id = json.value(QStringLiteral("activity_id")).toInt();
+        a._fileAction = json.value(QStringLiteral("type")).toString();
+        a._subject = json.value(QStringLiteral("subject")).toString();
+        a._message = json.value(QStringLiteral("message")).toString();
+        a._file = json.value(QStringLiteral("object_name")).toString();
+        a._link = QUrl(json.value(QStringLiteral("link")).toString());
+        a._dateTime = QDateTime::fromString(json.value(QStringLiteral("datetime")).toString(), Qt::ISODate);
+        a._icon = json.value(QStringLiteral("icon")).toString();
+
+        auto richSubjectData = json.value(QStringLiteral("subject_rich")).toArray();
+        Q_ASSERT(richSubjectData.size() > 1);
+
+        if(richSubjectData.size() > 1) {
+            a._subjectRich = richSubjectData[0].toString();
+            auto parameters = richSubjectData[1].toObject();
+            const QRegularExpression subjectRichParameterRe(QStringLiteral("({[a-zA-Z0-9]*})"));
+            const QRegularExpression subjectRichParameterBracesRe(QStringLiteral("[{}]"));
+
+            for (auto i = parameters.begin(); i != parameters.end(); ++i) {
+                const auto parameterJsonObject = i.value().toObject();
+                const Activity::RichSubjectParameter parameter = {
+                    parameterJsonObject.value(QStringLiteral("type")).toString(),
+                    parameterJsonObject.value(QStringLiteral("id")).toString(),
+                    parameterJsonObject.value(QStringLiteral("name")).toString(),
+                    parameterJsonObject.contains(QStringLiteral("path")) ? parameterJsonObject.value(QStringLiteral("path")).toString() : QString(),
+                    parameterJsonObject.contains(QStringLiteral("link")) ? QUrl(parameterJsonObject.value(QStringLiteral("link")).toString()) : QUrl(),
+                };
+
+                a._subjectRichParameters[i.key()] = parameter;
+            }
+
+            auto displayString = a._subjectRich;
+            auto i = subjectRichParameterRe.globalMatch(displayString);
+
+            while (i.hasNext()) {
+                const auto match = i.next();
+                auto word = match.captured(1);
+                word.remove(subjectRichParameterBracesRe);
+
+                Q_ASSERT(a._subjectRichParameters.contains(word));
+                displayString = displayString.replace(match.captured(1), a._subjectRichParameters[word].name);
+            }
+
+            a._subjectDisplay = displayString;
+        }
 
         list.append(a);
         _currentItem = list.last()._id;

+ 1 - 0
src/gui/tray/activitylistmodel.h

@@ -55,6 +55,7 @@ public:
         DisplayPathRole,
         PathRole,
         AbsolutePathRole,
+        DisplayLocationRole, // Provides the display path to a file's parent folder, relative to Nextcloud root
         LinkRole,
         PointInTimeRole,
         AccountConnectedRole,

+ 7 - 5
src/gui/tray/usermodel.cpp

@@ -506,6 +506,8 @@ void User::processCompletedSyncItem(const Folder *folder, const SyncFileItemPtr
     activity._folder = folder->alias();
     activity._fileAction = "";
 
+    const auto fileName = QFileInfo(item->_originalFile).fileName();
+
     if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) {
         activity._fileAction = "file_deleted";
     } else if (item->_instruction == CSYNC_INSTRUCTION_NEW) {
@@ -520,15 +522,15 @@ void User::processCompletedSyncItem(const Folder *folder, const SyncFileItemPtr
         qCWarning(lcActivity) << "Item " << item->_file << " retrieved successfully.";
 
         if (item->_direction != SyncFileItem::Up) {
-            activity._message = tr("Synced %1").arg(item->_originalFile);
+            activity._message = tr("Synced %1").arg(fileName);
         } else if (activity._fileAction == "file_renamed") {
-            activity._message = tr("You renamed %1").arg(item->_originalFile);
+            activity._message = tr("You renamed %1").arg(fileName);
         } else if (activity._fileAction == "file_deleted") {
-            activity._message = tr("You deleted %1").arg(item->_originalFile);
+            activity._message = tr("You deleted %1").arg(fileName);
         } else if (activity._fileAction == "file_created") {
-            activity._message = tr("You created %1").arg(item->_originalFile);
+            activity._message = tr("You created %1").arg(fileName);
         } else {
-            activity._message = tr("You changed %1").arg(item->_originalFile);
+            activity._message = tr("You changed %1").arg(fileName);
         }
 
         _activityModel->addSyncFileItemToActivityList(activity);