|
|
@ -18,26 +18,15 @@ |
|
|
|
#include <QDebug> |
|
|
|
#include <QDebug> |
|
|
|
#include <QSettings> |
|
|
|
#include <QSettings> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "AvatarProvider.h" |
|
|
|
#include "ChatPage.h" |
|
|
|
#include "ChatPage.h" |
|
|
|
|
|
|
|
#include "MainWindow.h" |
|
|
|
#include "Splitter.h" |
|
|
|
#include "Splitter.h" |
|
|
|
#include "Sync.h" |
|
|
|
#include "Sync.h" |
|
|
|
#include "Theme.h" |
|
|
|
#include "Theme.h" |
|
|
|
#include "TimelineViewManager.h" |
|
|
|
#include "TimelineViewManager.h" |
|
|
|
#include "UserInfoWidget.h" |
|
|
|
#include "UserInfoWidget.h" |
|
|
|
|
|
|
|
|
|
|
|
#include "AliasesEventContent.h" |
|
|
|
|
|
|
|
#include "AvatarEventContent.h" |
|
|
|
|
|
|
|
#include "AvatarProvider.h" |
|
|
|
|
|
|
|
#include "CanonicalAliasEventContent.h" |
|
|
|
|
|
|
|
#include "CreateEventContent.h" |
|
|
|
|
|
|
|
#include "HistoryVisibilityEventContent.h" |
|
|
|
|
|
|
|
#include "JoinRulesEventContent.h" |
|
|
|
|
|
|
|
#include "MainWindow.h" |
|
|
|
|
|
|
|
#include "MemberEventContent.h" |
|
|
|
|
|
|
|
#include "NameEventContent.h" |
|
|
|
|
|
|
|
#include "PowerLevelsEventContent.h" |
|
|
|
|
|
|
|
#include "TopicEventContent.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "StateEvent.h" |
|
|
|
#include "StateEvent.h" |
|
|
|
|
|
|
|
|
|
|
|
namespace events = matrix::events; |
|
|
|
namespace events = matrix::events; |
|
|
@ -127,9 +116,13 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent) |
|
|
|
|
|
|
|
|
|
|
|
connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo); |
|
|
|
connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo); |
|
|
|
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit); |
|
|
|
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit); |
|
|
|
connect(room_list_, &RoomList::roomChanged, view_manager_, &TimelineViewManager::setHistoryView); |
|
|
|
connect( |
|
|
|
|
|
|
|
room_list_, &RoomList::roomChanged, view_manager_, &TimelineViewManager::setHistoryView); |
|
|
|
|
|
|
|
|
|
|
|
connect(view_manager_, &TimelineViewManager::unreadMessages, this, [=](const QString &roomid, int count) { |
|
|
|
connect(view_manager_, |
|
|
|
|
|
|
|
&TimelineViewManager::unreadMessages, |
|
|
|
|
|
|
|
this, |
|
|
|
|
|
|
|
[=](const QString &roomid, int count) { |
|
|
|
if (!settingsManager_.contains(roomid)) { |
|
|
|
if (!settingsManager_.contains(roomid)) { |
|
|
|
qWarning() << "RoomId does not have settings" << roomid; |
|
|
|
qWarning() << "RoomId does not have settings" << roomid; |
|
|
|
room_list_->updateUnreadMessageCount(roomid, count); |
|
|
|
room_list_->updateUnreadMessageCount(roomid, count); |
|
|
@ -168,12 +161,18 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent) |
|
|
|
SIGNAL(syncCompleted(const SyncResponse &)), |
|
|
|
SIGNAL(syncCompleted(const SyncResponse &)), |
|
|
|
this, |
|
|
|
this, |
|
|
|
SLOT(syncCompleted(const SyncResponse &))); |
|
|
|
SLOT(syncCompleted(const SyncResponse &))); |
|
|
|
connect(client_.data(), SIGNAL(syncFailed(const QString &)), this, SLOT(syncFailed(const QString &))); |
|
|
|
connect(client_.data(), |
|
|
|
|
|
|
|
SIGNAL(syncFailed(const QString &)), |
|
|
|
|
|
|
|
this, |
|
|
|
|
|
|
|
SLOT(syncFailed(const QString &))); |
|
|
|
connect(client_.data(), |
|
|
|
connect(client_.data(), |
|
|
|
SIGNAL(getOwnProfileResponse(const QUrl &, const QString &)), |
|
|
|
SIGNAL(getOwnProfileResponse(const QUrl &, const QString &)), |
|
|
|
this, |
|
|
|
this, |
|
|
|
SLOT(updateOwnProfileInfo(const QUrl &, const QString &))); |
|
|
|
SLOT(updateOwnProfileInfo(const QUrl &, const QString &))); |
|
|
|
connect(client_.data(), SIGNAL(ownAvatarRetrieved(const QPixmap &)), this, SLOT(setOwnAvatar(const QPixmap &))); |
|
|
|
connect(client_.data(), |
|
|
|
|
|
|
|
SIGNAL(ownAvatarRetrieved(const QPixmap &)), |
|
|
|
|
|
|
|
this, |
|
|
|
|
|
|
|
SLOT(setOwnAvatar(const QPixmap &))); |
|
|
|
|
|
|
|
|
|
|
|
AvatarProvider::init(client); |
|
|
|
AvatarProvider::init(client); |
|
|
|
} |
|
|
|
} |
|
|
@ -265,10 +264,6 @@ ChatPage::updateDisplayNames(const RoomState &state) |
|
|
|
void |
|
|
|
void |
|
|
|
ChatPage::syncCompleted(const SyncResponse &response) |
|
|
|
ChatPage::syncCompleted(const SyncResponse &response) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// TODO: Catch exception
|
|
|
|
|
|
|
|
cache_->setNextBatchToken(response.nextBatch()); |
|
|
|
|
|
|
|
client_->setNextBatchToken(response.nextBatch()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto joined = response.rooms().join(); |
|
|
|
auto joined = response.rooms().join(); |
|
|
|
|
|
|
|
|
|
|
|
for (auto it = joined.constBegin(); it != joined.constEnd(); it++) { |
|
|
|
for (auto it = joined.constBegin(); it != joined.constEnd(); it++) { |
|
|
@ -278,18 +273,10 @@ ChatPage::syncCompleted(const SyncResponse &response) |
|
|
|
if (state_manager_.contains(it.key())) |
|
|
|
if (state_manager_.contains(it.key())) |
|
|
|
room_state = state_manager_[it.key()]; |
|
|
|
room_state = state_manager_[it.key()]; |
|
|
|
|
|
|
|
|
|
|
|
updateRoomState(room_state, it.value().state().events()); |
|
|
|
room_state.updateFromEvents(it.value().state().events()); |
|
|
|
updateRoomState(room_state, it.value().timeline().events()); |
|
|
|
room_state.updateFromEvents(it.value().timeline().events()); |
|
|
|
updateDisplayNames(room_state); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
updateDisplayNames(room_state); |
|
|
|
cache_->insertRoomState(it.key(), room_state); |
|
|
|
|
|
|
|
} catch (const lmdb::error &e) { |
|
|
|
|
|
|
|
qCritical() << e.what(); |
|
|
|
|
|
|
|
// Stop using the cache if an errors occurs.
|
|
|
|
|
|
|
|
// TODO: Should also be marked as invalid and be deleted.
|
|
|
|
|
|
|
|
cache_->unmount(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (state_manager_.contains(it.key())) { |
|
|
|
if (state_manager_.contains(it.key())) { |
|
|
|
// TODO: Use pointers instead of copying.
|
|
|
|
// TODO: Use pointers instead of copying.
|
|
|
@ -304,6 +291,16 @@ ChatPage::syncCompleted(const SyncResponse &response) |
|
|
|
changeTopRoomInfo(it.key()); |
|
|
|
changeTopRoomInfo(it.key()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
cache_->setState(response.nextBatch(), state_manager_); |
|
|
|
|
|
|
|
} catch (const lmdb::error &e) { |
|
|
|
|
|
|
|
qCritical() << "The cache couldn't be updated: " << e.what(); |
|
|
|
|
|
|
|
// TODO: Notify the user.
|
|
|
|
|
|
|
|
cache_->unmount(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client_->setNextBatchToken(response.nextBatch()); |
|
|
|
|
|
|
|
|
|
|
|
room_list_->sync(state_manager_); |
|
|
|
room_list_->sync(state_manager_); |
|
|
|
view_manager_->sync(response.rooms()); |
|
|
|
view_manager_->sync(response.rooms()); |
|
|
|
|
|
|
|
|
|
|
@ -313,20 +310,14 @@ ChatPage::syncCompleted(const SyncResponse &response) |
|
|
|
void |
|
|
|
void |
|
|
|
ChatPage::initialSyncCompleted(const SyncResponse &response) |
|
|
|
ChatPage::initialSyncCompleted(const SyncResponse &response) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!response.nextBatch().isEmpty()) |
|
|
|
|
|
|
|
client_->setNextBatchToken(response.nextBatch()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto joined = response.rooms().join(); |
|
|
|
auto joined = response.rooms().join(); |
|
|
|
|
|
|
|
|
|
|
|
// TODO: Catch exception
|
|
|
|
|
|
|
|
cache_->setNextBatchToken(response.nextBatch()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (auto it = joined.constBegin(); it != joined.constEnd(); it++) { |
|
|
|
for (auto it = joined.constBegin(); it != joined.constEnd(); it++) { |
|
|
|
RoomState room_state; |
|
|
|
RoomState room_state; |
|
|
|
|
|
|
|
|
|
|
|
// Build the current state from the timeline and state events.
|
|
|
|
// Build the current state from the timeline and state events.
|
|
|
|
updateRoomState(room_state, it.value().state().events()); |
|
|
|
room_state.updateFromEvents(it.value().state().events()); |
|
|
|
updateRoomState(room_state, it.value().timeline().events()); |
|
|
|
room_state.updateFromEvents(it.value().timeline().events()); |
|
|
|
|
|
|
|
|
|
|
|
// Remove redundant memberships.
|
|
|
|
// Remove redundant memberships.
|
|
|
|
room_state.removeLeaveMemberships(); |
|
|
|
room_state.removeLeaveMemberships(); |
|
|
@ -335,19 +326,11 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) |
|
|
|
room_state.resolveName(); |
|
|
|
room_state.resolveName(); |
|
|
|
room_state.resolveAvatar(); |
|
|
|
room_state.resolveAvatar(); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
cache_->insertRoomState(it.key(), room_state); |
|
|
|
|
|
|
|
} catch (const lmdb::error &e) { |
|
|
|
|
|
|
|
qCritical() << e.what(); |
|
|
|
|
|
|
|
// Stop using the cache if an errors occurs.
|
|
|
|
|
|
|
|
// TODO: Should also be marked as invalid and be deleted.
|
|
|
|
|
|
|
|
cache_->unmount(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateDisplayNames(room_state); |
|
|
|
updateDisplayNames(room_state); |
|
|
|
|
|
|
|
|
|
|
|
state_manager_.insert(it.key(), room_state); |
|
|
|
state_manager_.insert(it.key(), room_state); |
|
|
|
settingsManager_.insert(it.key(), QSharedPointer<RoomSettings>(new RoomSettings(it.key()))); |
|
|
|
settingsManager_.insert(it.key(), |
|
|
|
|
|
|
|
QSharedPointer<RoomSettings>(new RoomSettings(it.key()))); |
|
|
|
|
|
|
|
|
|
|
|
for (const auto membership : room_state.memberships) { |
|
|
|
for (const auto membership : room_state.memberships) { |
|
|
|
auto uid = membership.sender(); |
|
|
|
auto uid = membership.sender(); |
|
|
@ -358,6 +341,15 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
cache_->setState(response.nextBatch(), state_manager_); |
|
|
|
|
|
|
|
} catch (const lmdb::error &e) { |
|
|
|
|
|
|
|
qCritical() << "The cache couldn't be initialized: " << e.what(); |
|
|
|
|
|
|
|
cache_->unmount(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client_->setNextBatchToken(response.nextBatch()); |
|
|
|
|
|
|
|
|
|
|
|
// Populate timelines with messages.
|
|
|
|
// Populate timelines with messages.
|
|
|
|
view_manager_->initialize(response.rooms()); |
|
|
|
view_manager_->initialize(response.rooms()); |
|
|
|
|
|
|
|
|
|
|
@ -425,97 +417,6 @@ ChatPage::showUnreadMessageNotification(int count) |
|
|
|
emit changeWindowTitle(QString("nheko (%1)").arg(count)); |
|
|
|
emit changeWindowTitle(QString("nheko (%1)").arg(count)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
|
|
|
|
ChatPage::updateRoomState(RoomState &room_state, const QJsonArray &events) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
events::EventType ty; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto &event : events) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
ty = events::extractEventType(event.toObject()); |
|
|
|
|
|
|
|
} catch (const DeserializationException &e) { |
|
|
|
|
|
|
|
qWarning() << e.what() << event; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!events::isStateEvent(ty)) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
switch (ty) { |
|
|
|
|
|
|
|
case events::EventType::RoomAliases: { |
|
|
|
|
|
|
|
events::StateEvent<events::AliasesEventContent> aliases; |
|
|
|
|
|
|
|
aliases.deserialize(event); |
|
|
|
|
|
|
|
room_state.aliases = aliases; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case events::EventType::RoomAvatar: { |
|
|
|
|
|
|
|
events::StateEvent<events::AvatarEventContent> avatar; |
|
|
|
|
|
|
|
avatar.deserialize(event); |
|
|
|
|
|
|
|
room_state.avatar = avatar; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case events::EventType::RoomCanonicalAlias: { |
|
|
|
|
|
|
|
events::StateEvent<events::CanonicalAliasEventContent> canonical_alias; |
|
|
|
|
|
|
|
canonical_alias.deserialize(event); |
|
|
|
|
|
|
|
room_state.canonical_alias = canonical_alias; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case events::EventType::RoomCreate: { |
|
|
|
|
|
|
|
events::StateEvent<events::CreateEventContent> create; |
|
|
|
|
|
|
|
create.deserialize(event); |
|
|
|
|
|
|
|
room_state.create = create; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case events::EventType::RoomHistoryVisibility: { |
|
|
|
|
|
|
|
events::StateEvent<events::HistoryVisibilityEventContent> history_visibility; |
|
|
|
|
|
|
|
history_visibility.deserialize(event); |
|
|
|
|
|
|
|
room_state.history_visibility = history_visibility; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case events::EventType::RoomJoinRules: { |
|
|
|
|
|
|
|
events::StateEvent<events::JoinRulesEventContent> join_rules; |
|
|
|
|
|
|
|
join_rules.deserialize(event); |
|
|
|
|
|
|
|
room_state.join_rules = join_rules; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case events::EventType::RoomName: { |
|
|
|
|
|
|
|
events::StateEvent<events::NameEventContent> name; |
|
|
|
|
|
|
|
name.deserialize(event); |
|
|
|
|
|
|
|
room_state.name = name; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case events::EventType::RoomMember: { |
|
|
|
|
|
|
|
events::StateEvent<events::MemberEventContent> member; |
|
|
|
|
|
|
|
member.deserialize(event); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
room_state.memberships.insert(member.stateKey(), member); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case events::EventType::RoomPowerLevels: { |
|
|
|
|
|
|
|
events::StateEvent<events::PowerLevelsEventContent> power_levels; |
|
|
|
|
|
|
|
power_levels.deserialize(event); |
|
|
|
|
|
|
|
room_state.power_levels = power_levels; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case events::EventType::RoomTopic: { |
|
|
|
|
|
|
|
events::StateEvent<events::TopicEventContent> topic; |
|
|
|
|
|
|
|
topic.deserialize(event); |
|
|
|
|
|
|
|
room_state.topic = topic; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
default: { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (const DeserializationException &e) { |
|
|
|
|
|
|
|
qWarning() << e.what() << event; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
void |
|
|
|
ChatPage::loadStateFromCache() |
|
|
|
ChatPage::loadStateFromCache() |
|
|
|
{ |
|
|
|
{ |
|
|
@ -548,7 +449,8 @@ ChatPage::loadStateFromCache() |
|
|
|
state_manager_.insert(it.key(), room_state); |
|
|
|
state_manager_.insert(it.key(), room_state); |
|
|
|
|
|
|
|
|
|
|
|
// Create or restore the settings for this room.
|
|
|
|
// Create or restore the settings for this room.
|
|
|
|
settingsManager_.insert(it.key(), QSharedPointer<RoomSettings>(new RoomSettings(it.key()))); |
|
|
|
settingsManager_.insert(it.key(), |
|
|
|
|
|
|
|
QSharedPointer<RoomSettings>(new RoomSettings(it.key()))); |
|
|
|
|
|
|
|
|
|
|
|
// Resolve user avatars.
|
|
|
|
// Resolve user avatars.
|
|
|
|
for (const auto membership : room_state.memberships) { |
|
|
|
for (const auto membership : room_state.memberships) { |
|
|
@ -587,7 +489,10 @@ ChatPage::showQuickSwitcher() |
|
|
|
if (quickSwitcher_ == nullptr) { |
|
|
|
if (quickSwitcher_ == nullptr) { |
|
|
|
quickSwitcher_ = new QuickSwitcher(this); |
|
|
|
quickSwitcher_ = new QuickSwitcher(this); |
|
|
|
|
|
|
|
|
|
|
|
connect(quickSwitcher_, &QuickSwitcher::roomSelected, room_list_, &RoomList::highlightSelectedRoom); |
|
|
|
connect(quickSwitcher_, |
|
|
|
|
|
|
|
&QuickSwitcher::roomSelected, |
|
|
|
|
|
|
|
room_list_, |
|
|
|
|
|
|
|
&RoomList::highlightSelectedRoom); |
|
|
|
connect(quickSwitcher_, &QuickSwitcher::closing, this, [=]() { |
|
|
|
connect(quickSwitcher_, &QuickSwitcher::closing, this, [=]() { |
|
|
|
if (this->quickSwitcherModal_ != nullptr) |
|
|
|
if (this->quickSwitcherModal_ != nullptr) |
|
|
|
this->quickSwitcherModal_->fadeOut(); |
|
|
|
this->quickSwitcherModal_->fadeOut(); |
|
|
|