129beb57c9
Quoted replies now include matrix.to links for the event and the user. UI Rendering has been (slightly) improved... still very WIP. Restructured the reply structure in the code for future usability improvements.
341 lines
9.8 KiB
C++
341 lines
9.8 KiB
C++
/*
|
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <random>
|
|
|
|
#include <QApplication>
|
|
#include <QFileInfo>
|
|
#include <QSettings>
|
|
|
|
#include "Cache.h"
|
|
#include "Logging.h"
|
|
#include "Utils.h"
|
|
#include "timeline/TimelineView.h"
|
|
#include "timeline/TimelineViewManager.h"
|
|
#include "timeline/widgets/AudioItem.h"
|
|
#include "timeline/widgets/FileItem.h"
|
|
#include "timeline/widgets/ImageItem.h"
|
|
#include "timeline/widgets/VideoItem.h"
|
|
|
|
TimelineViewManager::TimelineViewManager(QWidget *parent)
|
|
: QStackedWidget(parent)
|
|
{}
|
|
|
|
void
|
|
TimelineViewManager::updateReadReceipts(const QString &room_id,
|
|
const std::vector<QString> &event_ids)
|
|
{
|
|
if (timelineViewExists(room_id)) {
|
|
auto view = views_[room_id];
|
|
if (view)
|
|
emit view->markReadEvents(event_ids);
|
|
}
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::removeTimelineEvent(const QString &room_id, const QString &event_id)
|
|
{
|
|
auto view = views_[room_id];
|
|
|
|
if (view)
|
|
view->removeEvent(event_id);
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::queueTextMessage(const QString &msg)
|
|
{
|
|
if (active_room_.isEmpty())
|
|
return;
|
|
|
|
auto room_id = active_room_;
|
|
auto view = views_[room_id];
|
|
|
|
view->addUserMessage(mtx::events::MessageType::Text, msg);
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::queueEmoteMessage(const QString &msg)
|
|
{
|
|
if (active_room_.isEmpty())
|
|
return;
|
|
|
|
auto room_id = active_room_;
|
|
auto view = views_[room_id];
|
|
|
|
view->addUserMessage(mtx::events::MessageType::Emote, msg);
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::queueReplyMessage(const QString &reply, const RelatedInfo &related)
|
|
{
|
|
if (active_room_.isEmpty())
|
|
return;
|
|
|
|
auto room_id = active_room_;
|
|
auto view = views_[room_id];
|
|
|
|
view->addUserMessage(mtx::events::MessageType::Text, reply, related);
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::queueImageMessage(const QString &roomid,
|
|
const QString &filename,
|
|
const QString &url,
|
|
const QString &mime,
|
|
uint64_t size,
|
|
const QSize &dimensions)
|
|
{
|
|
if (!timelineViewExists(roomid)) {
|
|
nhlog::ui()->warn("Cannot send m.image message to a non-managed view");
|
|
return;
|
|
}
|
|
|
|
auto view = views_[roomid];
|
|
|
|
view->addUserMessage<ImageItem, mtx::events::MessageType::Image>(
|
|
url, filename, mime, size, dimensions);
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::queueFileMessage(const QString &roomid,
|
|
const QString &filename,
|
|
const QString &url,
|
|
const QString &mime,
|
|
uint64_t size)
|
|
{
|
|
if (!timelineViewExists(roomid)) {
|
|
nhlog::ui()->warn("cannot send m.file message to a non-managed view");
|
|
return;
|
|
}
|
|
|
|
auto view = views_[roomid];
|
|
|
|
view->addUserMessage<FileItem, mtx::events::MessageType::File>(url, filename, mime, size);
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::queueAudioMessage(const QString &roomid,
|
|
const QString &filename,
|
|
const QString &url,
|
|
const QString &mime,
|
|
uint64_t size)
|
|
{
|
|
if (!timelineViewExists(roomid)) {
|
|
nhlog::ui()->warn("cannot send m.audio message to a non-managed view");
|
|
return;
|
|
}
|
|
|
|
auto view = views_[roomid];
|
|
|
|
view->addUserMessage<AudioItem, mtx::events::MessageType::Audio>(url, filename, mime, size);
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::queueVideoMessage(const QString &roomid,
|
|
const QString &filename,
|
|
const QString &url,
|
|
const QString &mime,
|
|
uint64_t size)
|
|
{
|
|
if (!timelineViewExists(roomid)) {
|
|
nhlog::ui()->warn("cannot send m.video message to a non-managed view");
|
|
return;
|
|
}
|
|
|
|
auto view = views_[roomid];
|
|
|
|
view->addUserMessage<VideoItem, mtx::events::MessageType::Video>(url, filename, mime, size);
|
|
}
|
|
|
|
void
|
|
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::initWithMessages(const std::map<QString, mtx::responses::Timeline> &msgs)
|
|
{
|
|
for (auto it = msgs.cbegin(); it != msgs.cend(); ++it) {
|
|
if (timelineViewExists(it->first))
|
|
return;
|
|
|
|
// Create a history view with the room events.
|
|
TimelineView *view = new TimelineView(it->second, it->first);
|
|
views_.emplace(it->first, QSharedPointer<TimelineView>(view));
|
|
|
|
connect(view,
|
|
&TimelineView::updateLastTimelineMessage,
|
|
this,
|
|
&TimelineViewManager::updateRoomsLastMessage);
|
|
|
|
// Add the view in the widget stack.
|
|
addWidget(view);
|
|
}
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::initialize(const std::vector<std::string> &rooms)
|
|
{
|
|
for (const auto &roomid : rooms)
|
|
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, room_id);
|
|
views_.emplace(room_id, QSharedPointer<TimelineView>(view));
|
|
|
|
connect(view,
|
|
&TimelineView::updateLastTimelineMessage,
|
|
this,
|
|
&TimelineViewManager::updateRoomsLastMessage);
|
|
|
|
// Add the view in the widget stack.
|
|
addWidget(view);
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::addRoom(const QString &room_id)
|
|
{
|
|
if (timelineViewExists(room_id))
|
|
return;
|
|
|
|
// Create a history view without any events.
|
|
TimelineView *view = new TimelineView(room_id);
|
|
views_.emplace(room_id, QSharedPointer<TimelineView>(view));
|
|
|
|
connect(view,
|
|
&TimelineView::updateLastTimelineMessage,
|
|
this,
|
|
&TimelineViewManager::updateRoomsLastMessage);
|
|
|
|
// Add the view in the widget stack.
|
|
addWidget(view);
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
|
|
{
|
|
for (const auto &room : rooms.join) {
|
|
auto roomid = QString::fromStdString(room.first);
|
|
|
|
if (!timelineViewExists(roomid)) {
|
|
nhlog::ui()->warn("ignoring event from unknown room: {}",
|
|
roomid.toStdString());
|
|
continue;
|
|
}
|
|
|
|
auto view = views_.at(roomid);
|
|
|
|
view->addEvents(room.second.timeline);
|
|
}
|
|
}
|
|
|
|
void
|
|
TimelineViewManager::setHistoryView(const QString &room_id)
|
|
{
|
|
if (!timelineViewExists(room_id)) {
|
|
nhlog::ui()->warn("room from RoomList is not present in ViewManager: {}",
|
|
room_id.toStdString());
|
|
return;
|
|
}
|
|
|
|
active_room_ = room_id;
|
|
auto view = views_.at(room_id);
|
|
|
|
setCurrentWidget(view.data());
|
|
|
|
view->fetchHistory();
|
|
view->scrollDown();
|
|
}
|
|
|
|
QString
|
|
TimelineViewManager::chooseRandomColor()
|
|
{
|
|
std::random_device random_device;
|
|
std::mt19937 engine{random_device()};
|
|
std::uniform_real_distribution<float> dist(0, 1);
|
|
|
|
float hue = dist(engine);
|
|
float saturation = 0.9;
|
|
float value = 0.7;
|
|
|
|
int hue_i = hue * 6;
|
|
|
|
float f = hue * 6 - hue_i;
|
|
|
|
float p = value * (1 - saturation);
|
|
float q = value * (1 - f * saturation);
|
|
float t = value * (1 - (1 - f) * saturation);
|
|
|
|
float r = 0;
|
|
float g = 0;
|
|
float b = 0;
|
|
|
|
if (hue_i == 0) {
|
|
r = value;
|
|
g = t;
|
|
b = p;
|
|
} else if (hue_i == 1) {
|
|
r = q;
|
|
g = value;
|
|
b = p;
|
|
} else if (hue_i == 2) {
|
|
r = p;
|
|
g = value;
|
|
b = t;
|
|
} else if (hue_i == 3) {
|
|
r = p;
|
|
g = q;
|
|
b = value;
|
|
} else if (hue_i == 4) {
|
|
r = t;
|
|
g = p;
|
|
b = value;
|
|
} else if (hue_i == 5) {
|
|
r = value;
|
|
g = p;
|
|
b = q;
|
|
}
|
|
|
|
int ri = r * 256;
|
|
int gi = g * 256;
|
|
int bi = b * 256;
|
|
|
|
QColor color(ri, gi, bi);
|
|
|
|
return color.name();
|
|
}
|
|
|
|
bool
|
|
TimelineViewManager::hasLoaded() const
|
|
{
|
|
return std::all_of(views_.cbegin(), views_.cend(), [](const auto &view) {
|
|
return view.second->hasLoaded();
|
|
});
|
|
}
|