diff --git a/CMakeLists.txt b/CMakeLists.txt index 181bcdace..fe69681ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) "MinSizeRel" "RelWithDebInfo") endif() -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) # Setup command line parameters for the compiler and linker foreach (FLAG "" all pedantic extra no-unused-parameter) diff --git a/imports/Spectral/Dialog/RoomSettingsDialog.qml b/imports/Spectral/Dialog/RoomSettingsDialog.qml index a504671e5..369950b23 100644 --- a/imports/Spectral/Dialog/RoomSettingsDialog.qml +++ b/imports/Spectral/Dialog/RoomSettingsDialog.qml @@ -276,42 +276,6 @@ Dialog { } } } - - MenuSeparator { - Layout.fillWidth: true - } - - RowLayout { - Layout.fillWidth: true - - Button { - Layout.fillWidth: true - - flat: true - text: "Set background image" - - onClicked: { - var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay) - - fileDialog.chosen.connect(function(path) { - if (!path) return - - room.setBackgroundFromLocalFile(path) - }) - - fileDialog.open() - } - } - - Button { - Layout.fillWidth: true - - flat: true - text: "Clear background image" - - onClicked: room.clearBackground() - } - } } Component { diff --git a/imports/Spectral/Panel/RoomPanel.qml b/imports/Spectral/Panel/RoomPanel.qml index 6b624dfa2..745c8ce41 100644 --- a/imports/Spectral/Panel/RoomPanel.qml +++ b/imports/Spectral/Panel/RoomPanel.qml @@ -172,25 +172,10 @@ Item { } } - Image { - readonly property int sourceDim: (Math.ceil(Math.max(width, height) / 360) + 1) * 360 - - anchors.fill: parent - - visible: currentRoom && currentRoom.backgroundMediaId - - sourceSize.width: sourceDim - sourceSize.height: sourceDim - - fillMode: Image.PreserveAspectCrop - - source: currentRoom && currentRoom.backgroundMediaId ? "image://mxc/" + currentRoom.backgroundMediaId : "" - } - Rectangle { anchors.fill: parent - visible: currentRoom && !currentRoom.backgroundMediaId + visible: currentRoom color: MSettings.darkTheme ? "#242424" : "#EBEFF2" } @@ -258,12 +243,12 @@ Item { if (currentRoom) { movingTimer.restart() -// var lastScrollPosition = sortedMessageEventModel.mapFromSource(currentRoom.savedTopVisibleIndex()) -// if (lastScrollPosition === 0) { -// messageListView.positionViewAtBeginning() -// } else { -// messageListView.currentIndex = lastScrollPosition -// } + // var lastScrollPosition = sortedMessageEventModel.mapFromSource(currentRoom.savedTopVisibleIndex()) + // if (lastScrollPosition === 0) { + // messageListView.positionViewAtBeginning() + // } else { + // messageListView.currentIndex = lastScrollPosition + // } if (messageListView.contentY < messageListView.originY + 10 || currentRoom.timelineSize < 20) currentRoom.getPreviousContent(50) @@ -544,7 +529,7 @@ Item { anchors.left: parent.left anchors.bottom: parent.bottom - visible: currentRoom && currentRoom.hasUsersTyping + visible: currentRoom && currentRoom.usersTyping.length > 0 padding: 4 contentItem: RowLayout { @@ -554,7 +539,7 @@ Item { spacing: -8 Repeater { - model: currentRoom && currentRoom.hasUsersTyping ? currentRoom.usersTyping : null + model: currentRoom && currentRoom.usersTyping.length > 0 ? currentRoom.usersTyping : null delegate: Rectangle { Layout.preferredWidth: 28 diff --git a/src/accountlistmodel.cpp b/src/accountlistmodel.cpp index 198ac4fb8..58b9058d9 100644 --- a/src/accountlistmodel.cpp +++ b/src/accountlistmodel.cpp @@ -6,53 +6,59 @@ AccountListModel::AccountListModel(QObject* parent) : QAbstractListModel(parent) {} void AccountListModel::setController(Controller* value) { - if (m_controller != value) { - beginResetModel(); - m_connections.clear(); - - m_controller = value; - - for (auto c : m_controller->connections()) m_connections.append(c); - - connect(m_controller, &Controller::connectionAdded, this, - [=](Connection* conn) { - if (!conn) { - } - beginInsertRows(QModelIndex(), m_connections.count(), - m_connections.count()); - m_connections.append(conn); - endInsertRows(); - }); - connect(m_controller, &Controller::connectionDropped, this, - [=](Connection* conn) { - qDebug() << "Dropping connection" << conn->userId(); - if (!conn) { - qDebug() << "Trying to remove null connection"; - return; - } - conn->disconnect(this); - const auto it = - std::find(m_connections.begin(), m_connections.end(), conn); - if (it == m_connections.end()) - return; // Already deleted, nothing to do - const int row = it - m_connections.begin(); - beginRemoveRows(QModelIndex(), row, row); - m_connections.erase(it); - endRemoveRows(); - }); - emit controllerChanged(); + if (m_controller == value) { + return; } + + beginResetModel(); + + m_connections.clear(); + m_controller = value; + m_connections += m_controller->connections(); + + connect(m_controller, &Controller::connectionAdded, this, + [=](Connection* conn) { + if (!conn) { + return; + } + beginInsertRows(QModelIndex(), m_connections.count(), + m_connections.count()); + m_connections.append(conn); + endInsertRows(); + }); + connect(m_controller, &Controller::connectionDropped, this, + [=](Connection* conn) { + qDebug() << "Dropping connection" << conn->userId(); + if (!conn) { + qDebug() << "Trying to remove null connection"; + return; + } + conn->disconnect(this); + const auto it = + std::find(m_connections.begin(), m_connections.end(), conn); + if (it == m_connections.end()) + return; // Already deleted, nothing to do + const int row = it - m_connections.begin(); + beginRemoveRows(QModelIndex(), row, row); + m_connections.erase(it); + endRemoveRows(); + }); + emit controllerChanged(); } QVariant AccountListModel::data(const QModelIndex& index, int role) const { - if (!index.isValid()) return QVariant(); + if (!index.isValid()) { + return {}; + } if (index.row() >= m_connections.count()) { qDebug() << "AccountListModel, something's wrong: index.row() >= " "m_users.count()"; - return QVariant(); + return {}; } + auto m_connection = m_connections.at(index.row()); + if (role == UserRole) { return QVariant::fromValue(m_connection->user()); } @@ -60,18 +66,22 @@ QVariant AccountListModel::data(const QModelIndex& index, int role) const { return QVariant::fromValue(m_connection); } - return QVariant(); + return {}; } int AccountListModel::rowCount(const QModelIndex& parent) const { - if (parent.isValid()) return 0; + if (parent.isValid()) { + return 0; + } return m_connections.count(); } QHash AccountListModel::roleNames() const { QHash roles; + roles[UserRole] = "user"; roles[ConnectionRole] = "connection"; + return roles; } diff --git a/src/accountlistmodel.h b/src/accountlistmodel.h index be28746ec..5bbb976cc 100644 --- a/src/accountlistmodel.h +++ b/src/accountlistmodel.h @@ -20,7 +20,7 @@ class AccountListModel : public QAbstractListModel { QHash roleNames() const override; - Controller* controller() { return m_controller; } + Controller* controller() const { return m_controller; } void setController(Controller* value); private: diff --git a/src/controller.cpp b/src/controller.cpp index 2ef5ecfb1..77b52991c 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -44,11 +44,11 @@ Controller::Controller(QObject* parent) : QObject(parent) { connect(&m_ncm, &QNetworkConfigurationManager::onlineStateChanged, this, &Controller::isOnlineChanged); - QTimer::singleShot(0, this, SLOT(invokeLogin())); + QTimer::singleShot(0, this, [=] { invokeLogin(); }); } Controller::~Controller() { - for (Connection* c : m_connections) { + for (auto c : m_connections) { c->stopSync(); c->saveState(); } @@ -66,74 +66,79 @@ void Controller::loginWithCredentials(QString serverAddr, QString user, QString pass, QString deviceName) { - if (!user.isEmpty() && !pass.isEmpty()) { - if (deviceName.isEmpty()) { - deviceName = "Spectral " + QSysInfo::machineHostName() + " " + - QSysInfo::productType() + " " + QSysInfo::productVersion() + - " " + QSysInfo::currentCpuArchitecture(); - } - - QUrl serverUrl(serverAddr); - - Connection* conn = new Connection(this); - if (serverUrl.isValid()) { - conn->setHomeserver(serverUrl); - } - conn->connectToServer(user, pass, deviceName, ""); - connect(conn, &Connection::connected, [=] { - AccountSettings account(conn->userId()); - account.setKeepLoggedIn(true); - account.clearAccessToken(); // Drop the legacy - just in case - account.setHomeserver(conn->homeserver()); - account.setDeviceId(conn->deviceId()); - account.setDeviceName(deviceName); - if (!saveAccessTokenToKeyChain(account, conn->accessToken())) - qWarning() << "Couldn't save access token"; - account.sync(); - addConnection(conn); - setConnection(conn); - }); - connect(conn, &Connection::networkError, - [=](QString error, QString, int, int) { - emit errorOccured("Network Error", error); - }); - connect(conn, &Connection::loginError, [=](QString error, QString) { - emit errorOccured("Login Failed", error); - }); + if (user.isEmpty() || pass.isEmpty()) { + return; } + + if (deviceName.isEmpty()) { + deviceName = "Spectral " + QSysInfo::machineHostName() + " " + + QSysInfo::productType() + " " + QSysInfo::productVersion() + + " " + QSysInfo::currentCpuArchitecture(); + } + + QUrl serverUrl(serverAddr); + + auto conn = new Connection(this); + if (serverUrl.isValid()) { + conn->setHomeserver(serverUrl); + } + conn->connectToServer(user, pass, deviceName, ""); + + connect(conn, &Connection::connected, [=] { + AccountSettings account(conn->userId()); + account.setKeepLoggedIn(true); + account.clearAccessToken(); // Drop the legacy - just in case + account.setHomeserver(conn->homeserver()); + account.setDeviceId(conn->deviceId()); + account.setDeviceName(deviceName); + if (!saveAccessTokenToKeyChain(account, conn->accessToken())) + qWarning() << "Couldn't save access token"; + account.sync(); + addConnection(conn); + setConnection(conn); + }); + connect(conn, &Connection::networkError, + [=](QString error, QString, int, int) { + emit errorOccured("Network Error", error); + }); + connect(conn, &Connection::loginError, [=](QString error, QString) { + emit errorOccured("Login Failed", error); + }); } void Controller::loginWithAccessToken(QString serverAddr, QString user, QString token, QString deviceName) { - if (!user.isEmpty() && !token.isEmpty()) { - QUrl serverUrl(serverAddr); - - Connection* conn = new Connection(this); - if (serverUrl.isValid()) { - conn->setHomeserver(serverUrl); - } - - connect(conn, &Connection::connected, [=] { - AccountSettings account(conn->userId()); - account.setKeepLoggedIn(true); - account.clearAccessToken(); // Drop the legacy - just in case - account.setHomeserver(conn->homeserver()); - account.setDeviceId(conn->deviceId()); - account.setDeviceName(deviceName); - if (!saveAccessTokenToKeyChain(account, conn->accessToken())) - qWarning() << "Couldn't save access token"; - account.sync(); - addConnection(conn); - setConnection(conn); - }); - connect(conn, &Connection::networkError, - [=](QString error, QString, int, int) { - emit errorOccured("Network Error", error); - }); - conn->connectWithToken(user, token, deviceName); + if (user.isEmpty() || token.isEmpty()) { + return; } + + QUrl serverUrl(serverAddr); + + auto conn = new Connection(this); + if (serverUrl.isValid()) { + conn->setHomeserver(serverUrl); + } + + connect(conn, &Connection::connected, [=] { + AccountSettings account(conn->userId()); + account.setKeepLoggedIn(true); + account.clearAccessToken(); // Drop the legacy - just in case + account.setHomeserver(conn->homeserver()); + account.setDeviceId(conn->deviceId()); + account.setDeviceName(deviceName); + if (!saveAccessTokenToKeyChain(account, conn->accessToken())) + qWarning() << "Couldn't save access token"; + account.sync(); + addConnection(conn); + setConnection(conn); + }); + connect(conn, &Connection::networkError, + [=](QString error, QString, int, int) { + emit errorOccured("Network Error", error); + }); + conn->connectWithToken(user, token, deviceName); } void Controller::logout(Connection* conn) { @@ -149,7 +154,8 @@ void Controller::logout(Connection* conn) { job.setAutoDelete(true); job.setKey(conn->userId()); QEventLoop loop; - QKeychain::DeletePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit); + QKeychain::DeletePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, + &QEventLoop::quit); job.start(); loop.exec(); @@ -169,21 +175,24 @@ void Controller::logout(Connection* conn) { void Controller::addConnection(Connection* c) { Q_ASSERT_X(c, __FUNCTION__, "Attempt to add a null connection"); - m_connections.push_back(c); + m_connections += c; c->setLazyLoading(true); connect(c, &Connection::syncDone, this, [=] { setBusy(false); + emit syncDone(); + c->sync(30000); c->saveState(); }); connect(c, &Connection::loggedOut, this, [=] { dropConnection(c); }); connect(&m_ncm, &QNetworkConfigurationManager::onlineStateChanged, [=](bool status) { - if (!status) + if (!status) { return; + } c->stopSync(); c->sync(30000); @@ -231,8 +240,11 @@ void Controller::invokeLogin() { c->connectWithToken(account.userId(), accessToken, account.deviceId()); } } - if (!m_connections.isEmpty()) + + if (!m_connections.isEmpty()) { setConnection(m_connections[0]); + } + emit initiated(); } @@ -333,7 +345,7 @@ bool Controller::saveAccessTokenToKeyChain(const AccountSettings& account, } void Controller::joinRoom(Connection* c, const QString& alias) { - JoinRoomJob* joinRoomJob = c->joinRoom(alias); + auto joinRoomJob = c->joinRoom(alias); joinRoomJob->connect(joinRoomJob, &JoinRoomJob::failure, [=] { emit errorOccured("Join Room Failed", joinRoomJob->errorString()); }); @@ -342,7 +354,7 @@ void Controller::joinRoom(Connection* c, const QString& alias) { void Controller::createRoom(Connection* c, const QString& name, const QString& topic) { - CreateRoomJob* createRoomJob = + auto createRoomJob = c->createRoom(Connection::PublishRoom, "", name, topic, QStringList()); createRoomJob->connect(createRoomJob, &CreateRoomJob::failure, [=] { emit errorOccured("Create Room Failed", createRoomJob->errorString()); @@ -350,7 +362,7 @@ void Controller::createRoom(Connection* c, } void Controller::createDirectChat(Connection* c, const QString& userID) { - CreateRoomJob* createRoomJob = c->createDirectChat(userID); + auto createRoomJob = c->createDirectChat(userID); createRoomJob->connect(createRoomJob, &CreateRoomJob::failure, [=] { emit errorOccured("Create Direct Chat Failed", createRoomJob->errorString()); @@ -358,24 +370,16 @@ void Controller::createDirectChat(Connection* c, const QString& userID) { } void Controller::playAudio(QUrl localFile) { - QMediaPlayer* player = new QMediaPlayer; + auto player = new QMediaPlayer; player->setMedia(localFile); player->play(); connect(player, &QMediaPlayer::stateChanged, [=] { player->deleteLater(); }); } -int Controller::dpi() { - return SettingsGroup("Interface").value("dpi", 100).toInt(); -} - -void Controller::setDpi(int dpi) { - SettingsGroup("Interface").setValue("dpi", dpi); -} - void Controller::changeAvatar(Connection* conn, QUrl localFile) { auto job = conn->uploadFile(localFile.toLocalFile()); if (isJobRunning(job)) { - connect(job, &BaseJob::success, this, [this, conn, job] { + connect(job, &BaseJob::success, this, [conn, job] { conn->callApi(conn->userId(), job->contentUri()); }); } diff --git a/src/controller.h b/src/controller.h index 5c9e1836f..904b38de6 100644 --- a/src/controller.h +++ b/src/controller.h @@ -34,10 +34,7 @@ class Controller : public QObject { Q_INVOKABLE void loginWithCredentials(QString, QString, QString, QString); Q_INVOKABLE void loginWithAccessToken(QString, QString, QString, QString); - QVector connections() { return m_connections; } - - Q_INVOKABLE int dpi(); - Q_INVOKABLE void setDpi(int dpi); + QVector connections() const { return m_connections; } // All the non-Q_INVOKABLE functions. void addConnection(Connection* c); @@ -46,19 +43,20 @@ class Controller : public QObject { // All the Q_PROPERTYs. int accountCount() { return m_connections.count(); } - bool quitOnLastWindowClosed() { + bool quitOnLastWindowClosed() const { return QApplication::quitOnLastWindowClosed(); } void setQuitOnLastWindowClosed(bool value) { if (quitOnLastWindowClosed() != value) { QApplication::setQuitOnLastWindowClosed(value); + emit quitOnLastWindowClosedChanged(); } } - bool isOnline() { return m_ncm.isOnline(); } + bool isOnline() const { return m_ncm.isOnline(); } - bool busy() { return m_busy; } + bool busy() const { return m_busy; } void setBusy(bool busy) { if (m_busy == busy) { return; @@ -67,9 +65,10 @@ class Controller : public QObject { emit busyChanged(); } - Connection* connection() { + Connection* connection() const { if (m_connection.isNull()) return nullptr; + return m_connection; } diff --git a/src/emojimodel.cpp b/src/emojimodel.cpp index 9f0ee4252..9e0f8c4f1 100644 --- a/src/emojimodel.cpp +++ b/src/emojimodel.cpp @@ -28,49 +28,49 @@ QVariantList EmojiModel::filterModel(const QString& filter) { QVariantList result; for (QVariant e : people) { - Emoji emoji = qvariant_cast(e); + auto emoji = qvariant_cast(e); if (emoji.shortname.startsWith(filter)) { result.append(e); } } for (QVariant e : nature) { - Emoji emoji = qvariant_cast(e); + auto emoji = qvariant_cast(e); if (emoji.shortname.startsWith(filter)) { result.append(e); } } for (QVariant e : food) { - Emoji emoji = qvariant_cast(e); + auto emoji = qvariant_cast(e); if (emoji.shortname.startsWith(filter)) { result.append(e); } } for (QVariant e : activity) { - Emoji emoji = qvariant_cast(e); + auto emoji = qvariant_cast(e); if (emoji.shortname.startsWith(filter)) { result.append(e); } } for (QVariant e : travel) { - Emoji emoji = qvariant_cast(e); + auto emoji = qvariant_cast(e); if (emoji.shortname.startsWith(filter)) { result.append(e); } } for (QVariant e : objects) { - Emoji emoji = qvariant_cast(e); + auto emoji = qvariant_cast(e); if (emoji.shortname.startsWith(filter)) { result.append(e); } } for (QVariant e : symbols) { - Emoji emoji = qvariant_cast(e); + auto emoji = qvariant_cast(e); if (emoji.shortname.startsWith(filter)) { result.append(e); } } for (QVariant e : flags) { - Emoji emoji = qvariant_cast(e); + auto emoji = qvariant_cast(e); if (emoji.shortname.startsWith(filter)) { result.append(e); } diff --git a/src/imageclipboard.cpp b/src/imageclipboard.cpp index d174f19b0..d8d10ae14 100644 --- a/src/imageclipboard.cpp +++ b/src/imageclipboard.cpp @@ -12,11 +12,11 @@ ImageClipboard::ImageClipboard(QObject* parent) &ImageClipboard::imageChanged); } -bool ImageClipboard::hasImage() { +bool ImageClipboard::hasImage() const { return !image().isNull(); } -QImage ImageClipboard::image() { +QImage ImageClipboard::image() const { return m_clipboard->image(); } @@ -31,8 +31,9 @@ bool ImageClipboard::saveImage(const QUrl& localPath) { QString path = QFileInfo(localPath.toLocalFile()).absolutePath(); QDir dir; - if (!dir.exists(path)) + if (!dir.exists(path)) { dir.mkpath(path); + } i.save(localPath.toLocalFile()); diff --git a/src/imageclipboard.h b/src/imageclipboard.h index a8cc181d1..ce3c588af 100644 --- a/src/imageclipboard.h +++ b/src/imageclipboard.h @@ -13,8 +13,8 @@ class ImageClipboard : public QObject { public: explicit ImageClipboard(QObject* parent = nullptr); - bool hasImage(); - QImage image(); + bool hasImage() const; + QImage image() const; Q_INVOKABLE bool saveImage(const QUrl& localPath); diff --git a/src/messageeventmodel.cpp b/src/messageeventmodel.cpp index 1583dc588..d3de9af34 100644 --- a/src/messageeventmodel.cpp +++ b/src/messageeventmodel.cpp @@ -307,6 +307,8 @@ QVariant MessageEventModel::data(const QModelIndex& idx, int role) const { return "audio"; case MessageEventType::Video: return "video"; + default: + break; } if (e->hasFileContent()) return "file"; @@ -341,8 +343,8 @@ QVariant MessageEventModel::data(const QModelIndex& idx, int role) const { if (evt.isRedacted()) { auto reason = evt.redactedBecause()->reason(); return (reason.isEmpty()) - ? tr("Redacted") - : tr("Redacted: %1").arg(evt.redactedBecause()->reason()); + ? tr("[REDACTED]") + : tr("[REDACTED: %1]").arg(evt.redactedBecause()->reason()); } if (auto e = eventCast(&evt)) { @@ -540,7 +542,7 @@ QVariant MessageEventModel::data(const QModelIndex& idx, int role) const { return {}; } -int MessageEventModel::eventIDToIndex(const QString& eventID) { +int MessageEventModel::eventIDToIndex(const QString& eventID) const { const auto it = m_currentRoom->findInTimeline(eventID); if (it == m_currentRoom->timelineEdge()) { qWarning() << "Trying to find inexistent event:" << eventID; diff --git a/src/messageeventmodel.h b/src/messageeventmodel.h index 38dd301b0..398aff2c4 100644 --- a/src/messageeventmodel.h +++ b/src/messageeventmodel.h @@ -48,9 +48,9 @@ class MessageEventModel : public QAbstractListModel { }; explicit MessageEventModel(QObject* parent = nullptr); - ~MessageEventModel(); + ~MessageEventModel() override; - SpectralRoom* room() { return m_currentRoom; } + SpectralRoom* room() const { return m_currentRoom; } void setRoom(SpectralRoom* room); int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -58,7 +58,7 @@ class MessageEventModel : public QAbstractListModel { int role = Qt::DisplayRole) const override; QHash roleNames() const override; - Q_INVOKABLE int eventIDToIndex(const QString& eventID); + Q_INVOKABLE int eventIDToIndex(const QString& eventID) const; private slots: int refreshEvent(const QString& eventId); diff --git a/src/notifications/managermac.mm b/src/notifications/managermac.mm index 782be1f29..bcd8c776d 100644 --- a/src/notifications/managermac.mm +++ b/src/notifications/managermac.mm @@ -4,45 +4,34 @@ #include @interface NSUserNotification (CFIPrivate) -- (void)set_identityImage:(NSImage *)image; +- (void)set_identityImage:(NSImage*)image; @end -NotificationsManager::NotificationsManager(QObject *parent): QObject(parent) -{ +NotificationsManager::NotificationsManager(QObject* parent) : QObject(parent) {} +void NotificationsManager::postNotification(const QString& roomId, + const QString& eventId, + const QString& roomName, + const QString& senderName, + const QString& text, + const QImage& icon) { + Q_UNUSED(roomId); + Q_UNUSED(eventId); + Q_UNUSED(icon); + + NSUserNotification* notif = [[NSUserNotification alloc] init]; + + notif.title = roomName.toNSString(); + notif.subtitle = QString("%1 sent a message").arg(senderName).toNSString(); + notif.informativeText = text.toNSString(); + notif.soundName = NSUserNotificationDefaultSoundName; + + [[NSUserNotificationCenter defaultUserNotificationCenter] + deliverNotification:notif]; + [notif autorelease]; } -void -NotificationsManager::postNotification( - const QString &roomId, - const QString &eventId, - const QString &roomName, - const QString &senderName, - const QString &text, - const QImage &icon) -{ - Q_UNUSED(roomId); - Q_UNUSED(eventId); - Q_UNUSED(icon); +// unused +void NotificationsManager::actionInvoked(uint, QString) {} - NSUserNotification * notif = [[NSUserNotification alloc] init]; - - notif.title = roomName.toNSString(); - notif.subtitle = QString("%1 sent a message").arg(senderName).toNSString(); - notif.informativeText = text.toNSString(); - notif.soundName = NSUserNotificationDefaultSoundName; - - [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification: notif]; - [notif autorelease]; -} - -//unused -void -NotificationsManager::actionInvoked(uint, QString) -{ -} - -void -NotificationsManager::notificationClosed(uint, uint) -{ -} +void NotificationsManager::notificationClosed(uint, uint) {} diff --git a/src/roomlistmodel.cpp b/src/roomlistmodel.cpp index 5b3bdd9e1..8f42f5ae3 100644 --- a/src/roomlistmodel.cpp +++ b/src/roomlistmodel.cpp @@ -59,18 +59,19 @@ void RoomListModel::setConnection(Connection* connection) { void RoomListModel::doResetModel() { beginResetModel(); m_rooms.clear(); - for (auto r : m_connection->roomMap()) + for (auto r : m_connection->roomMap()) { doAddRoom(r); + } endResetModel(); refreshNotificationCount(); } -SpectralRoom* RoomListModel::roomAt(int row) { +SpectralRoom* RoomListModel::roomAt(int row) const { return m_rooms.at(row); } void RoomListModel::doAddRoom(Room* r) { - if (auto* room = static_cast(r)) { + if (auto room = static_cast(r)) { m_rooms.append(room); connectRoomSignals(room); emit roomAdded(room); @@ -134,7 +135,7 @@ void RoomListModel::updateRoom(Room* room, Room* prev) { // That doesn't look right but technically we still can do it. } // Ok, we're through with pre-checks, now for the real thing. - auto* newRoom = static_cast(room); + auto newRoom = static_cast(room); const auto it = std::find_if( m_rooms.begin(), m_rooms.end(), [=](const SpectralRoom* r) { return r == prev || r == newRoom; }); diff --git a/src/roomlistmodel.h b/src/roomlistmodel.h index e24609c91..36ecc93b8 100644 --- a/src/roomlistmodel.h +++ b/src/roomlistmodel.h @@ -45,23 +45,23 @@ class RoomListModel : public QAbstractListModel { CurrentRoomRole, }; - RoomListModel(QObject* parent = 0); - virtual ~RoomListModel(); + RoomListModel(QObject* parent = nullptr); + virtual ~RoomListModel() override; - Connection* connection() { return m_connection; } + Connection* connection() const { return m_connection; } void setConnection(Connection* connection); void doResetModel(); - Q_INVOKABLE SpectralRoom* roomAt(int row); + Q_INVOKABLE SpectralRoom* roomAt(int row) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; Q_INVOKABLE int rowCount( const QModelIndex& parent = QModelIndex()) const override; - QHash roleNames() const; + QHash roleNames() const override; - int notificationCount() { return m_notificationCount; } + int notificationCount() const { return m_notificationCount; } private slots: void doAddRoom(Room* room); diff --git a/src/spectralroom.cpp b/src/spectralroom.cpp index 7936fec7d..ae9636820 100644 --- a/src/spectralroom.cpp +++ b/src/spectralroom.cpp @@ -15,6 +15,8 @@ #include "events/typingevent.h" #include "jobs/downloadfilejob.h" +#include + #include #include #include @@ -34,24 +36,10 @@ SpectralRoom::SpectralRoom(Connection* connection, &SpectralRoom::countChanged); connect(this, &SpectralRoom::highlightCountChanged, this, &SpectralRoom::countChanged); - connect(this, &Room::addedMessages, this, [=] { setBusy(false); }); connect(this, &Room::fileTransferCompleted, this, [=] { setFileUploadingProgress(0); setHasFileUploading(false); }); - connect(this, &Room::accountDataChanged, this, [=](QString type) { - if (type == backgroundEventType) - emit backgroundChanged(); - }); -} - -inline QString getMIME(const QUrl& fileUrl) { - return QMimeDatabase().mimeTypeForFile(fileUrl.toLocalFile()).name(); -} - -inline QSize getImageSize(const QUrl& imageUrl) { - QImageReader reader(imageUrl.toLocalFile()); - return reader.size(); } void SpectralRoom::uploadFile(const QUrl& url, const QString& body) { @@ -61,13 +49,13 @@ void SpectralRoom::uploadFile(const QUrl& url, const QString& body) { QString txnId = postFile(body.isEmpty() ? url.fileName() : body, url, false); setHasFileUploading(true); connect(this, &Room::fileTransferCompleted, - [=](QString id, QUrl localFile, QUrl mxcUrl) { + [=](QString id, QUrl /*localFile*/, QUrl /*mxcUrl*/) { if (id == txnId) { setFileUploadingProgress(0); setHasFileUploading(false); } }); - connect(this, &Room::fileTransferFailed, [=](QString id, QString error) { + connect(this, &Room::fileTransferFailed, [=](QString id, QString /*error*/) { if (id == txnId) { setFileUploadingProgress(0); setHasFileUploading(false); @@ -91,24 +79,14 @@ void SpectralRoom::forget() { connection()->forgetRoom(id()); } -bool SpectralRoom::hasUsersTyping() { - QList users = usersTyping(); - if (users.isEmpty()) - return false; - int count = users.length(); - if (users.contains(localUser())) - count--; - return count != 0; -} - -QVariantList SpectralRoom::getUsersTyping() { - QList users = usersTyping(); - users.removeOne(localUser()); - QVariantList out; +QVariantList SpectralRoom::getUsersTyping() const { + auto users = usersTyping(); + users.removeAll(localUser()); + QVariantList userVariants; for (User* user : users) { - out.append(QVariant::fromValue(user)); + userVariants.append(QVariant::fromValue(user)); } - return out; + return userVariants; } void SpectralRoom::sendTypingNotification(bool isTyping) { @@ -116,7 +94,7 @@ void SpectralRoom::sendTypingNotification(bool isTyping) { id(), isTyping, 10000); } -QString SpectralRoom::lastEvent() { +QString SpectralRoom::lastEvent() const { for (auto i = messageEvents().rbegin(); i < messageEvents().rend(); i++) { const RoomEvent* evt = i->get(); @@ -185,7 +163,7 @@ void SpectralRoom::countChanged() { } } -QDateTime SpectralRoom::lastActiveTime() { +QDateTime SpectralRoom::lastActiveTime() const { if (timelineSize() == 0) return QDateTime(); return messageEvents().rbegin()->get()->timestamp(); @@ -194,13 +172,13 @@ QDateTime SpectralRoom::lastActiveTime() { int SpectralRoom::savedTopVisibleIndex() const { return firstDisplayedMarker() == timelineEdge() ? 0 - : firstDisplayedMarker() - messageEvents().rbegin(); + : int(firstDisplayedMarker() - messageEvents().rbegin()); } int SpectralRoom::savedBottomVisibleIndex() const { return lastDisplayedMarker() == timelineEdge() ? 0 - : lastDisplayedMarker() - messageEvents().rbegin(); + : int(lastDisplayedMarker() - messageEvents().rbegin()); } void SpectralRoom::saveViewport(int topIndex, int bottomIndex) { @@ -217,17 +195,13 @@ void SpectralRoom::saveViewport(int topIndex, int bottomIndex) { setLastDisplayedEvent(maxTimelineIndex() - bottomIndex); } -void SpectralRoom::getPreviousContent(int limit) { - setBusy(true); - Room::getPreviousContent(limit); -} - -QVariantList SpectralRoom::getUsers(const QString& prefix) { - auto userList = users(); +QVariantList SpectralRoom::getUsers(const QString& keyword) const { + const auto userList = users(); QVariantList matchedList; - for (auto u : userList) - if (u->displayname(this).toLower().startsWith(prefix.toLower())) + for (const auto u : userList) + if (u->displayname(this).contains(keyword, Qt::CaseInsensitive)) { matchedList.append(QVariant::fromValue(u)); + } return matchedList; } @@ -236,54 +210,167 @@ QUrl SpectralRoom::urlToMxcUrl(QUrl mxcUrl) { return DownloadFileJob::makeRequestUrl(connection()->homeserver(), mxcUrl); } -QUrl SpectralRoom::backgroundUrl() { - return hasAccountData(backgroundEventType) - ? QUrl(accountData(backgroundEventType) - .get() - ->contentJson()["url"] - .toString()) - : QUrl(); +QString SpectralRoom::avatarMediaId() const { + if (const auto avatar = Room::avatarMediaId(); !avatar.isEmpty()) { + return avatar; + } + + // Use the first (excluding self) user's avatar for direct chats + const auto dcUsers = directChatUsers(); + for (const auto u : dcUsers) { + if (u != localUser()) { + return u->avatarMediaId(); + } + } + + return {}; } -void SpectralRoom::setBackgroundUrl(QUrl url) { - if (url.isEmpty() || url == backgroundUrl()) - return; +QString SpectralRoom::eventToString(const RoomEvent& evt, + Qt::TextFormat format) const { + const bool prettyPrint = (format == Qt::RichText); - connection()->callApi( - localUser()->id(), id(), backgroundEventType, - QJsonObject{{"url", url.toString()}}); -} + using namespace QMatrixClient; + return visit( + evt, + [prettyPrint](const RoomMessageEvent& e) { + using namespace MessageEventContent; -void SpectralRoom::setBackgroundFromLocalFile(QUrl url) { - if (url.isEmpty()) - return; - - auto txnId = connection()->generateTxnId(); - Room::uploadFile(txnId, url); - - connect(this, &Room::fileTransferCompleted, - [=](QString id, QUrl localFile, QUrl mxcUrl) { - if (id == txnId) { - setBackgroundUrl(mxcUrl); + if (prettyPrint && e.hasTextContent() && + e.mimeType().name() != "text/plain") + return static_cast(e.content())->body; + if (e.hasFileContent()) { + auto fileCaption = + e.content()->fileInfo()->originalName.toHtmlEscaped(); + if (fileCaption.isEmpty()) { + fileCaption = prettyPrint + ? QMatrixClient::prettyPrint(e.plainBody()) + : e.plainBody(); + } + return !fileCaption.isEmpty() ? fileCaption : tr("a file"); + } + return prettyPrint ? QMatrixClient::prettyPrint(e.plainBody()) + : e.plainBody(); + }, + [this](const RoomMemberEvent& e) { + // FIXME: Rewind to the name that was at the time of this event + auto subjectName = this->user(e.userId())->displayname(); + // 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().toHtmlEscaped()); + } + if (e.isAvatarUpdate()) { + if (!text.isEmpty()) + text += " and "; + if (e.avatarUrl().isEmpty()) + text += tr("cleared their avatar"); + else if (e.prevContent()->avatarUrl.isEmpty()) + text += tr("set an avatar"); + else + text += tr("updated their avatar"); + } + return text; + } + case MembershipType::Leave: + if (e.prevContent() && + e.prevContent()->membership == MembershipType::Invite) { + return (e.senderId() != e.userId()) + ? tr("withdrew %1's invitation").arg(subjectName) + : tr("rejected the invitation"); } - }); -} -void SpectralRoom::clearBackground() { - connection()->callApi( - localUser()->id(), id(), backgroundEventType, QJsonObject{}); -} - -QString SpectralRoom::backgroundMediaId() { - if (!hasAccountData(backgroundEventType)) - return {}; - - auto url = backgroundUrl(); - return url.authority() + url.path(); + 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 put %1 out of the room: %2") + .arg(subjectName, e.contentJson()["reason"_ls] + .toString() + .toHtmlEscaped()) + : tr("left the room"); + case MembershipType::Ban: + return (e.senderId() != e.userId()) + ? tr("banned %1 from the room: %2") + .arg(subjectName, e.contentJson()["reason"_ls] + .toString() + .toHtmlEscaped()) + : tr("self-banned from the room"); + case MembershipType::Knock: + return tr("knocked"); + default:; + } + return tr("made something unknown"); + }, + [](const RoomAliasesEvent& e) { + return tr("has set room aliases on server %1 to: %2") + .arg(e.stateKey(), QLocale().createSeparatedList(e.aliases())); + }, + [](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().toHtmlEscaped()); + }, + [prettyPrint](const RoomTopicEvent& e) { + return (e.topic().isEmpty()) + ? tr("cleared the topic") + : tr("set the topic to: %1") + .arg(prettyPrint + ? QMatrixClient::prettyPrint(e.topic()) + : e.topic()); + }, + [](const RoomAvatarEvent&) { return tr("changed the room avatar"); }, + [](const EncryptionEvent&) { + return tr("activated End-to-End Encryption"); + }, + [](const RoomCreateEvent& e) { + return (e.isUpgrade() ? tr("upgraded the room to version %1") + : tr("created the room, version %1")) + .arg(e.version().isEmpty() ? "1" : e.version().toHtmlEscaped()); + }, + [](const StateEventBase& e) { + // A small hack for state events from TWIM bot + return e.stateKey() == "twim" + ? tr("updated the database", "TWIM bot updated the database") + : e.stateKey().isEmpty() + ? tr("updated %1 state", "%1 - Matrix event type") + .arg(e.matrixType()) + : tr("updated %1 state for %2", + "%1 - Matrix event type, %2 - state key") + .arg(e.matrixType(), + e.stateKey().toHtmlEscaped()); + }, + tr("Unknown event")); } void SpectralRoom::changeAvatar(QUrl localFile) { - auto job = connection()->uploadFile(localFile.toLocalFile()); + const auto job = connection()->uploadFile(localFile.toLocalFile()); if (isJobRunning(job)) { connect(job, &BaseJob::success, this, [this, job] { connection()->callApi( @@ -297,7 +384,7 @@ void SpectralRoom::addLocalAlias(const QString& alias) { if (aliases.contains(alias)) return; - aliases.append(alias); + aliases += alias; setLocalAliases(aliases); } @@ -314,12 +401,12 @@ void SpectralRoom::removeLocalAlias(const QString& alias) { QString SpectralRoom::markdownToHTML(const QString& markdown) { const auto str = markdown.toUtf8(); - const char* tmp_buf = + char* tmp_buf = cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_DEFAULT); - std::string html(tmp_buf); + const std::string html(tmp_buf); - free((char*)tmp_buf); + free(tmp_buf); auto result = QString::fromStdString(html).trimmed(); @@ -332,8 +419,8 @@ QString SpectralRoom::markdownToHTML(const QString& markdown) { void SpectralRoom::postArbitaryMessage(const QString& text, MessageEventType type, const QString& replyEventId) { - auto parsedHTML = markdownToHTML(text); - bool isRichText = Qt::mightBeRichText(parsedHTML); + const auto parsedHTML = markdownToHTML(text); + const bool isRichText = Qt::mightBeRichText(parsedHTML); if (isRichText) { // Markdown postHtmlMessage(text, parsedHTML, type, replyEventId); @@ -463,7 +550,7 @@ void SpectralRoom::toggleReaction(const QString& eventId, } if (!redactEventIds.isEmpty()) { - for (auto redactEventId : redactEventIds) { + for (const auto& redactEventId : redactEventIds) { redactEvent(redactEventId); } } else { diff --git a/src/spectralroom.h b/src/spectralroom.h index 972a8f269..60c61458d 100644 --- a/src/spectralroom.h +++ b/src/spectralroom.h @@ -20,20 +20,12 @@ using namespace QMatrixClient; class SpectralRoom : public Room { Q_OBJECT - Q_PROPERTY(bool hasUsersTyping READ hasUsersTyping NOTIFY typingChanged) Q_PROPERTY(QVariantList usersTyping READ getUsersTyping NOTIFY typingChanged) - Q_PROPERTY(QString cachedInput READ cachedInput WRITE setCachedInput NOTIFY - cachedInputChanged) - Q_PROPERTY(bool hasFileUploading READ hasFileUploading NOTIFY - hasFileUploadingChanged) + Q_PROPERTY(QString cachedInput MEMBER m_cachedInput NOTIFY cachedInputChanged) + Q_PROPERTY(bool hasFileUploading READ hasFileUploading WRITE + setHasFileUploading NOTIFY hasFileUploadingChanged) Q_PROPERTY(int fileUploadingProgress READ fileUploadingProgress NOTIFY fileUploadingProgressChanged) - Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) - Q_PROPERTY(QUrl backgroundUrl READ backgroundUrl WRITE setBackgroundUrl NOTIFY - backgroundChanged) - Q_PROPERTY( - QString backgroundMediaId READ backgroundMediaId NOTIFY backgroundChanged) - // Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarChanged) Q_PROPERTY(QString avatarMediaId READ avatarMediaId NOTIFY avatarChanged STORED false) @@ -42,244 +34,51 @@ class SpectralRoom : public Room { QString roomId, JoinState joinState = {}); - const QString& cachedInput() const { return m_cachedInput; } - void setCachedInput(const QString& input) { - if (input != m_cachedInput) { - m_cachedInput = input; - emit cachedInputChanged(); - } - } + QVariantList getUsersTyping() const; - bool busy() { return m_busy; } - void setBusy(bool value) { - if (m_busy != value) { - m_busy = value; - emit busyChanged(); - } - } - - bool hasUsersTyping(); - QVariantList getUsersTyping(); - - QString lastEvent(); + QString lastEvent() const; bool isEventHighlighted(const QMatrixClient::RoomEvent* e) const; - QDateTime lastActiveTime(); + QDateTime lastActiveTime() const; - bool hasFileUploading() { return m_hasFileUploading; } + bool hasFileUploading() const { return m_hasFileUploading; } void setHasFileUploading(bool value) { - if (m_hasFileUploading != value) { - m_hasFileUploading = value; - emit hasFileUploadingChanged(); + if (value == m_hasFileUploading) { + return; } + m_hasFileUploading = value; + emit hasFileUploadingChanged(); } - int fileUploadingProgress() { return m_fileUploadingProgress; } + int fileUploadingProgress() const { return m_fileUploadingProgress; } void setFileUploadingProgress(int value) { - if (m_fileUploadingProgress != value) { - m_fileUploadingProgress = value; - emit fileUploadingProgressChanged(); + if (m_fileUploadingProgress == value) { + return; } + m_fileUploadingProgress = value; + emit fileUploadingProgressChanged(); } Q_INVOKABLE int savedTopVisibleIndex() const; Q_INVOKABLE int savedBottomVisibleIndex() const; Q_INVOKABLE void saveViewport(int topIndex, int bottomIndex); - Q_INVOKABLE void getPreviousContent(int limit = 10); - - Q_INVOKABLE QVariantList getUsers(const QString& prefix); + Q_INVOKABLE QVariantList getUsers(const QString& keyword) const; Q_INVOKABLE QUrl urlToMxcUrl(QUrl mxcUrl); - QUrl avatarUrl() const { - if (!Room::avatarUrl().isEmpty()) - return Room::avatarUrl(); + QString avatarMediaId() const; - // Use the first (excluding self) user's avatar for direct chats - const auto dcUsers = directChatUsers(); - for (auto* u : dcUsers) - if (u != localUser()) - return u->avatarUrl(); - - return {}; - } - - QString avatarMediaId() const { - if (!Room::avatarMediaId().isEmpty()) - return Room::avatarMediaId(); - - // Use the first (excluding self) user's avatar for direct chats - const auto dcUsers = directChatUsers(); - for (auto* u : dcUsers) - if (u != localUser()) - return u->avatarMediaId(); - - return {}; - } - - QUrl backgroundUrl(); - Q_INVOKABLE void setBackgroundUrl(QUrl url); - Q_INVOKABLE void clearBackground(); - Q_INVOKABLE void setBackgroundFromLocalFile(QUrl url); - - QString backgroundMediaId(); - - template - QString eventToString(const BaseEventT& evt, - Qt::TextFormat format = Qt::PlainText) { - bool prettyPrint = (format == Qt::RichText); - - using namespace QMatrixClient; - return visit( - evt, - [this, prettyPrint](const RoomMessageEvent& e) { - using namespace MessageEventContent; - - if (prettyPrint && e.hasTextContent() && - e.mimeType().name() != "text/plain") - return static_cast(e.content())->body; - if (e.hasFileContent()) { - auto fileCaption = - e.content()->fileInfo()->originalName.toHtmlEscaped(); - if (fileCaption.isEmpty()) { - if (prettyPrint) - fileCaption = this->prettyPrint(e.plainBody()); - else - fileCaption = e.plainBody(); - } - return !fileCaption.isEmpty() ? fileCaption : tr("a file"); - } - return prettyPrint ? this->prettyPrint(e.plainBody()) : e.plainBody(); - }, - [this](const RoomMemberEvent& e) { - // FIXME: Rewind to the name that was at the time of this event - auto subjectName = this->user(e.userId())->displayname(); - // 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().toHtmlEscaped()); - } - if (e.isAvatarUpdate()) { - if (!text.isEmpty()) - text += " and "; - if (e.avatarUrl().isEmpty()) - text += tr("cleared their avatar"); - else if (e.prevContent()->avatarUrl.isEmpty()) - text += tr("set an avatar"); - else - text += tr("updated their avatar"); - } - return text; - } - case MembershipType::Leave: - if (e.prevContent() && - e.prevContent()->membership == MembershipType::Invite) { - return (e.senderId() != e.userId()) - ? tr("withdrew %1's invitation").arg(subjectName) - : tr("rejected the invitation"); - } - - 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 put %1 out of the room: %2") - .arg(subjectName, e.contentJson()["reason"_ls] - .toString() - .toHtmlEscaped()) - : tr("left the room"); - case MembershipType::Ban: - return (e.senderId() != e.userId()) - ? tr("banned %1 from the room: %2") - .arg(subjectName, e.contentJson()["reason"_ls] - .toString() - .toHtmlEscaped()) - : tr("self-banned from the room"); - case MembershipType::Knock: - return tr("knocked"); - default:; - } - return tr("made something unknown"); - }, - [](const RoomAliasesEvent& e) { - return tr("has set room aliases on server %1 to: %2") - .arg(e.stateKey(), QLocale().createSeparatedList(e.aliases())); - }, - [](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().toHtmlEscaped()); - }, - [this, prettyPrint](const RoomTopicEvent& e) { - return (e.topic().isEmpty()) - ? tr("cleared the topic") - : tr("set the topic to: %1") - .arg(prettyPrint ? this->prettyPrint(e.topic()) - : e.topic()); - }, - [](const RoomAvatarEvent&) { return tr("changed the room avatar"); }, - [](const EncryptionEvent&) { - return tr("activated End-to-End Encryption"); - }, - [](const RoomCreateEvent& e) { - return (e.isUpgrade() ? tr("upgraded the room to version %1") - : tr("created the room, version %1")) - .arg(e.version().isEmpty() ? "1" : e.version().toHtmlEscaped()); - }, - [](const StateEventBase& e) { - // A small hack for state events from TWIM bot - return e.stateKey() == "twim" - ? tr("updated the database", - "TWIM bot updated the database") - : e.stateKey().isEmpty() - ? tr("updated %1 state", "%1 - Matrix event type") - .arg(e.matrixType()) - : tr("updated %1 state for %2", - "%1 - Matrix event type, %2 - state key") - .arg(e.matrixType(), - e.stateKey().toHtmlEscaped()); - }, - tr("Unknown event")); - } + QString eventToString(const RoomEvent& evt, + Qt::TextFormat format = Qt::PlainText) const; private: - const QString backgroundEventType = "org.eu.encom.spectral.background"; - QString m_cachedInput; QSet highlights; bool m_hasFileUploading = false; int m_fileUploadingProgress = 0; - bool m_busy = false; - void checkForHighlights(const QMatrixClient::TimelineItem& ti); void onAddNewTimelineEvents(timeline_iter_t from) override; diff --git a/src/userlistmodel.cpp b/src/userlistmodel.cpp index ff7e697c2..f711584b3 100644 --- a/src/userlistmodel.cpp +++ b/src/userlistmodel.cpp @@ -50,7 +50,7 @@ void UserListModel::setRoom(QMatrixClient::Room* room) { emit roomChanged(); } -QMatrixClient::User* UserListModel::userAt(QModelIndex index) { +QMatrixClient::User* UserListModel::userAt(QModelIndex index) const { if (index.row() < 0 || index.row() >= m_users.size()) return nullptr; return m_users.at(index.row()); @@ -133,9 +133,11 @@ int UserListModel::findUserPos(const QString& username) const { QHash UserListModel::roleNames() const { QHash roles; + roles[NameRole] = "name"; roles[UserIDRole] = "userId"; roles[AvatarRole] = "avatar"; roles[ObjectRole] = "user"; + return roles; } diff --git a/src/userlistmodel.h b/src/userlistmodel.h index 25db08af8..18b848b7e 100644 --- a/src/userlistmodel.h +++ b/src/userlistmodel.h @@ -28,9 +28,9 @@ class UserListModel : public QAbstractListModel { UserListModel(QObject* parent = nullptr); - QMatrixClient::Room* room() { return m_currentRoom; } + QMatrixClient::Room* room() const { return m_currentRoom; } void setRoom(QMatrixClient::Room* room); - User* userAt(QModelIndex index); + User* userAt(QModelIndex index) const; QVariant data(const QModelIndex& index, int role = NameRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;