1 // SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
2 // SPDX-FileCopyrightText: 2021 Nheko Contributors
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5
6 #pragma once
7
8 #include <atomic>
9 #include <optional>
10 #include <stack>
11 #include <variant>
12
13 #include <mtx/common.hpp>
14 #include <mtx/events.hpp>
15 #include <mtx/events/encrypted.hpp>
16 #include <mtx/events/member.hpp>
17 #include <mtx/events/presence.hpp>
18 #include <mtx/secret_storage.hpp>
19
20 #include <QHBoxLayout>
21 #include <QMap>
22 #include <QPoint>
23 #include <QTimer>
24 #include <QWidget>
25
26 #include "CacheCryptoStructs.h"
27 #include "CacheStructs.h"
28 #include "notifications/Manager.h"
29
30 class OverlayModal;
31 class TimelineViewManager;
32 class UserSettings;
33 class NotificationsManager;
34 class TimelineModel;
35 class CallManager;
36
37 constexpr int CONSENSUS_TIMEOUT = 1000;
38 constexpr int SHOW_CONTENT_TIMEOUT = 3000;
39 constexpr int TYPING_REFRESH_TIMEOUT = 10000;
40
41 namespace mtx::requests {
42 struct CreateRoom;
43 }
44 namespace mtx::responses {
45 struct Notifications;
46 struct Sync;
47 struct Timeline;
48 struct Rooms;
49 }
50
51 using SecretsToDecrypt = std::map<std::string, mtx::secret_storage::AesHmacSha2EncryptedData>;
52
53 class ChatPage : public QWidget
54 {
55 Q_OBJECT
56
57 public:
58 ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent = nullptr);
59
60 // Initialize all the components of the UI.
61 void bootstrap(QString userid, QString homeserver, QString token);
62
instance()63 static ChatPage *instance() { return instance_; }
64
userSettings()65 QSharedPointer<UserSettings> userSettings() { return userSettings_; }
callManager()66 CallManager *callManager() { return callManager_; }
timelineManager()67 TimelineViewManager *timelineManager() { return view_manager_; }
68 void deleteConfigs();
69
70 void initiateLogout();
71
72 QString status() const;
73 void setStatus(const QString &status);
74
75 mtx::presence::PresenceState currentPresence() const;
76
77 // TODO(Nico): Get rid of this!
78 QString currentRoom() const;
79
80 public slots:
81 bool handleMatrixUri(const QByteArray &uri);
82 bool handleMatrixUri(const QUrl &uri);
83
84 void startChat(QString userid);
85 void leaveRoom(const QString &room_id);
86 void createRoom(const mtx::requests::CreateRoom &req);
87 void joinRoom(const QString &room);
88 void joinRoomVia(const std::string &room_id,
89 const std::vector<std::string> &via,
90 bool promptForConfirmation = true);
91
92 void inviteUser(QString userid, QString reason);
93 void kickUser(QString userid, QString reason);
94 void banUser(QString userid, QString reason);
95 void unbanUser(QString userid, QString reason);
96
97 void receivedSessionKey(const std::string &room_id, const std::string &session_id);
98 void decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescription keyDesc,
99 const SecretsToDecrypt &secrets);
100 signals:
101 void connectionLost();
102 void connectionRestored();
103
104 void notificationsRetrieved(const mtx::responses::Notifications &);
105 void highlightedNotifsRetrieved(const mtx::responses::Notifications &, const QPoint widgetPos);
106
107 void contentLoaded();
108 void closing();
109 void changeWindowTitle(const int);
110 void unreadMessages(int count);
111 void showNotification(const QString &msg);
112 void showLoginPage(const QString &msg);
113 void showUserSettingsPage();
114 void showOverlayProgressBar();
115
116 void ownProfileOk();
117 void setUserDisplayName(const QString &name);
118 void setUserAvatar(const QString &avatar);
119 void loggedOut();
120
121 void trySyncCb();
122 void tryDelayedSyncCb();
123 void tryInitialSyncCb();
124 void newSyncResponse(const mtx::responses::Sync &res, const std::string &prev_batch_token);
125 void leftRoom(const QString &room_id);
126 void newRoom(const QString &room_id);
127 void changeToRoom(const QString &room_id);
128
129 void initializeViews(const mtx::responses::Rooms &rooms);
130 void initializeEmptyViews();
131 void initializeMentions(const QMap<QString, mtx::responses::Notifications> ¬ifs);
132 void syncUI(const mtx::responses::Rooms &rooms);
133 void dropToLoginPageCb(const QString &msg);
134
135 void notifyMessage(const QString &roomid,
136 const QString &eventid,
137 const QString &roomname,
138 const QString &sender,
139 const QString &message,
140 const QImage &icon);
141
142 void retrievedPresence(const QString &statusMsg, mtx::presence::PresenceState state);
143 void themeChanged();
144 void decryptSidebarChanged();
145 void chatFocusChanged(const bool focused);
146
147 //! Signals for device verificaiton
148 void receivedDeviceVerificationAccept(const mtx::events::msg::KeyVerificationAccept &message);
149 void receivedDeviceVerificationRequest(const mtx::events::msg::KeyVerificationRequest &message,
150 std::string sender);
151 void receivedRoomDeviceVerificationRequest(
152 const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &message,
153 TimelineModel *model);
154 void receivedDeviceVerificationCancel(const mtx::events::msg::KeyVerificationCancel &message);
155 void receivedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message);
156 void receivedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message);
157 void receivedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message,
158 std::string sender);
159 void receivedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message);
160 void receivedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message);
161
162 void downloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescription keyDesc,
163 const SecretsToDecrypt &secrets);
164
165 private slots:
166 void logout();
167 void removeRoom(const QString &room_id);
168 void changeRoom(const QString &room_id);
169 void dropToLoginPage(const QString &msg);
170
171 void handleSyncResponse(const mtx::responses::Sync &res, const std::string &prev_batch_token);
172
173 private:
174 static ChatPage *instance_;
175
176 void startInitialSync();
177 void tryInitialSync();
178 void trySync();
179 void verifyOneTimeKeyCountAfterStartup();
180 void ensureOneTimeKeyCount(const std::map<std::string, uint16_t> &counts);
181 void getProfileInfo();
182 void getBackupVersion();
183
184 //! Check if the given room is currently open.
185 bool isRoomActive(const QString &room_id);
186
187 using UserID = QString;
188 using Membership = mtx::events::StateEvent<mtx::events::state::Member>;
189 using Memberships = std::map<std::string, Membership>;
190
191 void loadStateFromCache();
192 void resetUI();
193
194 template<class Collection>
195 Memberships getMemberships(const std::vector<Collection> &events) const;
196
197 //! Send desktop notification for the received messages.
198 void sendNotifications(const mtx::responses::Notifications &);
199
200 template<typename T>
201 void connectCallMessage();
202
203 QHBoxLayout *topLayout_;
204
205 TimelineViewManager *view_manager_;
206
207 QTimer connectivityTimer_;
208 std::atomic_bool isConnected_;
209
210 // Global user settings.
211 QSharedPointer<UserSettings> userSettings_;
212
213 NotificationsManager notificationsManager;
214 CallManager *callManager_;
215 };
216
217 template<class Collection>
218 std::map<std::string, mtx::events::StateEvent<mtx::events::state::Member>>
getMemberships(const std::vector<Collection> & collection)219 ChatPage::getMemberships(const std::vector<Collection> &collection) const
220 {
221 std::map<std::string, mtx::events::StateEvent<mtx::events::state::Member>> memberships;
222
223 using Member = mtx::events::StateEvent<mtx::events::state::Member>;
224
225 for (const auto &event : collection) {
226 if (auto member = std::get_if<Member>(event)) {
227 memberships.emplace(member->state_key, *member);
228 }
229 }
230
231 return memberships;
232 }
233