mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
Cache refactoring
This commit is contained in:
parent
ca66940ec3
commit
2f00fc51bf
26 changed files with 1361 additions and 920 deletions
|
@ -17,45 +17,30 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
#include <QSharedPointer>
|
||||
#include <QUrl>
|
||||
#include <functional>
|
||||
|
||||
class MatrixClient;
|
||||
class TimelineItem;
|
||||
|
||||
//! Saved cache data per user.
|
||||
struct AvatarData
|
||||
{
|
||||
//! The avatar image of the user.
|
||||
QImage img;
|
||||
//! The url that was used to download the avatar.
|
||||
QUrl url;
|
||||
};
|
||||
|
||||
class AvatarProvider : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void init(QSharedPointer<MatrixClient> client);
|
||||
static void init(QSharedPointer<MatrixClient> client) { client_ = client; }
|
||||
//! The callback is called with the downloaded avatar for the given user
|
||||
//! or the avatar is downloaded first and then saved for re-use.
|
||||
static void resolve(const QString &userId,
|
||||
static void resolve(const QString &room_id,
|
||||
const QString &userId,
|
||||
QObject *receiver,
|
||||
std::function<void(QImage)> callback);
|
||||
//! Used to initialize the mapping user -> avatar url.
|
||||
static void setAvatarUrl(const QString &userId, const QUrl &url);
|
||||
//! Remove all saved data.
|
||||
static void clear() { avatars_.clear(); };
|
||||
|
||||
private:
|
||||
//! Update the cache with the downloaded avatar.
|
||||
static void updateAvatar(const QString &uid, const QImage &img);
|
||||
|
||||
static QSharedPointer<MatrixClient> client_;
|
||||
|
||||
using UserID = QString;
|
||||
static std::map<UserID, AvatarData> avatars_;
|
||||
static QHash<QString, QImage> avatars_;
|
||||
};
|
||||
|
|
317
include/Cache.h
317
include/Cache.h
|
@ -17,12 +17,23 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <json.hpp>
|
||||
#include <lmdb++.h>
|
||||
#include <mtx/responses.hpp>
|
||||
|
||||
#include "RoomState.h"
|
||||
#include "Utils.h"
|
||||
|
||||
struct SearchResult
|
||||
{
|
||||
QString user_id;
|
||||
QString display_name;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(SearchResult)
|
||||
Q_DECLARE_METATYPE(QVector<SearchResult>)
|
||||
|
||||
//! Used to uniquely identify a list of read receipts.
|
||||
struct ReadReceiptKey
|
||||
|
@ -44,6 +55,60 @@ from_json(const json &j, ReadReceiptKey &key)
|
|||
key.room_id = j.at("room_id").get<std::string>();
|
||||
}
|
||||
|
||||
//! UI info associated with a room.
|
||||
struct RoomInfo
|
||||
{
|
||||
//! The calculated name of the room.
|
||||
std::string name;
|
||||
//! The topic of the room.
|
||||
std::string topic;
|
||||
//! The calculated avatar url of the room.
|
||||
std::string avatar_url;
|
||||
//! Whether or not the room is an invite.
|
||||
bool is_invite = false;
|
||||
};
|
||||
|
||||
inline void
|
||||
to_json(json &j, const RoomInfo &info)
|
||||
{
|
||||
j["name"] = info.name;
|
||||
j["topic"] = info.topic;
|
||||
j["avatar_url"] = info.avatar_url;
|
||||
j["is_invite"] = info.is_invite;
|
||||
}
|
||||
|
||||
inline void
|
||||
from_json(const json &j, RoomInfo &info)
|
||||
{
|
||||
info.name = j.at("name");
|
||||
info.topic = j.at("topic");
|
||||
info.avatar_url = j.at("avatar_url");
|
||||
info.is_invite = j.at("is_invite");
|
||||
}
|
||||
|
||||
//! Basic information per member;
|
||||
struct MemberInfo
|
||||
{
|
||||
std::string name;
|
||||
std::string avatar_url;
|
||||
};
|
||||
|
||||
inline void
|
||||
to_json(json &j, const MemberInfo &info)
|
||||
{
|
||||
j["name"] = info.name;
|
||||
j["avatar_url"] = info.avatar_url;
|
||||
}
|
||||
|
||||
inline void
|
||||
from_json(const json &j, MemberInfo &info)
|
||||
{
|
||||
info.name = j.at("name");
|
||||
info.avatar_url = j.at("avatar_url");
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(RoomInfo)
|
||||
|
||||
class Cache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -51,22 +116,50 @@ class Cache : public QObject
|
|||
public:
|
||||
Cache(const QString &userId, QObject *parent = nullptr);
|
||||
|
||||
void setState(const QString &nextBatchToken,
|
||||
const std::map<QString, QSharedPointer<RoomState>> &states);
|
||||
static QHash<QString, QString> DisplayNames;
|
||||
static QHash<QString, QString> AvatarUrls;
|
||||
|
||||
static std::string displayName(const std::string &room_id, const std::string &user_id);
|
||||
static QString displayName(const QString &room_id, const QString &user_id);
|
||||
static QString avatarUrl(const QString &room_id, const QString &user_id);
|
||||
|
||||
static void removeDisplayName(const QString &room_id, const QString &user_id);
|
||||
static void removeAvatarUrl(const QString &room_id, const QString &user_id);
|
||||
|
||||
static void insertDisplayName(const QString &room_id,
|
||||
const QString &user_id,
|
||||
const QString &display_name);
|
||||
static void insertAvatarUrl(const QString &room_id,
|
||||
const QString &user_id,
|
||||
const QString &avatar_url);
|
||||
|
||||
//! Load saved data for the display names & avatars.
|
||||
void populateMembers();
|
||||
std::vector<std::string> joinedRooms();
|
||||
|
||||
QMap<QString, RoomInfo> roomInfo();
|
||||
|
||||
//! Calculate & return the name of the room.
|
||||
QString getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
|
||||
//! Retrieve the topic of the room if any.
|
||||
QString getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
//! Retrieve the room avatar's url if any.
|
||||
QString getRoomAvatarUrl(lmdb::txn &txn,
|
||||
lmdb::dbi &statesdb,
|
||||
lmdb::dbi &membersdb,
|
||||
const QString &room_id);
|
||||
|
||||
void saveState(const mtx::responses::Sync &res);
|
||||
bool isInitialized() const;
|
||||
|
||||
QString nextBatchToken() const;
|
||||
void states();
|
||||
|
||||
using Invites = std::map<std::string, mtx::responses::InvitedRoom>;
|
||||
Invites invites();
|
||||
void setInvites(const Invites &invites);
|
||||
|
||||
void deleteData();
|
||||
void unmount() { isMounted_ = false; };
|
||||
|
||||
void removeRoom(const QString &roomid);
|
||||
void removeInvite(const QString &roomid);
|
||||
void removeInvite(const std::string &room_id);
|
||||
void removeRoom(lmdb::txn &txn, const std::string &roomid);
|
||||
void removeRoom(const std::string &roomid);
|
||||
void removeRoom(const QString &roomid) { removeRoom(roomid.toStdString()); };
|
||||
void setup();
|
||||
|
||||
bool isFormatValid();
|
||||
|
@ -88,24 +181,206 @@ public:
|
|||
QByteArray image(const QString &url) const;
|
||||
void saveImage(const QString &url, const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void statesLoaded(std::map<QString, RoomState> states);
|
||||
std::vector<std::string> roomsWithStateUpdates(const mtx::responses::Sync &res);
|
||||
std::map<QString, RoomInfo> getRoomInfo(const std::vector<std::string> &rooms);
|
||||
std::map<QString, RoomInfo> roomUpdates(const mtx::responses::Sync &sync)
|
||||
{
|
||||
return getRoomInfo(roomsWithStateUpdates(sync));
|
||||
}
|
||||
|
||||
QVector<SearchResult> getAutocompleteMatches(const std::string &room_id,
|
||||
const std::string &query,
|
||||
std::uint8_t max_items = 5);
|
||||
|
||||
private:
|
||||
//! Save an invited room.
|
||||
void saveInvite(lmdb::txn &txn,
|
||||
lmdb::dbi &statesdb,
|
||||
lmdb::dbi &membersdb,
|
||||
const mtx::responses::InvitedRoom &room);
|
||||
|
||||
QString getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
|
||||
QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||
QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
|
||||
|
||||
//! Remove a room from the cache.
|
||||
// void removeLeftRoom(lmdb::txn &txn, const std::string &room_id);
|
||||
template<class T>
|
||||
void saveStateEvents(lmdb::txn &txn,
|
||||
const lmdb::dbi &statesdb,
|
||||
const lmdb::dbi &membersdb,
|
||||
const std::string &room_id,
|
||||
const std::vector<T> &events)
|
||||
{
|
||||
for (const auto &e : events)
|
||||
saveStateEvent(txn, statesdb, membersdb, room_id, e);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void saveStateEvent(lmdb::txn &txn,
|
||||
const lmdb::dbi &statesdb,
|
||||
const lmdb::dbi &membersdb,
|
||||
const std::string &room_id,
|
||||
const T &event)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
using namespace mtx::events::state;
|
||||
|
||||
if (mpark::holds_alternative<StateEvent<Member>>(event)) {
|
||||
const auto e = mpark::get<StateEvent<Member>>(event);
|
||||
|
||||
switch (e.content.membership) {
|
||||
//
|
||||
// We only keep users with invite or join membership.
|
||||
//
|
||||
case Membership::Invite:
|
||||
case Membership::Join: {
|
||||
auto display_name = e.content.display_name.empty()
|
||||
? e.state_key
|
||||
: e.content.display_name;
|
||||
|
||||
// Lightweight representation of a member.
|
||||
MemberInfo tmp{display_name, e.content.avatar_url};
|
||||
|
||||
lmdb::dbi_put(txn,
|
||||
membersdb,
|
||||
lmdb::val(e.state_key),
|
||||
lmdb::val(json(tmp).dump()));
|
||||
|
||||
insertDisplayName(QString::fromStdString(room_id),
|
||||
QString::fromStdString(e.state_key),
|
||||
QString::fromStdString(display_name));
|
||||
|
||||
insertAvatarUrl(QString::fromStdString(room_id),
|
||||
QString::fromStdString(e.state_key),
|
||||
QString::fromStdString(e.content.avatar_url));
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
lmdb::dbi_del(
|
||||
txn, membersdb, lmdb::val(e.state_key), lmdb::val(""));
|
||||
|
||||
removeDisplayName(QString::fromStdString(room_id),
|
||||
QString::fromStdString(e.state_key));
|
||||
removeAvatarUrl(QString::fromStdString(room_id),
|
||||
QString::fromStdString(e.state_key));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isStateEvent(event))
|
||||
return;
|
||||
|
||||
mpark::visit(
|
||||
[&txn, &statesdb](auto e) {
|
||||
lmdb::dbi_put(
|
||||
txn, statesdb, lmdb::val(to_string(e.type)), lmdb::val(json(e).dump()));
|
||||
},
|
||||
event);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool isStateEvent(const T &e)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
using namespace mtx::events::state;
|
||||
|
||||
return mpark::holds_alternative<StateEvent<Aliases>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<state::Avatar>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<CanonicalAlias>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<Create>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<GuestAccess>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<HistoryVisibility>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<JoinRules>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<Name>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<PowerLevels>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<Topic>>(e);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool containsStateUpdates(const T &e)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
using namespace mtx::events::state;
|
||||
|
||||
return mpark::holds_alternative<StateEvent<state::Avatar>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<CanonicalAlias>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<Name>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<Member>>(e) ||
|
||||
mpark::holds_alternative<StateEvent<Topic>>(e);
|
||||
}
|
||||
|
||||
bool containsStateUpdates(const mtx::events::collections::StrippedEvents &e)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
using namespace mtx::events::state;
|
||||
|
||||
return mpark::holds_alternative<StrippedEvent<state::Avatar>>(e) ||
|
||||
mpark::holds_alternative<StrippedEvent<CanonicalAlias>>(e) ||
|
||||
mpark::holds_alternative<StrippedEvent<Name>>(e) ||
|
||||
mpark::holds_alternative<StrippedEvent<Member>>(e) ||
|
||||
mpark::holds_alternative<StrippedEvent<Topic>>(e);
|
||||
}
|
||||
|
||||
void saveInvites(lmdb::txn &txn,
|
||||
const std::map<std::string, mtx::responses::InvitedRoom> &rooms);
|
||||
|
||||
//! Remove any saved invites that are not found in the input.
|
||||
void removeStaleInvites(lmdb::txn &txn, const std::map<std::string, bool> &curr);
|
||||
|
||||
//! Sends signals for the rooms that are removed.
|
||||
void removeLeftRooms(lmdb::txn &txn,
|
||||
const std::map<std::string, mtx::responses::LeftRoom> &rooms)
|
||||
{
|
||||
for (const auto &room : rooms)
|
||||
removeRoom(txn, room.first);
|
||||
}
|
||||
|
||||
lmdb::dbi getInviteStatesDb(lmdb::txn &txn, const std::string &room_id)
|
||||
{
|
||||
return lmdb::dbi::open(
|
||||
txn, std::string(room_id + "/invite_state").c_str(), MDB_CREATE);
|
||||
}
|
||||
|
||||
lmdb::dbi getInviteMembersDb(lmdb::txn &txn, const std::string &room_id)
|
||||
{
|
||||
return lmdb::dbi::open(
|
||||
txn, std::string(room_id + "/invite_members").c_str(), MDB_CREATE);
|
||||
}
|
||||
|
||||
lmdb::dbi getStatesDb(lmdb::txn &txn, const std::string &room_id)
|
||||
{
|
||||
return lmdb::dbi::open(txn, std::string(room_id + "/state").c_str(), MDB_CREATE);
|
||||
}
|
||||
|
||||
lmdb::dbi getMembersDb(lmdb::txn &txn, const std::string &room_id)
|
||||
{
|
||||
return lmdb::dbi::open(txn, std::string(room_id + "/members").c_str(), MDB_CREATE);
|
||||
}
|
||||
|
||||
QString getDisplayName(const mtx::events::StateEvent<mtx::events::state::Member> &event)
|
||||
{
|
||||
if (!event.content.display_name.empty())
|
||||
return QString::fromStdString(event.content.display_name);
|
||||
|
||||
return QString::fromStdString(event.state_key);
|
||||
}
|
||||
|
||||
void setNextBatchToken(lmdb::txn &txn, const std::string &token);
|
||||
void setNextBatchToken(lmdb::txn &txn, const QString &token);
|
||||
void insertRoomState(lmdb::txn &txn,
|
||||
const QString &roomid,
|
||||
const QSharedPointer<RoomState> &state);
|
||||
|
||||
lmdb::env env_;
|
||||
lmdb::dbi stateDb_;
|
||||
lmdb::dbi roomDb_;
|
||||
lmdb::dbi syncStateDb_;
|
||||
lmdb::dbi roomsDb_;
|
||||
lmdb::dbi invitesDb_;
|
||||
lmdb::dbi imagesDb_;
|
||||
lmdb::dbi mediaDb_;
|
||||
lmdb::dbi readReceiptsDb_;
|
||||
|
||||
bool isMounted_;
|
||||
|
||||
QString userId_;
|
||||
QString localUserId_;
|
||||
QString cacheDirectory_;
|
||||
};
|
||||
|
|
|
@ -24,16 +24,16 @@
|
|||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Cache.h"
|
||||
#include "CommunitiesList.h"
|
||||
#include "Community.h"
|
||||
|
||||
#include <mtx.hpp>
|
||||
|
||||
class Cache;
|
||||
class MatrixClient;
|
||||
class OverlayModal;
|
||||
class QuickSwitcher;
|
||||
class RoomList;
|
||||
class RoomSettings;
|
||||
class RoomState;
|
||||
class SideBarActions;
|
||||
class Splitter;
|
||||
|
@ -52,6 +52,9 @@ constexpr int CONSENSUS_TIMEOUT = 1000;
|
|||
constexpr int SHOW_CONTENT_TIMEOUT = 3000;
|
||||
constexpr int TYPING_REFRESH_TIMEOUT = 10000;
|
||||
|
||||
Q_DECLARE_METATYPE(mtx::responses::Rooms);
|
||||
Q_DECLARE_METATYPE(std::vector<std::string>);
|
||||
|
||||
class ChatPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -88,6 +91,14 @@ signals:
|
|||
void showLoginPage(const QString &msg);
|
||||
void showUserSettingsPage();
|
||||
void showOverlayProgressBar();
|
||||
void startConsesusTimer();
|
||||
|
||||
void initializeRoomList(QMap<QString, RoomInfo>);
|
||||
void initializeViews(const mtx::responses::Rooms &rooms);
|
||||
void initializeEmptyViews(const std::vector<std::string> &rooms);
|
||||
void syncUI(const mtx::responses::Rooms &rooms);
|
||||
void continueSync(const QString &next_batch);
|
||||
void syncRoomlist(const std::map<QString, RoomInfo> &updates);
|
||||
|
||||
private slots:
|
||||
void showUnreadMessageNotification(int count);
|
||||
|
@ -98,9 +109,9 @@ private slots:
|
|||
void syncCompleted(const mtx::responses::Sync &response);
|
||||
void changeTopRoomInfo(const QString &room_id);
|
||||
void logout();
|
||||
void addRoom(const QString &room_id);
|
||||
void removeRoom(const QString &room_id);
|
||||
void removeInvite(const QString &room_id);
|
||||
//! Handles initial sync failures.
|
||||
void retryInitialSync(int status_code = -1);
|
||||
|
||||
private:
|
||||
static ChatPage *instance_;
|
||||
|
@ -110,28 +121,11 @@ private:
|
|||
using Membership = mtx::events::StateEvent<mtx::events::state::Member>;
|
||||
using Memberships = std::map<std::string, Membership>;
|
||||
|
||||
using JoinedRooms = std::map<std::string, mtx::responses::JoinedRoom>;
|
||||
using LeftRooms = std::map<std::string, mtx::responses::LeftRoom>;
|
||||
using InvitedRooms = std::map<std::string, mtx::responses::InvitedRoom>;
|
||||
|
||||
using LeftRooms = std::map<std::string, mtx::responses::LeftRoom>;
|
||||
void removeLeftRooms(const LeftRooms &rooms);
|
||||
void updateJoinedRooms(const JoinedRooms &rooms);
|
||||
void trackInvites(const InvitedRooms &rooms)
|
||||
{
|
||||
for (const auto &invite : rooms)
|
||||
roomInvites_[QString::fromStdString(invite.first)] = true;
|
||||
}
|
||||
|
||||
std::map<QString, QSharedPointer<RoomState>> generateMembershipDifference(
|
||||
const JoinedRooms &rooms,
|
||||
const RoomStates &states) const;
|
||||
|
||||
void updateTypingUsers(const QString &roomid, const std::vector<std::string> &user_ids);
|
||||
|
||||
using MemberEvent = mtx::events::StateEvent<mtx::events::state::Member>;
|
||||
void updateUserDisplayName(const MemberEvent &event);
|
||||
void updateUserAvatarUrl(const MemberEvent &event);
|
||||
|
||||
void loadStateFromCache();
|
||||
void deleteConfigs();
|
||||
void resetUI();
|
||||
|
@ -141,10 +135,6 @@ private:
|
|||
template<class Collection>
|
||||
Memberships getMemberships(const std::vector<Collection> &events) const;
|
||||
|
||||
template<class Collection>
|
||||
void updateUserMetadata(const std::vector<Collection> &collection);
|
||||
|
||||
void retryInitialSync(int status_code = -1);
|
||||
//! Update the room with the new notification count.
|
||||
void updateRoomNotificationCount(const QString &room_id, uint16_t notification_count);
|
||||
|
||||
|
@ -186,8 +176,6 @@ private:
|
|||
UserInfoWidget *user_info_widget_;
|
||||
|
||||
RoomStates roomStates_;
|
||||
std::map<QString, QSharedPointer<RoomSettings>> roomSettings_;
|
||||
std::map<QString, bool> roomInvites_;
|
||||
|
||||
std::map<QString, QSharedPointer<Community>> communities_;
|
||||
|
||||
|
@ -211,22 +199,6 @@ private:
|
|||
QSharedPointer<Cache> cache_;
|
||||
};
|
||||
|
||||
template<class Collection>
|
||||
void
|
||||
ChatPage::updateUserMetadata(const std::vector<Collection> &collection)
|
||||
{
|
||||
using Member = mtx::events::StateEvent<mtx::events::state::Member>;
|
||||
|
||||
for (const auto &event : collection) {
|
||||
if (mpark::holds_alternative<Member>(event)) {
|
||||
auto member = mpark::get<Member>(event);
|
||||
|
||||
updateUserAvatarUrl(member);
|
||||
updateUserDisplayName(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Collection>
|
||||
std::map<std::string, mtx::events::StateEvent<mtx::events::state::Member>>
|
||||
ChatPage::getMemberships(const std::vector<Collection> &collection) const
|
||||
|
|
|
@ -32,6 +32,8 @@ signals:
|
|||
void avatarDownloaded(const QImage &img);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(mtx::responses::Sync)
|
||||
|
||||
/*
|
||||
* MatrixClient provides the high level API to communicate with
|
||||
* a Matrix homeserver. All the responses are returned through signals.
|
||||
|
|
|
@ -24,11 +24,9 @@
|
|||
|
||||
#include <mtx/responses.hpp>
|
||||
|
||||
#include "RoomState.h"
|
||||
|
||||
class Menu;
|
||||
class RippleOverlay;
|
||||
class RoomSettings;
|
||||
struct RoomInfo;
|
||||
|
||||
struct DescInfo
|
||||
{
|
||||
|
@ -70,24 +68,13 @@ class RoomInfoListItem : public QWidget
|
|||
Q_PROPERTY(QColor btnTextColor READ btnTextColor WRITE setBtnTextColor)
|
||||
|
||||
public:
|
||||
RoomInfoListItem(QSharedPointer<RoomSettings> settings,
|
||||
QSharedPointer<RoomState> state,
|
||||
QString room_id,
|
||||
QWidget *parent = 0);
|
||||
|
||||
RoomInfoListItem(QString room_id, mtx::responses::InvitedRoom room, QWidget *parent = 0);
|
||||
RoomInfoListItem(QString room_id, RoomInfo info, QWidget *parent = 0);
|
||||
|
||||
void updateUnreadMessageCount(int count);
|
||||
void clearUnreadMessageCount() { updateUnreadMessageCount(0); };
|
||||
void setState(QSharedPointer<RoomState> state)
|
||||
{
|
||||
state_ = state;
|
||||
update();
|
||||
}
|
||||
|
||||
QString roomId() { return roomId_; }
|
||||
bool isPressed() const { return isPressed_; }
|
||||
QSharedPointer<RoomState> state() const { return state_; }
|
||||
int unreadMessageCount() const { return unreadMsgCount_; }
|
||||
|
||||
void setAvatar(const QImage &avatar_image);
|
||||
|
@ -133,6 +120,15 @@ public:
|
|||
void setBubbleFgColor(QColor &color) { bubbleFgColor_ = color; }
|
||||
void setBubbleBgColor(QColor &color) { bubbleBgColor_ = color; }
|
||||
|
||||
void setRoomName(const QString &name) { roomName_ = name; }
|
||||
void setRoomType(bool isInvite)
|
||||
{
|
||||
if (isInvite)
|
||||
roomType_ = RoomType::Invited;
|
||||
else
|
||||
roomType_ = RoomType::Joined;
|
||||
}
|
||||
|
||||
signals:
|
||||
void clicked(const QString &room_id);
|
||||
void leaveRoom(const QString &room_id);
|
||||
|
@ -150,15 +146,7 @@ protected:
|
|||
|
||||
private:
|
||||
void init(QWidget *parent);
|
||||
QString roomName()
|
||||
{
|
||||
if (roomType_ == RoomType::Joined)
|
||||
return state_->getName();
|
||||
|
||||
return roomName_;
|
||||
}
|
||||
|
||||
QString notificationText();
|
||||
QString roomName() { return roomName_; }
|
||||
|
||||
RippleOverlay *ripple_overlay_;
|
||||
|
||||
|
@ -170,9 +158,6 @@ private:
|
|||
|
||||
RoomType roomType_ = RoomType::Joined;
|
||||
|
||||
// State information for the joined rooms.
|
||||
QSharedPointer<RoomState> state_;
|
||||
|
||||
// State information for the invited rooms.
|
||||
mtx::responses::InvitedRoom invitedRoom_;
|
||||
|
||||
|
@ -184,11 +169,8 @@ private:
|
|||
QPixmap roomAvatar_;
|
||||
|
||||
Menu *menu_;
|
||||
QAction *toggleNotifications_;
|
||||
QAction *leaveRoom_;
|
||||
|
||||
QSharedPointer<RoomSettings> roomSettings_;
|
||||
|
||||
bool isPressed_ = false;
|
||||
|
||||
int unreadMsgCount_ = 0;
|
||||
|
|
|
@ -23,14 +23,13 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Cache.h"
|
||||
#include <mtx.hpp>
|
||||
|
||||
class LeaveRoomDialog;
|
||||
class MatrixClient;
|
||||
class Cache;
|
||||
class OverlayModal;
|
||||
class RoomInfoListItem;
|
||||
class RoomSettings;
|
||||
class RoomState;
|
||||
class Sync;
|
||||
class UserSettings;
|
||||
|
@ -46,22 +45,18 @@ public:
|
|||
QWidget *parent = 0);
|
||||
|
||||
void setCache(QSharedPointer<Cache> cache) { cache_ = cache; }
|
||||
void setInitialRooms(const std::map<QString, QSharedPointer<RoomSettings>> &settings,
|
||||
const std::map<QString, QSharedPointer<RoomState>> &states);
|
||||
void sync(const std::map<QString, QSharedPointer<RoomState>> &states,
|
||||
const std::map<QString, QSharedPointer<RoomSettings>> &settings);
|
||||
void syncInvites(const std::map<std::string, mtx::responses::InvitedRoom> &rooms);
|
||||
void initialize(const QMap<QString, RoomInfo> &info);
|
||||
void sync(const std::map<QString, RoomInfo> &info);
|
||||
|
||||
void clear();
|
||||
void clear() { rooms_.clear(); };
|
||||
void updateAvatar(const QString &room_id, const QString &url);
|
||||
|
||||
void addRoom(const QSharedPointer<RoomSettings> &settings,
|
||||
const QSharedPointer<RoomState> &state,
|
||||
const QString &room_id);
|
||||
void addInvitedRoom(const QString &room_id, const mtx::responses::InvitedRoom &room);
|
||||
void addRoom(const QString &room_id, const RoomInfo &info);
|
||||
void addInvitedRoom(const QString &room_id, const RoomInfo &info);
|
||||
void removeRoom(const QString &room_id, bool reset);
|
||||
void setFilterRooms(bool filterRooms);
|
||||
void setRoomFilter(std::vector<QString> room_ids);
|
||||
void updateRoom(const QString &room_id, const RoomInfo &info);
|
||||
|
||||
signals:
|
||||
void roomChanged(const QString &room_id);
|
||||
|
|
|
@ -6,15 +6,7 @@
|
|||
#include <QWidget>
|
||||
|
||||
class Avatar;
|
||||
|
||||
struct SearchResult
|
||||
{
|
||||
QString user_id;
|
||||
QString display_name;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(SearchResult)
|
||||
Q_DECLARE_METATYPE(QVector<SearchResult>)
|
||||
struct SearchResult;
|
||||
|
||||
class PopupItem : public QWidget
|
||||
{
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
#include "emoji/PickButton.h"
|
||||
|
||||
class RoomState;
|
||||
class Cache;
|
||||
|
||||
namespace dialogs {
|
||||
class PreviewUploadOverlay;
|
||||
|
@ -131,12 +131,12 @@ public:
|
|||
|
||||
QColor borderColor() const { return borderColor_; }
|
||||
void setBorderColor(QColor &color) { borderColor_ = color; }
|
||||
void setCache(QSharedPointer<Cache> cache) { cache_ = cache; }
|
||||
|
||||
public slots:
|
||||
void openFileSelection();
|
||||
void hideUploadSpinner();
|
||||
void focusLineEdit() { input_->setFocus(); }
|
||||
void setRoomState(QSharedPointer<RoomState> state) { currState_ = state; }
|
||||
|
||||
private slots:
|
||||
void addSelectedEmoji(const QString &emoji);
|
||||
|
@ -172,8 +172,7 @@ private:
|
|||
FlatButton *sendMessageBtn_;
|
||||
emoji::PickButton *emojiBtn_;
|
||||
|
||||
//! State of the current room.
|
||||
QSharedPointer<RoomState> currState_;
|
||||
QSharedPointer<Cache> cache_;
|
||||
|
||||
QColor borderColor_;
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ descriptiveTime(const QDateTime &then);
|
|||
//! Generate a message description from the event to be displayed
|
||||
//! in the RoomList.
|
||||
DescInfo
|
||||
getMessageDescription(const TimelineEvent &event, const QString &localUser);
|
||||
getMessageDescription(const TimelineEvent &event, const QString &localUser, const QString &room_id);
|
||||
|
||||
//! Get the first character of a string, taking into account that
|
||||
//! surrogate pairs might be in use.
|
||||
|
|
|
@ -16,7 +16,10 @@ class ReceiptItem : public QWidget
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ReceiptItem(QWidget *parent, const QString &user_id, uint64_t timestamp);
|
||||
ReceiptItem(QWidget *parent,
|
||||
const QString &user_id,
|
||||
uint64_t timestamp,
|
||||
const QString &room_id);
|
||||
|
||||
private:
|
||||
QString dateFormat(const QDateTime &then) const;
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
#include <QStyleOption>
|
||||
|
||||
#include "AvatarProvider.h"
|
||||
#include "Cache.h"
|
||||
#include "ChatPage.h"
|
||||
#include "RoomInfoListItem.h"
|
||||
#include "TimelineViewManager.h"
|
||||
#include "Utils.h"
|
||||
|
||||
class ImageItem;
|
||||
|
@ -43,12 +43,15 @@ class TimelineItem : public QWidget
|
|||
public:
|
||||
TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice> &e,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent = 0);
|
||||
TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text> &e,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent = 0);
|
||||
TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote> &e,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent = 0);
|
||||
|
||||
// For local messages.
|
||||
|
@ -57,28 +60,49 @@ public:
|
|||
const QString &userid,
|
||||
QString body,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent = 0);
|
||||
// m.image
|
||||
TimelineItem(ImageItem *item, const QString &userid, bool withSender, QWidget *parent = 0);
|
||||
TimelineItem(FileItem *item, const QString &userid, bool withSender, QWidget *parent = 0);
|
||||
TimelineItem(AudioItem *item, const QString &userid, bool withSender, QWidget *parent = 0);
|
||||
TimelineItem(VideoItem *item, const QString &userid, bool withSender, QWidget *parent = 0);
|
||||
TimelineItem(ImageItem *item,
|
||||
const QString &userid,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent = 0);
|
||||
TimelineItem(FileItem *item,
|
||||
const QString &userid,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent = 0);
|
||||
TimelineItem(AudioItem *item,
|
||||
const QString &userid,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent = 0);
|
||||
TimelineItem(VideoItem *item,
|
||||
const QString &userid,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent = 0);
|
||||
|
||||
TimelineItem(ImageItem *img,
|
||||
const mtx::events::RoomEvent<mtx::events::msg::Image> &e,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent);
|
||||
TimelineItem(FileItem *file,
|
||||
const mtx::events::RoomEvent<mtx::events::msg::File> &e,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent);
|
||||
TimelineItem(AudioItem *audio,
|
||||
const mtx::events::RoomEvent<mtx::events::msg::Audio> &e,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent);
|
||||
TimelineItem(VideoItem *video,
|
||||
const mtx::events::RoomEvent<mtx::events::msg::Video> &e,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent);
|
||||
|
||||
void setUserAvatar(const QImage &pixmap);
|
||||
|
@ -86,7 +110,7 @@ public:
|
|||
QString eventId() const { return event_id_; }
|
||||
void setEventId(const QString &event_id) { event_id_ = event_id; }
|
||||
void markReceived();
|
||||
void setRoomId(const QString &room_id) { room_id_ = room_id; }
|
||||
void setRoomId(QString room_id) { room_id_ = room_id; }
|
||||
void sendReadReceipt() const
|
||||
{
|
||||
if (!event_id_.isEmpty())
|
||||
|
@ -159,7 +183,7 @@ TimelineItem::setupLocalWidgetLayout(Widget *widget,
|
|||
const QString &msgDescription,
|
||||
bool withSender)
|
||||
{
|
||||
auto displayName = TimelineViewManager::displayName(userid);
|
||||
auto displayName = Cache::displayName(room_id_, userid);
|
||||
auto timestamp = QDateTime::currentDateTime();
|
||||
|
||||
descriptionMsg_ = {"You",
|
||||
|
@ -183,7 +207,7 @@ TimelineItem::setupLocalWidgetLayout(Widget *widget,
|
|||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
userid, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
setupSimpleLayout();
|
||||
|
||||
|
@ -208,7 +232,7 @@ TimelineItem::setupWidgetLayout(Widget *widget,
|
|||
const auto sender = QString::fromStdString(event.sender);
|
||||
|
||||
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);
|
||||
auto displayName = TimelineViewManager::displayName(sender);
|
||||
auto displayName = Cache::displayName(room_id_, sender);
|
||||
|
||||
QSettings settings;
|
||||
descriptionMsg_ = {sender == settings.value("auth/user_id") ? "You" : displayName,
|
||||
|
@ -232,7 +256,7 @@ TimelineItem::setupWidgetLayout(Widget *widget,
|
|||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
setupSimpleLayout();
|
||||
|
||||
|
|
|
@ -259,8 +259,7 @@ TimelineView::addUserMessage(const QString &url,
|
|||
auto widget = new Widget(client_, url, trimmed, size, this);
|
||||
|
||||
TimelineItem *view_item =
|
||||
new TimelineItem(widget, local_user_, with_sender, scroll_widget_);
|
||||
view_item->setRoomId(room_id_);
|
||||
new TimelineItem(widget, local_user_, with_sender, room_id_, scroll_widget_);
|
||||
|
||||
addTimelineItem(view_item);
|
||||
|
||||
|
@ -280,8 +279,7 @@ template<class Event>
|
|||
TimelineItem *
|
||||
TimelineView::createTimelineItem(const Event &event, bool withSender)
|
||||
{
|
||||
TimelineItem *item = new TimelineItem(event, withSender, scroll_widget_);
|
||||
item->setRoomId(room_id_);
|
||||
TimelineItem *item = new TimelineItem(event, withSender, room_id_, scroll_widget_);
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -290,8 +288,7 @@ TimelineItem *
|
|||
TimelineView::createTimelineItem(const Event &event, bool withSender)
|
||||
{
|
||||
auto eventWidget = new Widget(client_, event);
|
||||
auto item = new TimelineItem(eventWidget, event, withSender, scroll_widget_);
|
||||
item->setRoomId(room_id_);
|
||||
auto item = new TimelineItem(eventWidget, event, withSender, room_id_, scroll_widget_);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
// Initialize with timeline events.
|
||||
void initialize(const mtx::responses::Rooms &rooms);
|
||||
// Empty initialization.
|
||||
void initialize(const std::vector<QString> &rooms);
|
||||
void initialize(const std::vector<std::string> &rooms);
|
||||
|
||||
void addRoom(const mtx::responses::JoinedRoom &room, const QString &room_id);
|
||||
void addRoom(const QString &room_id);
|
||||
|
@ -51,9 +51,6 @@ public:
|
|||
bool hasLoaded() const;
|
||||
|
||||
static QString chooseRandomColor();
|
||||
static QString displayName(const QString &userid);
|
||||
|
||||
static std::map<QString, QString> DISPLAY_NAMES;
|
||||
|
||||
signals:
|
||||
void clearRoomMessageCount(QString roomid);
|
||||
|
|
|
@ -16,41 +16,33 @@
|
|||
*/
|
||||
|
||||
#include "AvatarProvider.h"
|
||||
#include "Cache.h"
|
||||
#include "MatrixClient.h"
|
||||
|
||||
QSharedPointer<MatrixClient> AvatarProvider::client_;
|
||||
|
||||
std::map<QString, AvatarData> AvatarProvider::avatars_;
|
||||
QHash<QString, QImage> AvatarProvider::avatars_;
|
||||
|
||||
void
|
||||
AvatarProvider::init(QSharedPointer<MatrixClient> client)
|
||||
{
|
||||
client_ = client;
|
||||
}
|
||||
|
||||
void
|
||||
AvatarProvider::updateAvatar(const QString &uid, const QImage &img)
|
||||
{
|
||||
auto avatarData = &avatars_[uid];
|
||||
avatarData->img = img;
|
||||
}
|
||||
|
||||
void
|
||||
AvatarProvider::resolve(const QString &userId,
|
||||
AvatarProvider::resolve(const QString &room_id,
|
||||
const QString &user_id,
|
||||
QObject *receiver,
|
||||
std::function<void(QImage)> callback)
|
||||
{
|
||||
if (avatars_.find(userId) == avatars_.end())
|
||||
const auto key = QString("%1 %2").arg(room_id).arg(user_id);
|
||||
|
||||
if (!Cache::AvatarUrls.contains(key))
|
||||
return;
|
||||
|
||||
auto img = avatars_[userId].img;
|
||||
if (avatars_.contains(key)) {
|
||||
auto img = avatars_[key];
|
||||
|
||||
if (!img.isNull()) {
|
||||
callback(img);
|
||||
return;
|
||||
if (!img.isNull()) {
|
||||
callback(img);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto proxy = client_->fetchUserAvatar(avatars_[userId].url);
|
||||
auto proxy = client_->fetchUserAvatar(Cache::avatarUrl(room_id, user_id));
|
||||
|
||||
if (proxy.isNull())
|
||||
return;
|
||||
|
@ -58,18 +50,9 @@ AvatarProvider::resolve(const QString &userId,
|
|||
connect(proxy.data(),
|
||||
&DownloadMediaProxy::avatarDownloaded,
|
||||
receiver,
|
||||
[userId, proxy, callback](const QImage &img) {
|
||||
[user_id, proxy, callback, key](const QImage &img) {
|
||||
proxy->deleteLater();
|
||||
updateAvatar(userId, img);
|
||||
avatars_.insert(key, img);
|
||||
callback(img);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
AvatarProvider::setAvatarUrl(const QString &userId, const QUrl &url)
|
||||
{
|
||||
AvatarData data;
|
||||
data.url = url;
|
||||
|
||||
avatars_.emplace(userId, data);
|
||||
}
|
||||
|
|
893
src/Cache.cc
893
src/Cache.cc
File diff suppressed because it is too large
Load diff
442
src/ChatPage.cc
442
src/ChatPage.cc
|
@ -28,7 +28,6 @@
|
|||
#include "OverlayModal.h"
|
||||
#include "QuickSwitcher.h"
|
||||
#include "RoomList.h"
|
||||
#include "RoomSettings.h"
|
||||
#include "RoomState.h"
|
||||
#include "SideBarActions.h"
|
||||
#include "Splitter.h"
|
||||
|
@ -158,20 +157,21 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
|
|||
typingDisplay_->setUsers(users);
|
||||
});
|
||||
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::stopTyping);
|
||||
connect(room_list_, &RoomList::roomChanged, text_input_, [this](const QString &room_id) {
|
||||
if (roomStates_.find(room_id) != roomStates_.end())
|
||||
text_input_->setRoomState(roomStates_[room_id]);
|
||||
else
|
||||
qWarning() << "no state found for room_id" << room_id;
|
||||
});
|
||||
|
||||
connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo);
|
||||
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit);
|
||||
connect(
|
||||
room_list_, &RoomList::roomChanged, view_manager_, &TimelineViewManager::setHistoryView);
|
||||
|
||||
connect(room_list_, &RoomList::acceptInvite, client_.data(), &MatrixClient::joinRoom);
|
||||
connect(room_list_, &RoomList::declineInvite, client_.data(), &MatrixClient::leaveRoom);
|
||||
connect(room_list_, &RoomList::acceptInvite, this, [this](const QString &room_id) {
|
||||
view_manager_->addRoom(room_id);
|
||||
client_->joinRoom(room_id);
|
||||
room_list_->removeRoom(room_id, currentRoom() == room_id);
|
||||
});
|
||||
|
||||
connect(room_list_, &RoomList::declineInvite, this, [this](const QString &room_id) {
|
||||
client_->leaveRoom(room_id);
|
||||
room_list_->removeRoom(room_id, currentRoom() == room_id);
|
||||
});
|
||||
|
||||
connect(text_input_, &TextInputWidget::startedTyping, this, [this]() {
|
||||
if (!userSettings_->isTypingNotificationsEnabled())
|
||||
|
@ -329,15 +329,22 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
|
|||
|
||||
connect(client_.data(), &MatrixClient::joinedRoom, this, [this](const QString &room_id) {
|
||||
emit showNotification("You joined the room.");
|
||||
removeInvite(room_id);
|
||||
|
||||
// We remove any invites with the same room_id.
|
||||
try {
|
||||
cache_->removeInvite(room_id.toStdString());
|
||||
} catch (const lmdb::error &e) {
|
||||
emit showNotification(QString("Failed to remove invite: %1")
|
||||
.arg(QString::fromStdString(e.what())));
|
||||
}
|
||||
});
|
||||
connect(client_.data(), &MatrixClient::leftRoom, this, &ChatPage::removeRoom);
|
||||
connect(client_.data(), &MatrixClient::invitedUser, this, [this](QString, QString user) {
|
||||
emit showNotification(QString("Invited user %1").arg(user));
|
||||
});
|
||||
connect(client_.data(), &MatrixClient::roomCreated, this, [this](QString room_id) {
|
||||
emit showNotification(QString("Room %1 created").arg(room_id));
|
||||
});
|
||||
connect(client_.data(), &MatrixClient::leftRoom, this, &ChatPage::removeRoom);
|
||||
connect(client_.data(), &MatrixClient::redactionFailed, this, [this](const QString &error) {
|
||||
emit showNotification(QString("Message redaction failed: %1").arg(error));
|
||||
});
|
||||
|
@ -394,7 +401,38 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
|
|||
|
||||
AvatarProvider::init(client);
|
||||
|
||||
connect(this, &ChatPage::continueSync, this, [this](const QString &next_batch) {
|
||||
syncTimeoutTimer_->start(SYNC_RETRY_TIMEOUT);
|
||||
client_->setNextBatchToken(next_batch);
|
||||
client_->sync();
|
||||
});
|
||||
|
||||
connect(this, &ChatPage::startConsesusTimer, this, [this]() {
|
||||
consensusTimer_->start(CONSENSUS_TIMEOUT);
|
||||
showContentTimer_->start(SHOW_CONTENT_TIMEOUT);
|
||||
});
|
||||
connect(this, &ChatPage::initializeRoomList, room_list_, &RoomList::initialize);
|
||||
connect(this,
|
||||
&ChatPage::initializeViews,
|
||||
view_manager_,
|
||||
[this](const mtx::responses::Rooms &rooms) { view_manager_->initialize(rooms); });
|
||||
connect(
|
||||
this,
|
||||
&ChatPage::initializeEmptyViews,
|
||||
this,
|
||||
[this](const std::vector<std::string> &rooms) { view_manager_->initialize(rooms); });
|
||||
connect(this, &ChatPage::syncUI, this, [this](const mtx::responses::Rooms &rooms) {
|
||||
view_manager_->initialize(rooms);
|
||||
removeLeftRooms(rooms.leave);
|
||||
});
|
||||
connect(this, &ChatPage::syncRoomlist, room_list_, &RoomList::sync);
|
||||
|
||||
instance_ = this;
|
||||
|
||||
qRegisterMetaType<std::map<QString, RoomInfo>>();
|
||||
qRegisterMetaType<QMap<QString, RoomInfo>>();
|
||||
qRegisterMetaType<mtx::responses::Rooms>();
|
||||
qRegisterMetaType<std::vector<std::string>>();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -412,8 +450,6 @@ ChatPage::resetUI()
|
|||
{
|
||||
roomAvatars_.clear();
|
||||
room_list_->clear();
|
||||
roomSettings_.clear();
|
||||
roomStates_.clear();
|
||||
top_bar_->reset();
|
||||
user_info_widget_->reset();
|
||||
view_manager_->clearAll();
|
||||
|
@ -451,6 +487,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
|
|||
|
||||
cache_ = QSharedPointer<Cache>(new Cache(userid));
|
||||
room_list_->setCache(cache_);
|
||||
text_input_->setCache(cache_);
|
||||
|
||||
try {
|
||||
cache_->setup();
|
||||
|
@ -467,7 +504,6 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
|
|||
}
|
||||
} catch (const lmdb::error &e) {
|
||||
qCritical() << "Cache failure" << e.what();
|
||||
cache_->unmount();
|
||||
cache_->deleteData();
|
||||
qInfo() << "Falling back to initial sync ...";
|
||||
}
|
||||
|
@ -482,25 +518,28 @@ ChatPage::syncCompleted(const mtx::responses::Sync &response)
|
|||
{
|
||||
syncTimeoutTimer_->stop();
|
||||
|
||||
updateJoinedRooms(response.rooms.join);
|
||||
removeLeftRooms(response.rooms.leave);
|
||||
// Process ephemeral data per room.
|
||||
for (const auto &room : response.rooms.join) {
|
||||
auto room_id = QString::fromStdString(room.first);
|
||||
|
||||
const auto nextBatchToken = QString::fromStdString(response.next_batch);
|
||||
updateTypingUsers(room_id, room.second.ephemeral.typing);
|
||||
updateRoomNotificationCount(room_id,
|
||||
room.second.unread_notifications.notification_count);
|
||||
}
|
||||
|
||||
auto stateDiff = generateMembershipDifference(response.rooms.join, roomStates_);
|
||||
QtConcurrent::run(cache_.data(), &Cache::setState, nextBatchToken, stateDiff);
|
||||
QtConcurrent::run(cache_.data(), &Cache::setInvites, response.rooms.invite);
|
||||
QtConcurrent::run([this, res = std::move(response)]() {
|
||||
try {
|
||||
cache_->saveState(res);
|
||||
emit syncRoomlist(cache_->roomUpdates(res));
|
||||
} catch (const lmdb::error &e) {
|
||||
std::cout << "save cache error:" << e.what() << '\n';
|
||||
// TODO: retry sync.
|
||||
return;
|
||||
}
|
||||
|
||||
room_list_->sync(roomStates_, roomSettings_);
|
||||
room_list_->syncInvites(response.rooms.invite);
|
||||
trackInvites(response.rooms.invite);
|
||||
|
||||
view_manager_->sync(response.rooms);
|
||||
|
||||
client_->setNextBatchToken(nextBatchToken);
|
||||
client_->sync();
|
||||
|
||||
syncTimeoutTimer_->start(SYNC_RETRY_TIMEOUT);
|
||||
emit syncUI(std::move(res.rooms));
|
||||
emit continueSync(cache_->nextBatchToken());
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -508,57 +547,22 @@ ChatPage::initialSyncCompleted(const mtx::responses::Sync &response)
|
|||
{
|
||||
initialSyncTimer_->stop();
|
||||
|
||||
auto joined = response.rooms.join;
|
||||
qDebug() << "initial sync completed";
|
||||
|
||||
for (auto it = joined.cbegin(); it != joined.cend(); ++it) {
|
||||
auto roomState = QSharedPointer<RoomState>(new RoomState);
|
||||
|
||||
// Build the current state from the timeline and state events.
|
||||
roomState->updateFromEvents(it->second.state.events);
|
||||
roomState->updateFromEvents(it->second.timeline.events);
|
||||
|
||||
// Remove redundant memberships.
|
||||
roomState->removeLeaveMemberships();
|
||||
|
||||
// Resolve room name and avatar. e.g in case of one-to-one chats.
|
||||
roomState->resolveName();
|
||||
roomState->resolveAvatar();
|
||||
|
||||
const auto room_id = QString::fromStdString(it->first);
|
||||
|
||||
roomStates_.emplace(room_id, roomState);
|
||||
roomSettings_.emplace(room_id,
|
||||
QSharedPointer<RoomSettings>(new RoomSettings(room_id)));
|
||||
|
||||
for (const auto &membership : roomState->memberships) {
|
||||
updateUserDisplayName(membership.second);
|
||||
updateUserAvatarUrl(membership.second);
|
||||
QtConcurrent::run([this, res = std::move(response)]() {
|
||||
try {
|
||||
cache_->saveState(res);
|
||||
emit initializeRoomList(cache_->roomInfo());
|
||||
} catch (const lmdb::error &e) {
|
||||
qWarning() << "cache error:" << QString::fromStdString(e.what());
|
||||
emit retryInitialSync();
|
||||
return;
|
||||
}
|
||||
|
||||
QApplication::processEvents();
|
||||
}
|
||||
|
||||
QtConcurrent::run(cache_.data(),
|
||||
&Cache::setState,
|
||||
QString::fromStdString(response.next_batch),
|
||||
roomStates_);
|
||||
QtConcurrent::run(cache_.data(), &Cache::setInvites, response.rooms.invite);
|
||||
|
||||
// Create timelines
|
||||
view_manager_->initialize(response.rooms);
|
||||
|
||||
// Initialize room list.
|
||||
room_list_->setInitialRooms(roomSettings_, roomStates_);
|
||||
room_list_->syncInvites(response.rooms.invite);
|
||||
trackInvites(response.rooms.invite);
|
||||
|
||||
client_->setNextBatchToken(QString::fromStdString(response.next_batch));
|
||||
client_->sync();
|
||||
|
||||
// Add messages
|
||||
view_manager_->sync(response.rooms);
|
||||
|
||||
emit contentLoaded();
|
||||
emit initializeViews(std::move(res.rooms));
|
||||
emit continueSync(cache_->nextBatchToken());
|
||||
emit contentLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -613,19 +617,26 @@ ChatPage::updateOwnCommunitiesInfo(const QList<QString> &own_communities)
|
|||
void
|
||||
ChatPage::changeTopRoomInfo(const QString &room_id)
|
||||
{
|
||||
if (roomStates_.find(room_id) == roomStates_.end())
|
||||
return;
|
||||
try {
|
||||
auto room_info = cache_->getRoomInfo({room_id.toStdString()});
|
||||
|
||||
auto state = roomStates_[room_id];
|
||||
if (room_info.find(room_id) == room_info.end())
|
||||
return;
|
||||
|
||||
top_bar_->updateRoomName(state->getName());
|
||||
top_bar_->updateRoomTopic(state->getTopic());
|
||||
top_bar_->setRoomSettings(roomSettings_[room_id]);
|
||||
const auto name = QString::fromStdString(room_info[room_id].name);
|
||||
const auto avatar_url = QString::fromStdString(room_info[room_id].avatar_url);
|
||||
|
||||
if (roomAvatars_.find(room_id) != roomAvatars_.end())
|
||||
top_bar_->updateRoomAvatar(roomAvatars_[room_id].toImage());
|
||||
else
|
||||
top_bar_->updateRoomAvatarFromName(state->getName());
|
||||
top_bar_->updateRoomName(name);
|
||||
top_bar_->updateRoomTopic(QString::fromStdString(room_info[room_id].topic));
|
||||
|
||||
if (roomAvatars_.find(room_id) != roomAvatars_.end())
|
||||
top_bar_->updateRoomAvatar(roomAvatars_[room_id].toImage());
|
||||
else
|
||||
top_bar_->updateRoomAvatarFromName(name);
|
||||
} catch (const lmdb::error &e) {
|
||||
qWarning() << "failed to change top bar room info"
|
||||
<< QString::fromStdString(e.what());
|
||||
}
|
||||
|
||||
current_room_ = room_id;
|
||||
}
|
||||
|
@ -645,64 +656,26 @@ ChatPage::showUnreadMessageNotification(int count)
|
|||
void
|
||||
ChatPage::loadStateFromCache()
|
||||
{
|
||||
qDebug() << "Restoring state from cache";
|
||||
qDebug() << "restoring state from cache";
|
||||
|
||||
qDebug() << "Restored nextBatchToken" << cache_->nextBatchToken();
|
||||
client_->setNextBatchToken(cache_->nextBatchToken());
|
||||
QtConcurrent::run([this]() {
|
||||
try {
|
||||
cache_->populateMembers();
|
||||
|
||||
qRegisterMetaType<std::map<QString, RoomState>>();
|
||||
emit initializeRoomList(cache_->roomInfo());
|
||||
emit initializeEmptyViews(cache_->joinedRooms());
|
||||
} catch (const lmdb::error &e) {
|
||||
std::cout << "load cache error:" << e.what() << '\n';
|
||||
// TODO Clear cache and restart.
|
||||
return;
|
||||
}
|
||||
|
||||
QtConcurrent::run(cache_.data(), &Cache::states);
|
||||
// Start receiving events.
|
||||
emit continueSync(cache_->nextBatchToken());
|
||||
|
||||
connect(
|
||||
cache_.data(), &Cache::statesLoaded, this, [this](std::map<QString, RoomState> rooms) {
|
||||
qDebug() << "Cache data loaded";
|
||||
|
||||
std::vector<QString> roomKeys;
|
||||
|
||||
for (auto const &room : rooms) {
|
||||
auto roomState = QSharedPointer<RoomState>(new RoomState(room.second));
|
||||
|
||||
// Clean up and prepare state for use.
|
||||
roomState->removeLeaveMemberships();
|
||||
roomState->resolveName();
|
||||
roomState->resolveAvatar();
|
||||
|
||||
// Save the current room state.
|
||||
roomStates_.emplace(room.first, roomState);
|
||||
|
||||
// Create or restore the settings for this room.
|
||||
roomSettings_.emplace(
|
||||
room.first, QSharedPointer<RoomSettings>(new RoomSettings(room.first)));
|
||||
|
||||
// Resolve user avatars.
|
||||
for (auto const &membership : roomState->memberships) {
|
||||
updateUserDisplayName(membership.second);
|
||||
updateUserAvatarUrl(membership.second);
|
||||
}
|
||||
|
||||
roomKeys.emplace_back(room.first);
|
||||
}
|
||||
|
||||
// Initializing empty timelines.
|
||||
view_manager_->initialize(roomKeys);
|
||||
|
||||
// Initialize room list from the restored state and settings.
|
||||
room_list_->setInitialRooms(roomSettings_, roomStates_);
|
||||
|
||||
const auto invites = cache_->invites();
|
||||
room_list_->syncInvites(invites);
|
||||
trackInvites(invites);
|
||||
|
||||
// Check periodically if the timelines have been loaded.
|
||||
consensusTimer_->start(CONSENSUS_TIMEOUT);
|
||||
|
||||
// Show the content if consensus can't be achieved.
|
||||
showContentTimer_->start(SHOW_CONTENT_TIMEOUT);
|
||||
|
||||
// Start receiving events.
|
||||
client_->sync();
|
||||
});
|
||||
// Check periodically if the timelines have been loaded.
|
||||
emit startConsesusTimer();
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -734,69 +707,30 @@ ChatPage::showQuickSwitcher()
|
|||
|
||||
std::map<QString, QString> rooms;
|
||||
|
||||
for (auto const &state : roomStates_) {
|
||||
QString deambiguator =
|
||||
QString::fromStdString(state.second->canonical_alias.content.alias);
|
||||
if (deambiguator == "")
|
||||
deambiguator = state.first;
|
||||
rooms.emplace(state.second->getName() + " (" + deambiguator + ")", state.first);
|
||||
}
|
||||
|
||||
quickSwitcher_->setRoomList(rooms);
|
||||
quickSwitcherModal_->show();
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::addRoom(const QString &room_id)
|
||||
{
|
||||
if (roomStates_.find(room_id) == roomStates_.end()) {
|
||||
auto room_state = QSharedPointer<RoomState>(new RoomState);
|
||||
|
||||
roomStates_.emplace(room_id, room_state);
|
||||
roomSettings_.emplace(room_id,
|
||||
QSharedPointer<RoomSettings>(new RoomSettings(room_id)));
|
||||
|
||||
room_list_->addRoom(roomSettings_[room_id], roomStates_[room_id], room_id);
|
||||
room_list_->highlightSelectedRoom(room_id);
|
||||
|
||||
changeTopRoomInfo(room_id);
|
||||
try {
|
||||
auto info = cache_->roomInfo();
|
||||
for (auto it = info.begin(); it != info.end(); ++it)
|
||||
rooms.emplace(QString::fromStdString(it.value().name), it.key());
|
||||
quickSwitcher_->setRoomList(rooms);
|
||||
quickSwitcherModal_->show();
|
||||
} catch (const lmdb::error &e) {
|
||||
const auto err = QString::fromStdString(e.what());
|
||||
emit showNotification(QString("Failed to load room list: %1").arg(err));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::removeRoom(const QString &room_id)
|
||||
{
|
||||
roomStates_.erase(room_id);
|
||||
roomSettings_.erase(room_id);
|
||||
|
||||
try {
|
||||
cache_->removeRoom(room_id);
|
||||
cache_->removeInvite(room_id);
|
||||
cache_->removeInvite(room_id.toStdString());
|
||||
} catch (const lmdb::error &e) {
|
||||
qCritical() << "The cache couldn't be updated: " << e.what();
|
||||
// TODO: Notify the user.
|
||||
cache_->unmount();
|
||||
cache_->deleteData();
|
||||
}
|
||||
|
||||
room_list_->removeRoom(room_id, room_id == current_room_);
|
||||
roomInvites_.erase(room_id);
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::removeInvite(const QString &room_id)
|
||||
{
|
||||
try {
|
||||
cache_->removeInvite(room_id);
|
||||
} catch (const lmdb::error &e) {
|
||||
qCritical() << "The cache couldn't be updated: " << e.what();
|
||||
// TODO: Notify the user.
|
||||
cache_->unmount();
|
||||
cache_->deleteData();
|
||||
}
|
||||
|
||||
room_list_->removeRoom(room_id, room_id == current_room_);
|
||||
roomInvites_.erase(room_id);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -821,7 +755,7 @@ ChatPage::updateTypingUsers(const QString &roomid, const std::vector<std::string
|
|||
if (user == user_id)
|
||||
continue;
|
||||
|
||||
users.append(TimelineViewManager::displayName(user));
|
||||
users.append(Cache::displayName(roomid, user));
|
||||
}
|
||||
|
||||
users.sort();
|
||||
|
@ -833,135 +767,15 @@ ChatPage::updateTypingUsers(const QString &roomid, const std::vector<std::string
|
|||
typingUsers_.emplace(roomid, users);
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::updateUserAvatarUrl(const mtx::events::StateEvent<mtx::events::state::Member> &membership)
|
||||
{
|
||||
auto uid = QString::fromStdString(membership.state_key);
|
||||
auto url = QString::fromStdString(membership.content.avatar_url);
|
||||
|
||||
if (!url.isEmpty())
|
||||
AvatarProvider::setAvatarUrl(uid, url);
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::updateUserDisplayName(
|
||||
const mtx::events::StateEvent<mtx::events::state::Member> &membership)
|
||||
{
|
||||
auto displayName = QString::fromStdString(membership.content.display_name);
|
||||
auto stateKey = QString::fromStdString(membership.state_key);
|
||||
|
||||
if (!displayName.isEmpty())
|
||||
TimelineViewManager::DISPLAY_NAMES.emplace(stateKey, displayName);
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::removeLeftRooms(const std::map<std::string, mtx::responses::LeftRoom> &rooms)
|
||||
{
|
||||
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
|
||||
const auto room_id = QString::fromStdString(it->first);
|
||||
|
||||
if (roomStates_.find(room_id) != roomStates_.end())
|
||||
removeRoom(room_id);
|
||||
|
||||
if (roomInvites_.find(room_id) != roomInvites_.end())
|
||||
removeInvite(room_id);
|
||||
room_list_->removeRoom(room_id, room_id == current_room_);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::updateJoinedRooms(const std::map<std::string, mtx::responses::JoinedRoom> &rooms)
|
||||
{
|
||||
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
|
||||
const auto roomid = QString::fromStdString(it->first);
|
||||
|
||||
if (roomInvites_.find(roomid) != roomInvites_.end())
|
||||
removeInvite(roomid);
|
||||
|
||||
updateTypingUsers(roomid, it->second.ephemeral.typing);
|
||||
updateRoomNotificationCount(roomid,
|
||||
it->second.unread_notifications.notification_count);
|
||||
|
||||
if (it->second.ephemeral.receipts.size() > 0)
|
||||
QtConcurrent::run(cache_.data(),
|
||||
&Cache::updateReadReceipt,
|
||||
it->first,
|
||||
it->second.ephemeral.receipts);
|
||||
|
||||
const auto newStateEvents = it->second.state;
|
||||
const auto newTimelineEvents = it->second.timeline;
|
||||
|
||||
// Merge the new updates for rooms that we are tracking.
|
||||
if (roomStates_.find(roomid) != roomStates_.end()) {
|
||||
auto oldState = roomStates_[roomid];
|
||||
oldState->updateFromEvents(newStateEvents.events);
|
||||
oldState->updateFromEvents(newTimelineEvents.events);
|
||||
oldState->resolveName();
|
||||
oldState->resolveAvatar();
|
||||
} else {
|
||||
// Build the current state from the timeline and state events.
|
||||
auto roomState = QSharedPointer<RoomState>(new RoomState);
|
||||
roomState->updateFromEvents(newStateEvents.events);
|
||||
roomState->updateFromEvents(newTimelineEvents.events);
|
||||
|
||||
// Resolve room name and avatar. e.g in case of one-to-one chats.
|
||||
roomState->resolveName();
|
||||
roomState->resolveAvatar();
|
||||
|
||||
roomStates_.emplace(roomid, roomState);
|
||||
|
||||
roomSettings_.emplace(
|
||||
roomid, QSharedPointer<RoomSettings>(new RoomSettings(roomid)));
|
||||
|
||||
view_manager_->addRoom(it->second, roomid);
|
||||
}
|
||||
|
||||
updateUserMetadata(newStateEvents.events);
|
||||
updateUserMetadata(newTimelineEvents.events);
|
||||
|
||||
if (roomid == current_room_)
|
||||
changeTopRoomInfo(roomid);
|
||||
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
std::map<QString, QSharedPointer<RoomState>>
|
||||
ChatPage::generateMembershipDifference(
|
||||
const std::map<std::string, mtx::responses::JoinedRoom> &rooms,
|
||||
const std::map<QString, QSharedPointer<RoomState>> &states) const
|
||||
{
|
||||
std::map<QString, QSharedPointer<RoomState>> stateDiff;
|
||||
|
||||
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
|
||||
const auto room_id = QString::fromStdString(it->first);
|
||||
|
||||
if (states.find(room_id) == states.end())
|
||||
continue;
|
||||
|
||||
auto all_memberships = getMemberships(it->second.state.events);
|
||||
auto timelineMemberships = getMemberships(it->second.timeline.events);
|
||||
|
||||
// We have to process first the state events and then the timeline.
|
||||
for (auto mm = timelineMemberships.cbegin(); mm != timelineMemberships.cend(); ++mm)
|
||||
all_memberships.emplace(mm->first, mm->second);
|
||||
|
||||
auto local = QSharedPointer<RoomState>(new RoomState);
|
||||
local->aliases = states.at(room_id)->aliases;
|
||||
local->avatar = states.at(room_id)->avatar;
|
||||
local->canonical_alias = states.at(room_id)->canonical_alias;
|
||||
local->history_visibility = states.at(room_id)->history_visibility;
|
||||
local->join_rules = states.at(room_id)->join_rules;
|
||||
local->name = states.at(room_id)->name;
|
||||
local->power_levels = states.at(room_id)->power_levels;
|
||||
local->topic = states.at(room_id)->topic;
|
||||
local->memberships = all_memberships;
|
||||
|
||||
stateDiff.emplace(room_id, local);
|
||||
}
|
||||
|
||||
return stateDiff;
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::showReadReceipts(const QString &event_id)
|
||||
{
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <QProcessEnvironment>
|
||||
#include <QSettings>
|
||||
#include <QUrlQuery>
|
||||
#include <QtConcurrent>
|
||||
#include <mtx/errors.hpp>
|
||||
|
||||
#include "Deserializable.h"
|
||||
|
@ -438,16 +439,15 @@ MatrixClient::initialSync() noexcept
|
|||
return;
|
||||
}
|
||||
|
||||
auto data = reply->readAll();
|
||||
|
||||
try {
|
||||
mtx::responses::Sync response = nlohmann::json::parse(data);
|
||||
emit initialSyncCompleted(response);
|
||||
} catch (std::exception &e) {
|
||||
qWarning() << "Initial sync error:" << e.what();
|
||||
emit initialSyncFailed();
|
||||
return;
|
||||
}
|
||||
qRegisterMetaType<mtx::responses::Sync>();
|
||||
QtConcurrent::run([data = reply->readAll(), this]() {
|
||||
try {
|
||||
emit initialSyncCompleted(nlohmann::json::parse(std::move(data)));
|
||||
} catch (std::exception &e) {
|
||||
qWarning() << "Initial sync error:" << e.what();
|
||||
emit initialSyncFailed();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -730,10 +730,8 @@ MatrixClient::fetchUserAvatar(const QUrl &avatarUrl)
|
|||
{
|
||||
QList<QString> url_parts = avatarUrl.toString().split("mxc://");
|
||||
|
||||
if (url_parts.size() != 2) {
|
||||
qDebug() << "Invalid format for user avatar:" << avatarUrl.toString();
|
||||
if (url_parts.size() != 2)
|
||||
return QSharedPointer<DownloadMediaProxy>();
|
||||
}
|
||||
|
||||
QUrlQuery query;
|
||||
query.addQueryItem("width", "128");
|
||||
|
|
|
@ -22,12 +22,12 @@
|
|||
|
||||
#include <variant.hpp>
|
||||
|
||||
#include "Cache.h"
|
||||
#include "Config.h"
|
||||
#include "Menu.h"
|
||||
#include "Ripple.h"
|
||||
#include "RippleOverlay.h"
|
||||
#include "RoomInfoListItem.h"
|
||||
#include "RoomSettings.h"
|
||||
#include "Theme.h"
|
||||
#include "Utils.h"
|
||||
|
||||
|
@ -73,15 +73,20 @@ RoomInfoListItem::init(QWidget *parent)
|
|||
headingFont_ = font_;
|
||||
headingFont_.setPixelSize(conf::roomlist::fonts::heading);
|
||||
headingFont_.setWeight(60);
|
||||
|
||||
menu_ = new Menu(this);
|
||||
leaveRoom_ = new QAction(tr("Leave room"), this);
|
||||
connect(leaveRoom_, &QAction::triggered, this, [this]() { emit leaveRoom(roomId_); });
|
||||
menu_->addAction(leaveRoom_);
|
||||
}
|
||||
|
||||
RoomInfoListItem::RoomInfoListItem(QString room_id,
|
||||
mtx::responses::InvitedRoom room,
|
||||
QWidget *parent)
|
||||
RoomInfoListItem::RoomInfoListItem(QString room_id, RoomInfo info, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, roomType_{RoomType::Invited}
|
||||
, invitedRoom_{std::move(room)}
|
||||
, roomId_{std::move(room_id)}
|
||||
, roomType_{info.is_invite ? RoomType::Invited : RoomType::Joined}
|
||||
, roomId_(std::move(room_id))
|
||||
, roomName_{QString::fromStdString(std::move(info.name))}
|
||||
, isPressed_(false)
|
||||
, unreadMsgCount_(0)
|
||||
{
|
||||
init(parent);
|
||||
|
||||
|
@ -91,47 +96,8 @@ RoomInfoListItem::RoomInfoListItem(QString room_id,
|
|||
//
|
||||
// State events in invited rooms don't contain timestamp info,
|
||||
// so we can't use them for sorting.
|
||||
auto now = QDateTime::currentDateTime();
|
||||
lastMsgInfo_ = {"-", "-", "-", "-", now.addYears(10)};
|
||||
|
||||
roomAvatar_ = QString::fromStdString(invitedRoom_.avatar());
|
||||
roomName_ = QString::fromStdString(invitedRoom_.name());
|
||||
}
|
||||
|
||||
RoomInfoListItem::RoomInfoListItem(QSharedPointer<RoomSettings> settings,
|
||||
QSharedPointer<RoomState> state,
|
||||
QString room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, state_(state)
|
||||
, roomId_(room_id)
|
||||
, roomSettings_{settings}
|
||||
, isPressed_(false)
|
||||
, unreadMsgCount_(0)
|
||||
{
|
||||
init(parent);
|
||||
|
||||
menu_ = new Menu(this);
|
||||
|
||||
toggleNotifications_ = new QAction(notificationText(), this);
|
||||
connect(toggleNotifications_, &QAction::triggered, this, [this]() {
|
||||
roomSettings_->toggleNotifications();
|
||||
});
|
||||
|
||||
leaveRoom_ = new QAction(tr("Leave room"), this);
|
||||
connect(leaveRoom_, &QAction::triggered, this, [this]() { emit leaveRoom(roomId_); });
|
||||
|
||||
menu_->addAction(toggleNotifications_);
|
||||
menu_->addAction(leaveRoom_);
|
||||
}
|
||||
|
||||
QString
|
||||
RoomInfoListItem::notificationText()
|
||||
{
|
||||
if (roomSettings_.isNull() || roomSettings_->isNotificationsEnabled())
|
||||
return QString(tr("Disable notifications"));
|
||||
|
||||
return tr("Enable notifications");
|
||||
if (roomType_ == RoomType::Invited)
|
||||
lastMsgInfo_ = {"-", "-", "-", "-", QDateTime::currentDateTime().addYears(10)};
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -352,7 +318,6 @@ RoomInfoListItem::contextMenuEvent(QContextMenuEvent *event)
|
|||
if (roomType_ == RoomType::Invited)
|
||||
return;
|
||||
|
||||
toggleNotifications_->setText(notificationText());
|
||||
menu_->popup(event->globalPos());
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "OverlayModal.h"
|
||||
#include "RoomInfoListItem.h"
|
||||
#include "RoomList.h"
|
||||
#include "RoomSettings.h"
|
||||
#include "RoomState.h"
|
||||
#include "UserSettingsPage.h"
|
||||
|
||||
|
@ -74,17 +73,11 @@ RoomList::RoomList(QSharedPointer<MatrixClient> client,
|
|||
}
|
||||
|
||||
void
|
||||
RoomList::clear()
|
||||
RoomList::addRoom(const QString &room_id, const RoomInfo &info)
|
||||
{
|
||||
rooms_.clear();
|
||||
}
|
||||
auto room_item = new RoomInfoListItem(room_id, info, scrollArea_);
|
||||
room_item->setRoomName(QString::fromStdString(std::move(info.name)));
|
||||
|
||||
void
|
||||
RoomList::addRoom(const QSharedPointer<RoomSettings> &settings,
|
||||
const QSharedPointer<RoomState> &state,
|
||||
const QString &room_id)
|
||||
{
|
||||
auto room_item = new RoomInfoListItem(settings, state, room_id, scrollArea_);
|
||||
connect(room_item, &RoomInfoListItem::clicked, this, &RoomList::highlightSelectedRoom);
|
||||
connect(room_item, &RoomInfoListItem::leaveRoom, this, [](const QString &room_id) {
|
||||
MainWindow::instance()->openLeaveRoomDialog(room_id);
|
||||
|
@ -92,8 +85,8 @@ RoomList::addRoom(const QSharedPointer<RoomSettings> &settings,
|
|||
|
||||
rooms_.emplace(room_id, QSharedPointer<RoomInfoListItem>(room_item));
|
||||
|
||||
if (!state->getAvatar().toString().isEmpty())
|
||||
updateAvatar(room_id, state->getAvatar().toString());
|
||||
if (!info.avatar_url.empty())
|
||||
updateAvatar(room_id, QString::fromStdString(info.avatar_url));
|
||||
|
||||
int pos = contentsLayout_->count() - 1;
|
||||
contentsLayout_->insertWidget(pos, room_item);
|
||||
|
@ -164,20 +157,19 @@ RoomList::calculateUnreadMessageCount()
|
|||
}
|
||||
|
||||
void
|
||||
RoomList::setInitialRooms(const std::map<QString, QSharedPointer<RoomSettings>> &settings,
|
||||
const std::map<QString, QSharedPointer<RoomState>> &states)
|
||||
RoomList::initialize(const QMap<QString, RoomInfo> &info)
|
||||
{
|
||||
qDebug() << "initialize room list";
|
||||
|
||||
rooms_.clear();
|
||||
|
||||
if (settings.size() != states.size()) {
|
||||
qWarning() << "Initializing room list";
|
||||
qWarning() << "Different number of room states and room settings";
|
||||
return;
|
||||
for (auto it = info.begin(); it != info.end(); it++) {
|
||||
if (it.value().is_invite)
|
||||
addInvitedRoom(it.key(), it.value());
|
||||
else
|
||||
addRoom(it.key(), it.value());
|
||||
}
|
||||
|
||||
for (auto const &state : states)
|
||||
addRoom(settings.at(state.first), state.second, state.first);
|
||||
|
||||
if (rooms_.empty())
|
||||
return;
|
||||
|
||||
|
@ -190,21 +182,11 @@ RoomList::setInitialRooms(const std::map<QString, QSharedPointer<RoomSettings>>
|
|||
}
|
||||
|
||||
void
|
||||
RoomList::sync(const std::map<QString, QSharedPointer<RoomState>> &states,
|
||||
const std::map<QString, QSharedPointer<RoomSettings>> &settings)
|
||||
RoomList::sync(const std::map<QString, RoomInfo> &info)
|
||||
|
||||
{
|
||||
for (auto const &state : states) {
|
||||
if (!roomExists(state.first))
|
||||
addRoom(settings.at(state.first), state.second, state.first);
|
||||
|
||||
auto room = rooms_[state.first];
|
||||
auto new_avatar = state.second->getAvatar();
|
||||
|
||||
updateAvatar(state.first, new_avatar.toString());
|
||||
|
||||
room->setState(state.second);
|
||||
}
|
||||
for (const auto &room : info)
|
||||
updateRoom(room.first, room.second);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -368,14 +350,24 @@ RoomList::paintEvent(QPaintEvent *)
|
|||
}
|
||||
|
||||
void
|
||||
RoomList::syncInvites(const std::map<std::string, mtx::responses::InvitedRoom> &rooms)
|
||||
RoomList::updateRoom(const QString &room_id, const RoomInfo &info)
|
||||
{
|
||||
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
|
||||
const auto room_id = QString::fromStdString(it->first);
|
||||
qDebug() << "updateRoom" << QString::fromStdString(info.name) << room_id;
|
||||
|
||||
if (!roomExists(room_id))
|
||||
addInvitedRoom(room_id, it->second);
|
||||
if (!roomExists(room_id)) {
|
||||
if (info.is_invite)
|
||||
addInvitedRoom(room_id, info);
|
||||
else
|
||||
addRoom(room_id, info);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto room = rooms_[room_id];
|
||||
updateAvatar(room_id, QString::fromStdString(info.avatar_url));
|
||||
room->setRoomName(QString::fromStdString(info.name));
|
||||
room->setRoomType(info.is_invite);
|
||||
room->update();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -386,15 +378,16 @@ RoomList::setRoomFilter(std::vector<QString> room_ids)
|
|||
}
|
||||
|
||||
void
|
||||
RoomList::addInvitedRoom(const QString &room_id, const mtx::responses::InvitedRoom &room)
|
||||
RoomList::addInvitedRoom(const QString &room_id, const RoomInfo &info)
|
||||
{
|
||||
auto room_item = new RoomInfoListItem(room_id, room, scrollArea_);
|
||||
auto room_item = new RoomInfoListItem(room_id, info, scrollArea_);
|
||||
|
||||
connect(room_item, &RoomInfoListItem::acceptInvite, this, &RoomList::acceptInvite);
|
||||
connect(room_item, &RoomInfoListItem::declineInvite, this, &RoomList::declineInvite);
|
||||
|
||||
rooms_.emplace(room_id, QSharedPointer<RoomInfoListItem>(room_item));
|
||||
|
||||
updateAvatar(room_id, QString::fromStdString(room.avatar()));
|
||||
updateAvatar(room_id, QString::fromStdString(info.avatar_url));
|
||||
|
||||
int pos = contentsLayout_->count() - 1;
|
||||
contentsLayout_->insertWidget(pos, room_item);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#include "Avatar.h"
|
||||
#include "AvatarProvider.h"
|
||||
#include "Cache.h"
|
||||
#include "ChatPage.h"
|
||||
#include "Config.h"
|
||||
#include "DropShadow.h"
|
||||
#include "SuggestionsPopup.hpp"
|
||||
#include "Utils.h"
|
||||
#include "timeline/TimelineViewManager.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPaintEvent>
|
||||
|
@ -30,7 +31,7 @@ PopupItem::PopupItem(QWidget *parent, const QString &user_id)
|
|||
QFont font;
|
||||
font.setPixelSize(conf::popup::font);
|
||||
|
||||
auto displayName = TimelineViewManager::displayName(user_id);
|
||||
auto displayName = Cache::displayName(ChatPage::instance()->currentRoom(), user_id);
|
||||
|
||||
avatar_->setSize(conf::popup::avatar);
|
||||
avatar_->setLetter(utils::firstChar(displayName));
|
||||
|
@ -45,8 +46,10 @@ PopupItem::PopupItem(QWidget *parent, const QString &user_id)
|
|||
topLayout_->addWidget(avatar_);
|
||||
topLayout_->addWidget(userName_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
user_id, this, [this](const QImage &img) { avatar_->setImage(img); });
|
||||
AvatarProvider::resolve(ChatPage::instance()->currentRoom(),
|
||||
user_id,
|
||||
this,
|
||||
[this](const QImage &img) { avatar_->setImage(img); });
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -65,7 +68,7 @@ void
|
|||
PopupItem::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->buttons() != Qt::RightButton)
|
||||
emit clicked(TimelineViewManager::displayName(user_id_));
|
||||
emit clicked(Cache::displayName(ChatPage::instance()->currentRoom(), user_id_));
|
||||
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
|
@ -164,7 +167,7 @@ SuggestionsPopup::selectHoveredSuggestion()
|
|||
return;
|
||||
|
||||
const auto &widget = qobject_cast<PopupItem *>(item->widget());
|
||||
emit itemSelected(TimelineViewManager::displayName(widget->user()));
|
||||
emit itemSelected(Cache::displayName(ChatPage::instance()->currentRoom(), widget->user()));
|
||||
|
||||
resetSelection();
|
||||
}
|
||||
|
|
|
@ -31,8 +31,9 @@
|
|||
|
||||
#include <variant.hpp>
|
||||
|
||||
#include "Cache.h"
|
||||
#include "ChatPage.h"
|
||||
#include "Config.h"
|
||||
#include "RoomState.h"
|
||||
#include "TextInputWidget.h"
|
||||
#include "Utils.h"
|
||||
|
||||
|
@ -40,7 +41,6 @@ static constexpr size_t INPUT_HISTORY_SIZE = 127;
|
|||
static constexpr int MAX_TEXTINPUT_HEIGHT = 120;
|
||||
static constexpr int InputHeight = 26;
|
||||
static constexpr int ButtonHeight = 24;
|
||||
static constexpr int MaxPopupItems = 5;
|
||||
|
||||
FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
||||
: QTextEdit{parent}
|
||||
|
@ -454,49 +454,16 @@ TextInputWidget::TextInputWidget(QWidget *parent)
|
|||
input_->setFixedHeight(textInputHeight);
|
||||
});
|
||||
connect(input_, &FilteredTextEdit::showSuggestions, this, [this](const QString &q) {
|
||||
if (q.isEmpty() || currState_.isNull())
|
||||
if (q.isEmpty() || cache_.isNull())
|
||||
return;
|
||||
|
||||
QtConcurrent::run([this, q = q.toLower().toStdString()]() {
|
||||
std::multimap<int, std::pair<std::string, std::string>> items;
|
||||
|
||||
auto get_name = [](auto membership) {
|
||||
auto name = membership.second.content.display_name;
|
||||
auto key = membership.first;
|
||||
|
||||
// Remove the leading '@' character.
|
||||
if (name.empty()) {
|
||||
key.erase(0, 1);
|
||||
name = key;
|
||||
}
|
||||
|
||||
return std::make_pair(key, name);
|
||||
};
|
||||
|
||||
for (const auto &m : currState_->memberships) {
|
||||
const auto user = get_name(m);
|
||||
const int score = utils::levenshtein_distance(q, user.second);
|
||||
|
||||
items.emplace(score, user);
|
||||
try {
|
||||
emit input_->resultsRetrieved(cache_->getAutocompleteMatches(
|
||||
ChatPage::instance()->currentRoom().toStdString(), q));
|
||||
} catch (const lmdb::error &e) {
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
|
||||
QVector<SearchResult> results;
|
||||
auto end = items.begin();
|
||||
|
||||
if (items.size() >= MaxPopupItems)
|
||||
std::advance(end, MaxPopupItems);
|
||||
else if (items.size() > 0)
|
||||
std::advance(end, items.size());
|
||||
|
||||
for (auto it = items.begin(); it != end; it++) {
|
||||
const auto user = it->second;
|
||||
|
||||
results.push_back(
|
||||
SearchResult{QString::fromStdString(user.first),
|
||||
QString::fromStdString(user.second)});
|
||||
}
|
||||
|
||||
emit input_->resultsRetrieved(results);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
20
src/Utils.cc
20
src/Utils.cc
|
@ -1,5 +1,5 @@
|
|||
#include "Cache.h"
|
||||
#include "Utils.h"
|
||||
#include "timeline/TimelineViewManager.h"
|
||||
|
||||
#include <variant.hpp>
|
||||
|
||||
|
@ -22,7 +22,9 @@ utils::descriptiveTime(const QDateTime &then)
|
|||
}
|
||||
|
||||
DescInfo
|
||||
utils::getMessageDescription(const TimelineEvent &event, const QString &localUser)
|
||||
utils::getMessageDescription(const TimelineEvent &event,
|
||||
const QString &localUser,
|
||||
const QString &room_id)
|
||||
{
|
||||
using Audio = mtx::events::RoomEvent<mtx::events::msg::Audio>;
|
||||
using Emote = mtx::events::RoomEvent<mtx::events::msg::Emote>;
|
||||
|
@ -36,7 +38,7 @@ utils::getMessageDescription(const TimelineEvent &event, const QString &localUse
|
|||
const auto msg = mpark::get<Audio>(event);
|
||||
QString sender = QString::fromStdString(msg.sender);
|
||||
|
||||
const auto username = TimelineViewManager::displayName(sender);
|
||||
const auto username = Cache::displayName(room_id, sender);
|
||||
const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
|
||||
|
||||
return DescInfo{sender == localUser ? "You" : username,
|
||||
|
@ -48,7 +50,7 @@ utils::getMessageDescription(const TimelineEvent &event, const QString &localUse
|
|||
auto msg = mpark::get<Emote>(event);
|
||||
QString sender = QString::fromStdString(msg.sender);
|
||||
|
||||
const auto username = TimelineViewManager::displayName(sender);
|
||||
const auto username = Cache::displayName(room_id, sender);
|
||||
const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
|
||||
const auto body = QString::fromStdString(msg.content.body).trimmed();
|
||||
|
||||
|
@ -61,7 +63,7 @@ utils::getMessageDescription(const TimelineEvent &event, const QString &localUse
|
|||
const auto msg = mpark::get<File>(event);
|
||||
QString sender = QString::fromStdString(msg.sender);
|
||||
|
||||
const auto username = TimelineViewManager::displayName(sender);
|
||||
const auto username = Cache::displayName(room_id, sender);
|
||||
const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
|
||||
|
||||
return DescInfo{sender == localUser ? "You" : username,
|
||||
|
@ -73,7 +75,7 @@ utils::getMessageDescription(const TimelineEvent &event, const QString &localUse
|
|||
const auto msg = mpark::get<Image>(event);
|
||||
QString sender = QString::fromStdString(msg.sender);
|
||||
|
||||
const auto username = TimelineViewManager::displayName(sender);
|
||||
const auto username = Cache::displayName(room_id, sender);
|
||||
const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
|
||||
|
||||
return DescInfo{sender == localUser ? "You" : username,
|
||||
|
@ -85,7 +87,7 @@ utils::getMessageDescription(const TimelineEvent &event, const QString &localUse
|
|||
const auto msg = mpark::get<Notice>(event);
|
||||
QString sender = QString::fromStdString(msg.sender);
|
||||
|
||||
const auto username = TimelineViewManager::displayName(sender);
|
||||
const auto username = Cache::displayName(room_id, sender);
|
||||
const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
|
||||
|
||||
return DescInfo{
|
||||
|
@ -94,7 +96,7 @@ utils::getMessageDescription(const TimelineEvent &event, const QString &localUse
|
|||
const auto msg = mpark::get<Text>(event);
|
||||
QString sender = QString::fromStdString(msg.sender);
|
||||
|
||||
const auto username = TimelineViewManager::displayName(sender);
|
||||
const auto username = Cache::displayName(room_id, sender);
|
||||
const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
|
||||
const auto body = QString::fromStdString(msg.content.body).trimmed();
|
||||
|
||||
|
@ -107,7 +109,7 @@ utils::getMessageDescription(const TimelineEvent &event, const QString &localUse
|
|||
const auto msg = mpark::get<Video>(event);
|
||||
QString sender = QString::fromStdString(msg.sender);
|
||||
|
||||
const auto username = TimelineViewManager::displayName(sender);
|
||||
const auto username = Cache::displayName(room_id, sender);
|
||||
const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts);
|
||||
|
||||
return DescInfo{sender == localUser ? "You" : username,
|
||||
|
|
|
@ -6,17 +6,21 @@
|
|||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "ChatPage.h"
|
||||
#include "Config.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include "Avatar.h"
|
||||
#include "AvatarProvider.h"
|
||||
#include "Cache.h"
|
||||
#include "dialogs/ReadReceipts.h"
|
||||
#include "timeline/TimelineViewManager.h"
|
||||
|
||||
using namespace dialogs;
|
||||
|
||||
ReceiptItem::ReceiptItem(QWidget *parent, const QString &user_id, uint64_t timestamp)
|
||||
ReceiptItem::ReceiptItem(QWidget *parent,
|
||||
const QString &user_id,
|
||||
uint64_t timestamp,
|
||||
const QString &room_id)
|
||||
: QWidget(parent)
|
||||
{
|
||||
topLayout_ = new QHBoxLayout(this);
|
||||
|
@ -29,7 +33,7 @@ ReceiptItem::ReceiptItem(QWidget *parent, const QString &user_id, uint64_t times
|
|||
QFont font;
|
||||
font.setPixelSize(conf::receipts::font);
|
||||
|
||||
auto displayName = TimelineViewManager::displayName(user_id);
|
||||
auto displayName = Cache::displayName(room_id, user_id);
|
||||
|
||||
avatar_ = new Avatar(this);
|
||||
avatar_->setSize(40);
|
||||
|
@ -51,8 +55,10 @@ ReceiptItem::ReceiptItem(QWidget *parent, const QString &user_id, uint64_t times
|
|||
topLayout_->addWidget(avatar_);
|
||||
topLayout_->addLayout(textLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
user_id, this, [this](const QImage &img) { avatar_->setImage(img); });
|
||||
AvatarProvider::resolve(ChatPage::instance()->currentRoom(),
|
||||
user_id,
|
||||
this,
|
||||
[this](const QImage &img) { avatar_->setImage(img); });
|
||||
}
|
||||
|
||||
QString
|
||||
|
@ -104,8 +110,10 @@ ReadReceipts::addUsers(const std::multimap<uint64_t, std::string, std::greater<u
|
|||
userList_->clear();
|
||||
|
||||
for (const auto &receipt : receipts) {
|
||||
auto user =
|
||||
new ReceiptItem(this, QString::fromStdString(receipt.second), receipt.first);
|
||||
auto user = new ReceiptItem(this,
|
||||
QString::fromStdString(receipt.second),
|
||||
receipt.first,
|
||||
ChatPage::instance()->currentRoom());
|
||||
auto item = new QListWidgetItem(userList_);
|
||||
|
||||
item->setSizeHint(user->minimumSizeHint());
|
||||
|
|
|
@ -99,12 +99,14 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
|
|||
const QString &userid,
|
||||
QString body,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, room_id_{room_id}
|
||||
{
|
||||
init();
|
||||
|
||||
auto displayName = TimelineViewManager::displayName(userid);
|
||||
auto displayName = Cache::displayName(room_id_, userid);
|
||||
auto timestamp = QDateTime::currentDateTime();
|
||||
|
||||
if (ty == mtx::events::MessageType::Emote) {
|
||||
|
@ -127,7 +129,7 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
|
|||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
userid, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
generateBody(body);
|
||||
setupSimpleLayout();
|
||||
|
@ -143,8 +145,10 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
|
|||
TimelineItem::TimelineItem(ImageItem *image,
|
||||
const QString &userid,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget{parent}
|
||||
, room_id_{room_id}
|
||||
{
|
||||
init();
|
||||
|
||||
|
@ -153,8 +157,13 @@ TimelineItem::TimelineItem(ImageItem *image,
|
|||
addSaveImageAction(image);
|
||||
}
|
||||
|
||||
TimelineItem::TimelineItem(FileItem *file, const QString &userid, bool withSender, QWidget *parent)
|
||||
TimelineItem::TimelineItem(FileItem *file,
|
||||
const QString &userid,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget{parent}
|
||||
, room_id_{room_id}
|
||||
{
|
||||
init();
|
||||
|
||||
|
@ -164,8 +173,10 @@ TimelineItem::TimelineItem(FileItem *file, const QString &userid, bool withSende
|
|||
TimelineItem::TimelineItem(AudioItem *audio,
|
||||
const QString &userid,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget{parent}
|
||||
, room_id_{room_id}
|
||||
{
|
||||
init();
|
||||
|
||||
|
@ -175,8 +186,10 @@ TimelineItem::TimelineItem(AudioItem *audio,
|
|||
TimelineItem::TimelineItem(VideoItem *video,
|
||||
const QString &userid,
|
||||
bool withSender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget{parent}
|
||||
, room_id_{room_id}
|
||||
{
|
||||
init();
|
||||
|
||||
|
@ -186,8 +199,10 @@ TimelineItem::TimelineItem(VideoItem *video,
|
|||
TimelineItem::TimelineItem(ImageItem *image,
|
||||
const mtx::events::RoomEvent<mtx::events::msg::Image> &event,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, room_id_{room_id}
|
||||
{
|
||||
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Image>, ImageItem>(
|
||||
image, event, " sent an image", with_sender);
|
||||
|
@ -198,8 +213,10 @@ TimelineItem::TimelineItem(ImageItem *image,
|
|||
TimelineItem::TimelineItem(FileItem *file,
|
||||
const mtx::events::RoomEvent<mtx::events::msg::File> &event,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, room_id_{room_id}
|
||||
{
|
||||
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::File>, FileItem>(
|
||||
file, event, " sent a file", with_sender);
|
||||
|
@ -208,8 +225,10 @@ TimelineItem::TimelineItem(FileItem *file,
|
|||
TimelineItem::TimelineItem(AudioItem *audio,
|
||||
const mtx::events::RoomEvent<mtx::events::msg::Audio> &event,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, room_id_{room_id}
|
||||
{
|
||||
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Audio>, AudioItem>(
|
||||
audio, event, " sent an audio clip", with_sender);
|
||||
|
@ -218,8 +237,10 @@ TimelineItem::TimelineItem(AudioItem *audio,
|
|||
TimelineItem::TimelineItem(VideoItem *video,
|
||||
const mtx::events::RoomEvent<mtx::events::msg::Video> &event,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, room_id_{room_id}
|
||||
{
|
||||
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Video>, VideoItem>(
|
||||
video, event, " sent a video clip", with_sender);
|
||||
|
@ -230,8 +251,10 @@ TimelineItem::TimelineItem(VideoItem *video,
|
|||
*/
|
||||
TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice> &event,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, room_id_{room_id}
|
||||
{
|
||||
init();
|
||||
|
||||
|
@ -240,7 +263,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
|
|||
const auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);
|
||||
auto body = QString::fromStdString(event.content.body).trimmed().toHtmlEscaped();
|
||||
|
||||
descriptionMsg_ = {TimelineViewManager::displayName(sender),
|
||||
descriptionMsg_ = {Cache::displayName(room_id_, sender),
|
||||
sender,
|
||||
" sent a notification",
|
||||
utils::descriptiveTime(timestamp),
|
||||
|
@ -253,7 +276,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
|
|||
body = "<i>" + body + "</i>";
|
||||
|
||||
if (with_sender) {
|
||||
auto displayName = TimelineViewManager::displayName(sender);
|
||||
auto displayName = Cache::displayName(room_id_, sender);
|
||||
|
||||
generateBody(displayName, body);
|
||||
setupAvatarLayout(displayName);
|
||||
|
@ -261,7 +284,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
|
|||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
generateBody(body);
|
||||
setupSimpleLayout();
|
||||
|
@ -279,8 +302,10 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
|
|||
*/
|
||||
TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote> &event,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, room_id_{room_id}
|
||||
{
|
||||
init();
|
||||
|
||||
|
@ -289,7 +314,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote>
|
|||
|
||||
auto body = QString::fromStdString(event.content.body).trimmed();
|
||||
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);
|
||||
auto displayName = TimelineViewManager::displayName(sender);
|
||||
auto displayName = Cache::displayName(room_id_, sender);
|
||||
auto emoteMsg = QString("* %1 %2").arg(displayName).arg(body);
|
||||
|
||||
descriptionMsg_ = {"", sender, emoteMsg, utils::descriptiveTime(timestamp), timestamp};
|
||||
|
@ -306,7 +331,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote>
|
|||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
generateBody(emoteMsg);
|
||||
setupSimpleLayout();
|
||||
|
@ -324,8 +349,10 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote>
|
|||
*/
|
||||
TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text> &event,
|
||||
bool with_sender,
|
||||
const QString &room_id,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, room_id_{room_id}
|
||||
{
|
||||
init();
|
||||
|
||||
|
@ -334,7 +361,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
|
|||
|
||||
auto body = QString::fromStdString(event.content.body).trimmed();
|
||||
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);
|
||||
auto displayName = TimelineViewManager::displayName(sender);
|
||||
auto displayName = Cache::displayName(room_id_, sender);
|
||||
|
||||
QSettings settings;
|
||||
descriptionMsg_ = {sender == settings.value("auth/user_id") ? "You" : displayName,
|
||||
|
@ -356,7 +383,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
|
|||
messageLayout_->addLayout(headerLayout_, 1);
|
||||
|
||||
AvatarProvider::resolve(
|
||||
sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
} else {
|
||||
generateBody(body);
|
||||
setupSimpleLayout();
|
||||
|
@ -532,7 +559,7 @@ TimelineItem::addAvatar()
|
|||
|
||||
// TODO: should be replaced with the proper event struct.
|
||||
auto userid = descriptionMsg_.userid;
|
||||
auto displayName = TimelineViewManager::displayName(userid);
|
||||
auto displayName = Cache::displayName(room_id_, userid);
|
||||
|
||||
QFontMetrics fm(usernameFont_);
|
||||
userName_ = new QLabel(this);
|
||||
|
@ -566,5 +593,6 @@ TimelineItem::addAvatar()
|
|||
messageLayout_->addWidget(checkmark_);
|
||||
messageLayout_->addWidget(timestamp_);
|
||||
|
||||
AvatarProvider::resolve(userid, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
AvatarProvider::resolve(
|
||||
room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
|
||||
}
|
||||
|
|
|
@ -477,8 +477,7 @@ TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body)
|
|||
auto with_sender = lastSender_ != local_user_;
|
||||
|
||||
TimelineItem *view_item =
|
||||
new TimelineItem(ty, local_user_, body, with_sender, scroll_widget_);
|
||||
view_item->setRoomId(room_id_);
|
||||
new TimelineItem(ty, local_user_, body, with_sender, room_id_, scroll_widget_);
|
||||
|
||||
addTimelineItem(view_item);
|
||||
|
||||
|
@ -538,7 +537,7 @@ TimelineView::notifyForLastEvent()
|
|||
void
|
||||
TimelineView::notifyForLastEvent(const TimelineEvent &event)
|
||||
{
|
||||
auto descInfo = utils::getMessageDescription(event, local_user_);
|
||||
auto descInfo = utils::getMessageDescription(event, local_user_, room_id_);
|
||||
|
||||
if (!descInfo.timestamp.isEmpty())
|
||||
emit updateLastTimelineMessage(room_id_, descInfo);
|
||||
|
|
|
@ -172,18 +172,23 @@ TimelineViewManager::initialize(const mtx::responses::Rooms &rooms)
|
|||
for (auto it = rooms.join.cbegin(); it != rooms.join.cend(); ++it) {
|
||||
addRoom(it->second, QString::fromStdString(it->first));
|
||||
}
|
||||
|
||||
sync(rooms);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewManager::initialize(const std::vector<QString> &rooms)
|
||||
TimelineViewManager::initialize(const std::vector<std::string> &rooms)
|
||||
{
|
||||
for (const auto &roomid : rooms)
|
||||
addRoom(roomid);
|
||||
addRoom(QString::fromStdString(roomid));
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewManager::addRoom(const mtx::responses::JoinedRoom &room, const QString &room_id)
|
||||
{
|
||||
if (timelineViewExists(room_id))
|
||||
return;
|
||||
|
||||
// Create a history view with the room events.
|
||||
TimelineView *view = new TimelineView(room.timeline, client_, room_id);
|
||||
views_.emplace(room_id, QSharedPointer<TimelineView>(view));
|
||||
|
@ -200,6 +205,9 @@ TimelineViewManager::addRoom(const mtx::responses::JoinedRoom &room, const QStri
|
|||
void
|
||||
TimelineViewManager::addRoom(const QString &room_id)
|
||||
{
|
||||
if (timelineViewExists(room_id))
|
||||
return;
|
||||
|
||||
// Create a history view without any events.
|
||||
TimelineView *view = new TimelineView(client_, room_id);
|
||||
views_.emplace(room_id, QSharedPointer<TimelineView>(view));
|
||||
|
@ -247,8 +255,6 @@ TimelineViewManager::setHistoryView(const QString &room_id)
|
|||
view->scrollDown();
|
||||
}
|
||||
|
||||
std::map<QString, QString> TimelineViewManager::DISPLAY_NAMES;
|
||||
|
||||
QString
|
||||
TimelineViewManager::chooseRandomColor()
|
||||
{
|
||||
|
@ -307,15 +313,6 @@ TimelineViewManager::chooseRandomColor()
|
|||
return color.name();
|
||||
}
|
||||
|
||||
QString
|
||||
TimelineViewManager::displayName(const QString &userid)
|
||||
{
|
||||
if (DISPLAY_NAMES.find(userid) != DISPLAY_NAMES.end())
|
||||
return DISPLAY_NAMES.at(userid);
|
||||
|
||||
return userid;
|
||||
}
|
||||
|
||||
bool
|
||||
TimelineViewManager::hasLoaded() const
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue