diff --git a/spectral.pro b/spectral.pro index e7f8dc0..0536a8f 100644 --- a/spectral.pro +++ b/spectral.pro @@ -92,7 +92,8 @@ HEADERS += \ src/imageitem.h \ src/accountlistmodel.h \ src/spectraluser.h \ - src/notifications/manager.h + src/notifications/manager.h \ + src/utils.h SOURCES += src/main.cpp \ src/controller.cpp \ @@ -104,7 +105,8 @@ SOURCES += src/main.cpp \ src/userlistmodel.cpp \ src/imageitem.cpp \ src/accountlistmodel.cpp \ - src/spectraluser.cpp + src/spectraluser.cpp \ + src/utils.cpp unix:!mac { SOURCES += src/notifications/managerlinux.cpp diff --git a/src/messageeventmodel.cpp b/src/messageeventmodel.cpp index 0340224..cad6bfc 100644 --- a/src/messageeventmodel.cpp +++ b/src/messageeventmodel.cpp @@ -13,6 +13,8 @@ #include #include // for qmlRegisterType() +#include "utils.h" + static QString parseAvatarUrl(QUrl url) { return url.host() + "/" + url.path(); } @@ -232,120 +234,13 @@ QVariant MessageEventModel::data(const QModelIndex& idx, int role) const { std::min(row, timelineBaseIndex()); const auto& evt = isPending ? **pendingIt : **timelineIt; - using namespace QMatrixClient; if (role == Qt::DisplayRole) { - if (evt.isRedacted()) { - auto reason = evt.redactedBecause()->reason(); - if (reason.isEmpty()) return tr("Redacted"); - - return tr("Redacted: %1").arg(evt.redactedBecause()->reason()); - } - - return visit( - evt, - [this](const RoomMessageEvent& e) { - using namespace MessageEventContent; - - if (e.hasTextContent() && e.mimeType().name() != "text/plain") { - static const QRegExp userPillRegExp( - "(.*)"); - QString formattedStr( - static_cast(e.content())->body); - formattedStr.replace(userPillRegExp, - "\\1"); - return formattedStr; - } - if (e.hasFileContent()) { - auto fileCaption = e.content()->fileInfo()->originalName; - if (fileCaption.isEmpty()) - fileCaption = m_currentRoom->prettyPrint(e.plainBody()); - if (fileCaption.isEmpty()) return tr("a file"); - } - return m_currentRoom->prettyPrint(e.plainBody()); - }, - [this](const RoomMemberEvent& e) { - // FIXME: Rewind to the name that was at the time of this event - QString subjectName = m_currentRoom->roomMembername(e.userId()); - // The below code assumes senderName output in AuthorRole - switch (e.membership()) { - case MembershipType::Invite: - if (e.repeatsState()) - return tr("reinvited %1 to the room").arg(subjectName); - FALLTHROUGH; - case MembershipType::Join: { - if (e.repeatsState()) return tr("joined the room (repeated)"); - if (!e.prevContent() || - e.membership() != e.prevContent()->membership) { - return e.membership() == MembershipType::Invite - ? tr("invited %1 to the room").arg(subjectName) - : tr("joined the room"); - } - QString text{}; - if (e.isRename()) { - if (e.displayName().isEmpty()) - text = tr("cleared their display name"); - else - text = tr("changed their display name to %1") - .arg(e.displayName()); - } - if (e.isAvatarUpdate()) { - if (!text.isEmpty()) text += " and "; - if (e.avatarUrl().isEmpty()) - text += tr("cleared the avatar"); - else - text += tr("updated the avatar"); - } - return text; - } - case MembershipType::Leave: - if (e.prevContent() && - e.prevContent()->membership == MembershipType::Ban) { - return (e.senderId() != e.userId()) - ? tr("unbanned %1").arg(subjectName) - : tr("self-unbanned"); - } - return (e.senderId() != e.userId()) - ? tr("has kicked %1 from the room").arg(subjectName) - : tr("left the room"); - case MembershipType::Ban: - return (e.senderId() != e.userId()) - ? tr("banned %1 from the room").arg(subjectName) - : tr("self-banned from the room"); - case MembershipType::Knock: - return tr("knocked"); - default:; - } - return tr("made something unknown"); - }, - [](const RoomAliasesEvent& e) { - return tr("set aliases to: %1").arg(e.aliases().join(", ")); - }, - [](const RoomCanonicalAliasEvent& e) { - return (e.alias().isEmpty()) - ? tr("cleared the room main alias") - : tr("set the room main alias to: %1").arg(e.alias()); - }, - [](const RoomNameEvent& e) { - return (e.name().isEmpty()) - ? tr("cleared the room name") - : tr("set the room name to: %1").arg(e.name()); - }, - [](const RoomTopicEvent& e) { - return (e.topic().isEmpty()) - ? tr("cleared the topic") - : tr("set the topic to: %1").arg(e.topic()); - }, - [](const RoomAvatarEvent&) { return tr("changed the room avatar"); }, - [](const EncryptionEvent&) { - return tr("activated End-to-End Encryption"); - }, - tr("Unknown Event")); + return utils::eventToString(evt, m_currentRoom, Qt::RichText); } if (role == MessageRole) { static const QRegExp rmReplyRegExp("^> <@.*:.*> .*\n\n(.*)"); - return evt.contentJson().value("body").toString().replace(rmReplyRegExp, - "\\1"); + return utils::eventToString(evt, m_currentRoom).replace(rmReplyRegExp, "\\1"); } if (role == Qt::ToolTipRole) { diff --git a/src/roomlistmodel.cpp b/src/roomlistmodel.cpp index a6f39cd..2304795 100644 --- a/src/roomlistmodel.cpp +++ b/src/roomlistmodel.cpp @@ -1,6 +1,7 @@ #include "roomlistmodel.h" #include "user.h" +#include "utils.h" #include "events/roomevent.h" @@ -79,13 +80,12 @@ void RoomListModel::connectRoomSignals(SpectralRoom* room) { room, &Room::aboutToAddNewMessages, this, [=](QMatrixClient::RoomEventsRange eventsRange) { RoomEvent* event = (eventsRange.end() - 1)->get(); - if (event->isStateEvent()) return; User* sender = room->user(event->senderId()); if (sender == room->localUser()) return; QUrl _url = room->avatarUrl(); emit newMessage( room->id(), event->id(), room->displayName(), sender->displayname(), - event->contentJson().value("body").toString(), room->avatar(128), + utils::eventToString(*event), room->avatar(128), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/avatar/" + _url.authority() + '_' + _url.fileName() + ".png")); }); diff --git a/src/spectralroom.cpp b/src/spectralroom.cpp index 89233d3..e151577 100644 --- a/src/spectralroom.cpp +++ b/src/spectralroom.cpp @@ -12,6 +12,8 @@ #include #include +#include "utils.h" + SpectralRoom::SpectralRoom(Connection* connection, QString roomId, JoinState joinState) : Room(connection, std::move(roomId), joinState) { @@ -105,9 +107,8 @@ void SpectralRoom::sendTypingNotification(bool isTyping) { QString SpectralRoom::lastEvent() { if (timelineSize() == 0) return ""; const RoomEvent* lastEvent = messageEvents().rbegin()->get(); - if (lastEvent->contentJson().value("body").toString() == "") return ""; return user(lastEvent->senderId())->displayname() + ": " + - lastEvent->contentJson().value("body").toString(); + utils::removeReply(utils::eventToString(*lastEvent, this)); } bool SpectralRoom::isEventHighlighted(const RoomEvent* e) const { diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..b14e9be --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,6 @@ +#include "utils.h" + +QString utils::removeReply(const QString& text) { + QString result(text); + return result.remove(utils::removeReplyRegex); +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..2304610 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,140 @@ +#ifndef Utils_H +#define Utils_H + +#include "room.h" + +#include +#include +#include + +#include +#include +#include +#include + +namespace utils { +const QRegExp removeReplyRegex{"> <.*>.*\\n\\n"}; + +QString removeReply(const QString& text); + +template +QString eventToString(const BaseEventT& evt, + QMatrixClient::Room* room = nullptr, + Qt::TextFormat format = Qt::PlainText) { + bool prettyPrint = (format == Qt::RichText); + + using namespace QMatrixClient; + return visit( + evt, + [room, prettyPrint](const RoomMessageEvent& e) { + using namespace MessageEventContent; + + if (prettyPrint && e.hasTextContent() && + e.mimeType().name() != "text/plain") { + static const QRegExp userPillRegExp( + "(.*)"); + QString formattedStr( + static_cast(e.content())->body); + formattedStr.replace(userPillRegExp, + "\\1"); + return formattedStr; + } + if (e.hasFileContent()) { + auto fileCaption = e.content()->fileInfo()->originalName; + if (fileCaption.isEmpty()) + fileCaption = prettyPrint && room ? room->prettyPrint(e.plainBody()) + : e.plainBody(); + if (fileCaption.isEmpty()) return QObject::tr("a file"); + } + return prettyPrint && room ? room->prettyPrint(e.plainBody()) + : e.plainBody(); + }, + [room](const RoomMemberEvent& e) { + // FIXME: Rewind to the name that was at the time of this event + QString subjectName = + room ? room->roomMembername(e.userId()) : e.userId(); + // The below code assumes senderName output in AuthorRole + switch (e.membership()) { + case MembershipType::Invite: + if (e.repeatsState()) + return QObject::tr("reinvited %1 to the room").arg(subjectName); + FALLTHROUGH; + case MembershipType::Join: { + if (e.repeatsState()) + return QObject::tr("joined the room (repeated)"); + if (!e.prevContent() || + e.membership() != e.prevContent()->membership) { + return e.membership() == MembershipType::Invite + ? QObject::tr("invited %1 to the room") + .arg(subjectName) + : QObject::tr("joined the room"); + } + QString text{}; + if (e.isRename()) { + if (e.displayName().isEmpty()) + text = QObject::tr("cleared their display name"); + else + text = QObject::tr("changed their display name to %1") + .arg(e.displayName()); + } + if (e.isAvatarUpdate()) { + if (!text.isEmpty()) text += " and "; + if (e.avatarUrl().isEmpty()) + text += QObject::tr("cleared the avatar"); + else + text += QObject::tr("updated the avatar"); + } + return text; + } + case MembershipType::Leave: + if (e.prevContent() && + e.prevContent()->membership == MembershipType::Ban) { + return (e.senderId() != e.userId()) + ? QObject::tr("unbanned %1").arg(subjectName) + : QObject::tr("self-unbanned"); + } + return (e.senderId() != e.userId()) + ? QObject::tr("has kicked %1 from the room") + .arg(subjectName) + : QObject::tr("left the room"); + case MembershipType::Ban: + return (e.senderId() != e.userId()) + ? QObject::tr("banned %1 from the room ") + .arg(subjectName) + : QObject::tr(" self-banned from the room "); + case MembershipType::Knock: + return QObject::tr("knocked"); + default:; + } + return QObject::tr("made something unknown"); + }, + [](const RoomAliasesEvent& e) { + return QObject::tr("set aliases to: %1").arg(e.aliases().join(",")); + }, + [](const RoomCanonicalAliasEvent& e) { + return (e.alias().isEmpty()) + ? QObject::tr("cleared the room main alias") + : QObject::tr("set the room main alias to: %1") + .arg(e.alias()); + }, + [](const RoomNameEvent& e) { + return (e.name().isEmpty()) + ? QObject::tr("cleared the room name") + : QObject::tr("set the room name to: %1").arg(e.name()); + }, + [](const RoomTopicEvent& e) { + return (e.topic().isEmpty()) + ? QObject::tr("cleared the topic") + : QObject::tr("set the topic to: %1").arg(e.topic()); + }, + [](const RoomAvatarEvent&) { + return QObject::tr("changed the room avatar"); + }, + [](const EncryptionEvent&) { + return QObject::tr("activated End-to-End Encryption"); + }, + QObject::tr("Unknown Event")); +}; +} // namespace utils + +#endif