aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/eventitem.cpp19
-rw-r--r--lib/eventitem.h146
-rw-r--r--lib/room.cpp84
-rw-r--r--lib/room.h47
4 files changed, 225 insertions, 71 deletions
diff --git a/lib/eventitem.cpp b/lib/eventitem.cpp
index e69de29b..79ef769c 100644
--- a/lib/eventitem.cpp
+++ b/lib/eventitem.cpp
@@ -0,0 +1,19 @@
+/******************************************************************************
+ * Copyright (C) 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "eventitem.h"
diff --git a/lib/eventitem.h b/lib/eventitem.h
index 9e02fab0..6bec5a7f 100644
--- a/lib/eventitem.h
+++ b/lib/eventitem.h
@@ -1,4 +1,144 @@
-#ifndef EVENTITEM_H
-#define EVENTITEM_H
+/******************************************************************************
+ * Copyright (C) 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
-#endif // EVENTITEM_H
+#pragma once
+
+#include "events/stateevent.h"
+
+#include <utility>
+
+namespace QMatrixClient
+{
+ class StateEventBase;
+
+ class EventStatus
+ {
+ Q_GADGET
+ public:
+ /** Special marks an event can assume
+ *
+ * This is used to hint at a special status of some events in UI.
+ * Most status values are mutually exclusive.
+ */
+ enum Code {
+ Normal = 0x0, //< No special designation
+ Submitted = 0x01, //< The event has just been submitted for sending
+ Departed = 0x02, //< The event has left the client
+ ReachedServer = 0x03, //< The server has received the event
+ SendingFailed = 0x04, //< The server could not receive the event
+ Redacted = 0x08, //< The event has been redacted
+ Hidden = 0x10, //< The event should be hidden
+ };
+ Q_DECLARE_FLAGS(Status, Code)
+ Q_FLAG(Status)
+ };
+
+ class EventItemBase
+ {
+ public:
+ explicit EventItemBase(RoomEventPtr&& e)
+ : evt(std::move(e))
+ {
+ Q_ASSERT(evt);
+ }
+
+ const RoomEvent* event() const { return rawPtr(evt); }
+ const RoomEvent* get() const { return event(); }
+ template <typename EventT>
+ const EventT* viewAs() const { return eventCast<const EventT>(evt); }
+ const RoomEventPtr& operator->() const { return evt; }
+ const RoomEvent& operator*() const { return *evt; }
+
+ // Used for event redaction
+ RoomEventPtr replaceEvent(RoomEventPtr&& other)
+ {
+ return std::exchange(evt, move(other));
+ }
+
+ private:
+ RoomEventPtr evt;
+ };
+
+ class TimelineItem : public EventItemBase
+ {
+ public:
+ // For compatibility with Qt containers, even though we use
+ // a std:: container now for the room timeline
+ using index_t = int;
+
+ TimelineItem(RoomEventPtr&& e, index_t number)
+ : EventItemBase(std::move(e)), idx(number)
+ { }
+
+ index_t index() const { return idx; }
+
+ private:
+ index_t idx;
+ };
+
+ template<>
+ inline const StateEventBase* EventItemBase::viewAs<StateEventBase>() const
+ {
+ return evt->isStateEvent() ? weakPtrCast<const StateEventBase>(evt)
+ : nullptr;
+ }
+
+ class PendingEventItem : public EventItemBase
+ {
+ Q_GADGET
+ public:
+ using EventItemBase::EventItemBase;
+
+ EventStatus::Code deliveryStatus() const { return _status; }
+ QDateTime lastUpdated() const { return _lastUpdated; }
+ QString annotation() const { return _annotation; }
+
+ void setDeparted() { setStatus(EventStatus::Departed); }
+ void setReachedServer(const QString& eventId)
+ {
+ setStatus(EventStatus::ReachedServer);
+ (*this)->addId(eventId);
+ }
+ void setSendingFailed(QString errorText)
+ {
+ setStatus(EventStatus::SendingFailed);
+ _annotation = std::move(errorText);
+ }
+ void resetStatus() { setStatus(EventStatus::Submitted); }
+
+ private:
+ EventStatus::Code _status = EventStatus::Submitted;
+ QDateTime _lastUpdated = QDateTime::currentDateTimeUtc();
+ QString _annotation;
+
+ void setStatus(EventStatus::Code status)
+ {
+ _status = status;
+ _lastUpdated = QDateTime::currentDateTimeUtc();
+ _annotation.clear();
+ }
+ };
+
+ inline QDebug& operator<<(QDebug& d, const TimelineItem& ti)
+ {
+ QDebugStateSaver dss(d);
+ d.nospace() << "(" << ti.index() << "|" << ti->id() << ")";
+ return d;
+ }
+}
+Q_DECLARE_METATYPE(QMatrixClient::EventStatus)
diff --git a/lib/room.cpp b/lib/room.cpp
index bfa6df68..e615060b 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -88,7 +88,7 @@ class Room::Private
Connection* connection;
Timeline timeline;
- RoomEvents unsyncedEvents;
+ PendingEvents unsyncedEvents;
QHash<QString, TimelineItem::index_t> eventsIndex;
QString id;
QStringList aliases;
@@ -207,6 +207,9 @@ class Room::Private
return sendEvent(makeEvent<EventT>(std::forward<ArgTs>(eventArgs)...));
}
+ QString doSendEvent(const RoomEvent* pEvent);
+ PendingEvents::iterator findAsPending(const RoomEvent* rawEvtPtr);
+
template <typename EvT>
auto requestSetState(const QString& stateKey, const EvT& event)
{
@@ -250,11 +253,6 @@ class Room::Private
}
};
-RoomEventPtr TimelineItem::replaceEvent(RoomEventPtr&& other)
-{
- return std::exchange(evt, move(other));
-}
-
Room::Room(Connection* connection, QString id, JoinState initialJoinState)
: QObject(connection), d(new Private(connection, id, initialJoinState))
{
@@ -283,7 +281,7 @@ const Room::Timeline& Room::messageEvents() const
return d->timeline;
}
-const RoomEvents& Room::pendingEvents() const
+const Room::PendingEvents& Room::pendingEvents() const
{
return d->unsyncedEvents;
}
@@ -1128,30 +1126,68 @@ void Room::updateData(SyncRoomData&& data)
QString Room::Private::sendEvent(RoomEventPtr&& event)
{
+ if (event->transactionId().isEmpty())
+ event->setTransactionId(connection->generateTxnId());
auto* pEvent = rawPtr(event);
emit q->pendingEventAboutToAdd();
unsyncedEvents.emplace_back(move(event));
emit q->pendingEventAdded();
+ return doSendEvent(pEvent);
+}
- if (pEvent->transactionId().isEmpty())
- pEvent->setTransactionId(connection->generateTxnId());
+QString Room::Private::doSendEvent(const RoomEvent* pEvent)
+{
+ auto txnId = pEvent->transactionId();
// 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; };
+ auto call = connection->callApi<SendMessageJob>(BackgroundRequest,
+ id, pEvent->matrixType(), txnId, pEvent->contentJson());
+ Room::connect(call, &BaseJob::started, q,
+ [this,pEvent,txnId] {
+ auto it = findAsPending(pEvent);
+ if (it == unsyncedEvents.end())
+ {
+ qWarning(EVENTS) << "Pending event for transaction" << txnId
+ << "not found - got synced so soon?";
+ return;
+ }
+ it->setDeparted();
+ emit q->pendingEventChanged(it - unsyncedEvents.begin());
+ });
+ Room::connect(call, &BaseJob::failure, q,
+ [this,pEvent,txnId,call] {
+ auto it = findAsPending(pEvent);
+ if (it == unsyncedEvents.end())
+ {
+ qCritical(EVENTS) << "Pending event for transaction" << txnId
+ << "got lost without successful sending";
+ return;
+ }
+ it->setSendingFailed(
+ call->statusCaption() % ": " % call->errorString());
+ emit q->pendingEventChanged(it - unsyncedEvents.begin());
+
+ });
+ Room::connect(call, &BaseJob::success, q,
+ [this,call,pEvent] {
+ // Find an event by the pointer saved in the lambda (the pointer
+ // may be dangling by now but we can still search by it).
+ auto it = findAsPending(pEvent);
+ if (it == unsyncedEvents.end())
+ return; // The event is already synced, nothing to do
+
+ it->setReachedServer(call->eventId());
+ emit q->pendingEventChanged(it - unsyncedEvents.begin());
+ });
+ return txnId;
+}
- // 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
+Room::PendingEvents::iterator Room::Private::findAsPending(
+ const RoomEvent* rawEvtPtr)
+{
+ const auto comp =
+ [rawEvtPtr] (const auto& pe) { return pe.event() == rawEvtPtr; };
- pEvent->addId(call->eventId());
- emit q->pendingEventChanged(it - unsyncedEvents.begin());
- });
- return pEvent->transactionId();
+ return std::find_if(unsyncedEvents.begin(), unsyncedEvents.end(), comp);
}
QString Room::postMessage(const QString& type, const QString& plainText)
@@ -1202,7 +1238,7 @@ void Room::setTopic(const QString& newTopic)
d->requestSetState(RoomTopicEvent(newTopic));
}
-bool isEchoEvent(const RoomEventPtr& le, const RoomEventPtr& re)
+bool isEchoEvent(const RoomEventPtr& le, const PendingEventItem& re)
{
if (le->type() != re->type())
return false;
diff --git a/lib/room.h b/lib/room.h
index 599b7fbf..15e2146e 100644
--- a/lib/room.h
+++ b/lib/room.h
@@ -21,6 +21,7 @@
#include "jobs/syncjob.h"
#include "events/roommessageevent.h"
#include "events/accountdataevents.h"
+#include "eventitem.h"
#include "joinstate.h"
#include <QtGui/QPixmap>
@@ -40,49 +41,6 @@ namespace QMatrixClient
class SetRoomStateWithKeyJob;
class RedactEventJob;
- class TimelineItem
- {
- public:
- // For compatibility with Qt containers, even though we use
- // a std:: container now for the room timeline
- using index_t = int;
-
- TimelineItem(RoomEventPtr&& e, index_t number)
- : evt(std::move(e)), idx(number)
- {
- Q_ASSERT(evt);
- }
-
- const RoomEvent* event() const { return rawPtr(evt); }
- const RoomEvent* get() const { return event(); }
- template <typename EventT>
- const EventT* viewAs() const { return eventCast<const EventT>(evt); }
- const RoomEventPtr& operator->() const { return evt; }
- const RoomEvent& operator*() const { return *evt; }
- index_t index() const { return idx; }
-
- // Used for event redaction
- RoomEventPtr replaceEvent(RoomEventPtr&& other);
-
- private:
- RoomEventPtr evt;
- index_t idx;
- };
-
- template<>
- inline const StateEventBase* TimelineItem::viewAs<StateEventBase>() const
- {
- return evt->isStateEvent() ? weakPtrCast<const StateEventBase>(evt)
- : nullptr;
- }
-
- inline QDebug& operator<<(QDebug& d, const TimelineItem& ti)
- {
- QDebugStateSaver dss(d);
- d.nospace() << "(" << ti.index() << "|" << ti->id() << ")";
- return d;
- }
-
class FileTransferInfo
{
Q_GADGET
@@ -139,6 +97,7 @@ namespace QMatrixClient
public:
using Timeline = std::deque<TimelineItem>;
+ using PendingEvents = std::vector<PendingEventItem>;
using rev_iter_t = Timeline::const_reverse_iterator;
using timeline_iter_t = Timeline::const_iterator;
@@ -216,7 +175,7 @@ namespace QMatrixClient
Q_INVOKABLE QString roomMembername(const QString& userId) const;
const Timeline& messageEvents() const;
- const RoomEvents& pendingEvents() const;
+ const PendingEvents& pendingEvents() const;
/**
* A convenience method returning the read marker to the before-oldest
* message