Skip to content

Commit

Permalink
Make ActivityLinks immutable objects
Browse files Browse the repository at this point in the history
Signed-off-by: Claudio Cambra <[email protected]>
  • Loading branch information
claucambra committed Jul 5, 2023
1 parent a680f71 commit bc92484
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 74 deletions.
12 changes: 6 additions & 6 deletions src/gui/systray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,12 @@ void Systray::createCallDialog(const Activity &callNotification, const AccountSt
QVariantList links;
for(const auto &link : callNotification._links) {
links.append(QVariantMap{
{"imageSource", link._imageSource},
{"imageSourceHovered", link._imageSourceHovered},
{"label", link._label},
{"link", link._link},
{"primary", link._primary},
{"verb", link._verb},
{"imageSource", link.imageSource()},
{"imageSourceHovered", link.imageSourceHovered()},
{"label", link.label()},
{"link", link.link()},
{"primary", link.primary()},
{"verb", link.verb()},
});
}

Expand Down
66 changes: 52 additions & 14 deletions src/gui/tray/activitydata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* for more details.
*/

#include <QtCore/qbytearray.h>
#include <QtCore>

#include "activitydata.h"
Expand Down Expand Up @@ -72,27 +73,64 @@ QString ActivityAction::label() const

ActivityActionFunction ActivityAction::action() const
{
return _action;
return _action == nullptr ? _fallbackAction : _action;
}

ActivityLink::ActivityLink(const QString &label,
const bool primary,
const QString &link,
const QByteArray &verb,
const QString &imageSource,
const QString &imageSourceHovered)
: ActivityAction(label, primary, nullptr)
, _imageSource(imageSource)
, _imageSourceHovered(imageSourceHovered)
, _link(link)
, _verb(verb)
{
_action = [this] {
return linkAction();
};
}

ActivityLink ActivityLink::createFromJsonObject(const QJsonObject &obj)
{
ActivityLink activityLink;
activityLink._label = QUrl::fromPercentEncoding(obj.value(QStringLiteral("label")).toString().toUtf8());
activityLink._link = obj.value(QStringLiteral("link")).toString();
activityLink._verb = obj.value(QStringLiteral("type")).toString().toUtf8();
activityLink._primary = obj.value(QStringLiteral("primary")).toBool();
const auto label = QUrl::fromPercentEncoding(obj.value(QStringLiteral("label")).toString().toUtf8());
const auto link = obj.value(QStringLiteral("link")).toString();
const auto verb = obj.value(QStringLiteral("type")).toString().toUtf8();
const auto primary = obj.value(QStringLiteral("primary")).toBool();

activityLink._action = [activityLink]() -> bool {
if (activityLink._verb == "WEB" || !activityLink._link.isEmpty()) {
Utility::openBrowser(QUrl(activityLink._link));
return true;
}
return ActivityLink(label, primary, link, verb);
}

return false;
};
bool ActivityLink::linkAction()
{
if (_verb == "WEB" || !_link.isEmpty()) {
Utility::openBrowser(QUrl(_link));
return true;
}

return false;
}

return activityLink;
QString ActivityLink::imageSource() const
{
return _imageSource;
}

QString ActivityLink::imageSourceHovered() const
{
return _imageSourceHovered;
}

QString ActivityLink::link() const
{
return _link;
}

QByteArray ActivityLink::verb() const
{
return _verb;
}

OCC::Activity Activity::fromActivityJson(const QJsonObject &json, const AccountPtr account)
Expand Down
31 changes: 26 additions & 5 deletions src/gui/tray/activitydata.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class ActivityAction
Q_PROPERTY(QString label READ label CONSTANT)

public:
explicit ActivityAction() = default;
explicit ActivityAction(const QString &label, const bool primary, ActivityActionFunction action = nullptr);

bool primary() const;
QString label() const;

Expand All @@ -51,7 +54,10 @@ class ActivityAction
protected:
bool _primary = false;
QString _label;
ActivityActionFunction _action = [] {
ActivityActionFunction _action = nullptr;

private:
ActivityActionFunction _fallbackAction = [] {
return false;
};
};
Expand All @@ -60,14 +66,29 @@ class ActivityLink : public ActivityAction
{
Q_GADGET

Q_PROPERTY(QString imageSource MEMBER _imageSource)
Q_PROPERTY(QString imageSourceHovered MEMBER _imageSourceHovered)
Q_PROPERTY(QString link MEMBER _link)
Q_PROPERTY(QByteArray verb MEMBER _verb)
Q_PROPERTY(QString imageSource READ imageSource CONSTANT)
Q_PROPERTY(QString imageSourceHovered READ imageSourceHovered CONSTANT)
Q_PROPERTY(QString link READ link CONSTANT)
Q_PROPERTY(QByteArray verb READ verb CONSTANT)

public:
explicit ActivityLink() = default;
explicit ActivityLink(const QString &label,
const bool primary,
const QString &link,
const QByteArray &verb = {},
const QString &imageSource = {},
const QString &imageSourceHovered = {});
static ActivityLink createFromJsonObject(const QJsonObject &obj);

QString imageSource() const;
QString imageSourceHovered() const;
QString link() const;
QByteArray verb() const;

private:
bool linkAction();

QString _imageSource;
QString _imageSourceHovered;
QString _link;
Expand Down
20 changes: 9 additions & 11 deletions src/gui/tray/activitylistmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,13 +776,13 @@ void ActivityListModel::slotTriggerAction(const int activityIndex, const int act

if (action.action()()) {
return;
} else if (action._verb == "FIX_CONFLICT_LOCALLY" && activity._type == Activity::SyncFileItemType
} else if (action.verb() == "FIX_CONFLICT_LOCALLY" && activity._type == Activity::SyncFileItemType
&& (activity._syncFileItemStatus == SyncFileItem::Conflict || activity._syncFileItemStatus == SyncFileItem::FileNameClash)) {
slotTriggerDefaultAction(activityIndex);
return;
}

emit sendNotificationRequest(activity._accName, action._link, action._verb, activityIndex);
emit sendNotificationRequest(activity._accName, action.link(), action.verb(), activityIndex);
}

void ActivityListModel::slotTriggerDismiss(const int activityIndex)
Expand Down Expand Up @@ -821,17 +821,15 @@ QVariantList ActivityListModel::convertLinksToActionButtons(const Activity &acti

QVariant ActivityListModel::convertLinkToActionButton(const OCC::ActivityLink &activityLink)
{
auto activityLinkCopy = activityLink;

const auto isReplyIconApplicable = activityLink._verb == QStringLiteral("REPLY");

const QString replyButtonPath = QStringLiteral("image://svgimage-custom-color/reply.svg");

if (isReplyIconApplicable) {
activityLinkCopy._imageSource = QString(replyButtonPath + "/");
activityLinkCopy._imageSourceHovered = QString(replyButtonPath + "/");
const auto linkVerb = activityLink.verb();
const auto isReplyIconApplicable = linkVerb == QStringLiteral("REPLY");
if (!isReplyIconApplicable) {
return QVariant::fromValue(activityLink);
}

static const QString replyButtonPath = QStringLiteral("image://svgimage-custom-color/reply.svg");
const auto imageSource = QString(replyButtonPath + "/");
const auto activityLinkCopy = ActivityLink(activityLink.label(), activityLink.primary(), activityLink.link(), linkVerb, imageSource, imageSource);
return QVariant::fromValue(activityLinkCopy);
}

Expand Down
17 changes: 7 additions & 10 deletions src/gui/tray/notificationhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,9 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
const auto objectId = json.value("object_id").toString();
const auto objectIdData = objectId.split("/");

ActivityLink al;
al._label = tr("Reply");
al._verb = "REPLY";
al._primary = true;
const auto label = tr("Reply");
const auto verb = "REPLY";
auto primary = true;

a._talkNotificationData.conversationToken = objectIdData.first();

Expand All @@ -132,7 +131,7 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j

// callback then it is the primary action
if (a._objectType == "call") {
al._primary = false;
primary = false;
}

a._talkNotificationData.userAvatar = ai->account()->url().toString() + QStringLiteral("/index.php/avatar/") + a._subjectRichParameters["user"].value<Activity::RichSubjectParameter>().id + QStringLiteral("/128");
Expand All @@ -143,14 +142,12 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
callList.append(a);
}

a._links.insert(al._primary? 0 : a._links.size(), al);
ActivityLink al(label, primary, {}, verb);
a._links.insert(al.primary() ? 0 : a._links.size(), al);
}

if (a._links.isEmpty()) {
ActivityLink dismissLink;
dismissLink._label = tr("Dismiss");
dismissLink._verb = "DELETE";
dismissLink._primary = false;
ActivityLink dismissLink(tr("Dismiss"), false, {}, "DELETE");
a._links.insert(0, dismissLink);
}

Expand Down
20 changes: 3 additions & 17 deletions src/gui/tray/usermodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,11 +619,7 @@ void User::slotAddError(const QString &folderAlias, const QString &message, Erro


if (category == ErrorCategory::InsufficientRemoteStorage) {
ActivityLink link;
link._label = tr("Retry all uploads");
link._link = folderInstance->path();
link._verb = "";
link._primary = true;
ActivityLink link(tr("Retry all uploads"), true, folderInstance->path());
activity._links.append(link);
}

Expand Down Expand Up @@ -670,12 +666,7 @@ void User::slotAddErrorToGui(const QString &folderAlias, const SyncFileItem::Sta
activity._folder = folderAlias;

if (status == SyncFileItem::Conflict || status == SyncFileItem::FileNameClash) {
ActivityLink buttonActivityLink;
buttonActivityLink._label = tr("Resolve conflict");
buttonActivityLink._link = activity._link.toString();
buttonActivityLink._verb = "FIX_CONFLICT_LOCALLY";
buttonActivityLink._primary = true;

ActivityLink buttonActivityLink(tr("Resolve conflict"), true, activity._link.toString(), "FIX_CONFLICT_LOCALLY");
activity._links = {buttonActivityLink};
}

Expand Down Expand Up @@ -811,12 +802,7 @@ void User::processCompletedSyncItem(const Folder *folder, const SyncFileItemPtr
if (item->_status == SyncFileItem::Status::FileNameInvalid) {
showDesktopNotification(item->_file, activity._subject, activity._id);
} else if (item->_status == SyncFileItem::Conflict || item->_status == SyncFileItem::FileNameClash) {
ActivityLink buttonActivityLink;
buttonActivityLink._label = tr("Resolve conflict");
buttonActivityLink._link = activity._link.toString();
buttonActivityLink._verb = "FIX_CONFLICT_LOCALLY";
buttonActivityLink._primary = true;

ActivityLink buttonActivityLink(tr("Resolve conflict"), true, activity._link.toString(), "FIX_CONFLICT_LOCALLY");
activity._links = {buttonActivityLink};
}
_activityModel->addErrorToActivityList(activity, ActivityListModel::ErrorType::SyncError);
Expand Down
24 changes: 13 additions & 11 deletions test/testactivitylistmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ public slots:
// TODO: move the logic to populate "_links" to "activitylistmodel.cpp"
auto actions = activityJsonObject.toObject().value("actions").toArray();
foreach (auto action, actions) {
activity._links.append(OCC::ActivityLink::createFomJsonObject(action.toObject()));
activity._links.append(OCC::ActivityLink::createFromJsonObject(action.toObject()));
}

finalListCopy[modelIndex.row()] = activity;
Expand Down Expand Up @@ -702,9 +702,12 @@ private slots:
QVERIFY(actionsLinksContextMenu.isEmpty() || actionsLinksContextMenu.size() < actionsLinks.size());

// context menu must not contain the primary action
QVERIFY(std::find_if(std::begin(actionsLinksContextMenu), std::end(actionsLinksContextMenu),
[](const QVariant &entry) { return entry.value<OCC::ActivityLink>()._primary; })
== std::end(actionsLinksContextMenu));
QVERIFY(std::find_if(std::begin(actionsLinksContextMenu),
std::end(actionsLinksContextMenu),
[](const QVariant &entry) {
return entry.value<OCC::ActivityLink>().primary();
})
== std::end(actionsLinksContextMenu));

const auto objectType = index.data(OCC::ActivityListModel::ObjectTypeRole).toString();

Expand All @@ -714,15 +717,15 @@ private slots:
// Login attempt notification
if (objectType == QStringLiteral("2fa_id")) {
QVERIFY(actionsLinks.size() == 2);
QVERIFY(actionsLinks[0].value<OCC::ActivityLink>()._primary);
QVERIFY(!actionsLinks[1].value<OCC::ActivityLink>()._primary);
QVERIFY(actionsLinks[0].value<OCC::ActivityLink>().primary());
QVERIFY(!actionsLinks[1].value<OCC::ActivityLink>().primary());
QVERIFY(actionsLinksContextMenu.isEmpty());
}

// Generate 2FA backup codes notification
if (objectType == QStringLiteral("create")) {
QVERIFY(actionsLinks.size() == 1);
QVERIFY(!actionsLinks[0].value<OCC::ActivityLink>()._primary);
QVERIFY(!actionsLinks[0].value<OCC::ActivityLink>().primary());
QVERIFY(actionsLinksContextMenu.isEmpty());
}

Expand All @@ -735,7 +738,7 @@ private slots:
}

// both action links and buttons must contain a "REPLY" verb element as secondary action
QVERIFY(actionsLinks[replyActionPos].value<OCC::ActivityLink>()._verb == QStringLiteral("REPLY"));
QVERIFY(actionsLinks[replyActionPos].value<OCC::ActivityLink>().verb() == QStringLiteral("REPLY"));
//QVERIFY(actionButtonsLinks[replyActionPos].value<OCC::ActivityLink>()._verb == QStringLiteral("REPLY"));

// the first action button for chat must have image set
Expand All @@ -747,7 +750,7 @@ private slots:
|| (objectType != QStringLiteral("room") && objectType != QStringLiteral("call")))) {

// button's label for "chat" must be renamed to "Reply"
QVERIFY(actionButtonsLinks[0].value<OCC::ActivityLink>()._label == QObject::tr("Reply"));
QVERIFY(actionButtonsLinks[0].value<OCC::ActivityLink>().label() == QObject::tr("Reply"));

if (static_cast<quint32>(actionsLinks.size()) > OCC::ActivityListModel::maxActionButtons()) {
// in case total actions is longer than ActivityListModel::maxActionButtons, only one button must be present in a list of action buttons
Expand All @@ -757,8 +760,7 @@ private slots:
QVERIFY(actionButtonsLinks.size() + actionsLinksContextMenu.size() == actionsLinks.size());
}
} else if ((objectType == QStringLiteral("call"))) {
QVERIFY(
actionButtonsLinks[0].value<OCC::ActivityLink>()._label == QStringLiteral("Call back"));
QVERIFY(actionButtonsLinks[0].value<OCC::ActivityLink>().label() == QStringLiteral("Call back"));
}
}
}
Expand Down

0 comments on commit bc92484

Please sign in to comment.