aboutsummaryrefslogtreecommitdiff
path: root/lib/room.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/room.cpp')
-rw-r--r--lib/room.cpp107
1 files changed, 98 insertions, 9 deletions
diff --git a/lib/room.cpp b/lib/room.cpp
index a8007f20..ca29eca5 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -27,13 +27,13 @@
#include "csapi/account-data.h"
#include "csapi/message_pagination.h"
#include "csapi/room_state.h"
+#include "csapi/room_send.h"
#include "events/simplestateevents.h"
#include "events/roomavatarevent.h"
#include "events/roommemberevent.h"
#include "events/typingevent.h"
#include "events/receiptevent.h"
#include "events/redactionevent.h"
-#include "jobs/sendeventjob.h"
#include "jobs/mediathumbnailjob.h"
#include "jobs/downloadfilejob.h"
#include "jobs/postreadmarkersjob.h"
@@ -88,6 +88,7 @@ class Room::Private
Connection* connection;
Timeline timeline;
+ RoomEvents unsyncedEvents;
QHash<QString, TimelineItem::index_t> eventsIndex;
QString id;
QStringList aliases;
@@ -199,9 +200,20 @@ class Room::Private
void markMessagesAsRead(rev_iter_t upToMarker);
+ void sendEvent(RoomEventPtr&& event);
+
+ template <typename EventT, typename... ArgTs>
+ void sendEvent(ArgTs&&... eventArgs)
+ {
+ sendEvent(makeEvent<EventT>(std::forward<ArgTs>(eventArgs)...));
+ }
+
+ void deleteLocalEcho(const RoomEventPtr& remoteEcho);
+
template <typename EvT>
auto requestSetState(const QString& stateKey, const EvT& event)
{
+ // TODO: Queue up state events sending (see #133).
return connection->callApi<SetRoomStateWithKeyJob>(
id, EvT::matrixTypeId(), stateKey, event.contentJson());
}
@@ -274,6 +286,11 @@ const Room::Timeline& Room::messageEvents() const
return d->timeline;
}
+const RoomEvents& Room::pendingEvents() const
+{
+ return d->unsyncedEvents;
+}
+
QString Room::name() const
{
return d->name;
@@ -1073,32 +1090,64 @@ void Room::updateData(SyncRoomData&& data)
}
}
+void Room::Private::sendEvent(RoomEventPtr&& event)
+{
+ auto* pEvent = rawPtr(event);
+ emit q->pendingEventAboutToAdd();
+ unsyncedEvents.emplace_back(move(event));
+ emit q->pendingEventAdded();
+
+ if (pEvent->transactionId().isEmpty())
+ pEvent->setTransactionId(connection->generateTxnId());
+ // TODO: Enqueue the job rather than immediately trigger it
+ auto call = connection->sendMessage(id, *pEvent);
+ Room::connect(call, &BaseJob::success, q, [this,call,pEvent]
+ {
+ const auto comparator =
+ [pEvent] (const auto& eptr) { return rawPtr(eptr) == pEvent; };
+
+ // Find an event by the pointer saved in the lambda
+ auto it = std::find_if(unsyncedEvents.begin(), unsyncedEvents.end(),
+ comparator);
+ if (it == unsyncedEvents.end())
+ return; // The event is already synced, nothing to do
+
+ pEvent->addId(call->eventId());
+ emit q->pendingEventChanged(it - unsyncedEvents.begin());
+ });
+}
+
void Room::postMessage(const QString& type, const QString& plainText)
{
- postMessage(RoomMessageEvent { plainText, type });
+ d->sendEvent<RoomMessageEvent>(plainText, type);
}
void Room::postMessage(const QString& plainText, MessageEventType type)
{
- postMessage(RoomMessageEvent { plainText, type });
+ d->sendEvent<RoomMessageEvent>(plainText, type);
}
void Room::postHtmlMessage(const QString& plainText, const QString& htmlText,
MessageEventType type)
{
- postMessage(RoomMessageEvent { plainText, type,
- new EventContent::TextContent(htmlText, QStringLiteral("text/html")) });
-
+ d->sendEvent<RoomMessageEvent>(plainText, type,
+ new EventContent::TextContent(htmlText, QStringLiteral("text/html")));
}
-void Room::postMessage(const RoomMessageEvent& event)
+void Room::postMessage(RoomEvent* event)
{
if (usesEncryption())
{
qCCritical(MAIN) << "Room" << displayName()
<< "enforces encryption; sending encrypted messages is not supported yet";
}
- connection()->callApi<SendEventJob>(id(), event);
+ d->sendEvent(RoomEventPtr(event));
+}
+
+void Room::postMessage(const QString& matrixType,
+ const QJsonObject& eventContent)
+{
+ d->sendEvent(loadEvent<RoomEvent>(basicEventJson(matrixType, eventContent)));
}
void Room::setName(const QString& newName)
@@ -1116,6 +1165,41 @@ void Room::setTopic(const QString& newTopic)
d->requestSetState(RoomTopicEvent(newTopic));
}
+void Room::Private::deleteLocalEcho(const RoomEventPtr& remoteEcho)
+{
+ if (remoteEcho->senderId() == connection->userId())
+ {
+ auto localEchoIt =
+ std::find_if(unsyncedEvents.begin(), unsyncedEvents.end(),
+ [&remoteEcho] (const RoomEventPtr& le)
+ {
+ if (le->type() != remoteEcho->type())
+ return false;
+
+ if (!le->id().isEmpty())
+ return le->id() == remoteEcho->id();
+ if (!le->transactionId().isEmpty())
+ return le->transactionId() ==
+ remoteEcho->transactionId();
+
+ // This one is not reliable (there can be two unsynced
+ // events with the same type, sender and state key) but
+ // it's the best we have for state events.
+ if (le->isStateEvent())
+ return le->stateKey() == remoteEcho->stateKey();
+
+ // Empty id and no state key, hmm... (shrug)
+ return le->contentJson() == remoteEcho->contentJson();
+ });
+ if (localEchoIt != unsyncedEvents.end())
+ {
+ emit q->pendingEventAboutToRemove(localEchoIt - unsyncedEvents.begin());
+ unsyncedEvents.erase(localEchoIt);
+ emit q->pendingEventRemoved();
+ }
+ }
+}
+
void Room::getPreviousContent(int limit)
{
d->getPreviousContent(limit);
@@ -1399,7 +1483,12 @@ void Room::Private::addNewMessageEvents(RoomEvents&& events)
#endif
if (!normalEvents.empty())
+ {
+ for (const auto& e: normalEvents)
+ deleteLocalEcho(e);
+
emit q->aboutToAddNewMessages(normalEvents);
+ }
const auto insertedSize = moveEventsToTimeline(normalEvents, Newer);
const auto from = timeline.cend() - insertedSize;
if (insertedSize > 0)
@@ -1648,7 +1737,7 @@ void Room::processAccountDataEvent(EventPtr&& event)
// efficient; maaybe do it another day
if (!currentData || currentData->contentJson() != event->contentJson())
{
- currentData = std::move(event);
+ currentData = move(event);
qCDebug(MAIN) << "Updated account data of type"
<< currentData->matrixType();
emit accountDataChanged(currentData->matrixType());