diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index 422e4cfa3103de09289848a3d776c679ef2c401b..999fbe4723f0331234477b54e9807dbb3da48f55 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -59,6 +59,7 @@ public:
         void leaveRoom(const QString &roomId);
         void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000);
         void removeTypingNotification(const QString &roomid);
+        void readEvent(const QString &room_id, const QString &event_id);
 
         QUrl getHomeServer() { return server_; };
         int transactionId() { return txn_id_; };
diff --git a/include/RoomList.h b/include/RoomList.h
index f1653a38bd9c14052c566e653a5199da53cc15c2..7a48f7bdad1f6c70c93cf85361a9d405248387ce 100644
--- a/include/RoomList.h
+++ b/include/RoomList.h
@@ -66,6 +66,7 @@ public slots:
         void closeJoinRoomDialog(bool isJoining, QString roomAlias);
         void openLeaveRoomDialog(const QString &room_id);
         void closeLeaveRoomDialog(bool leaving, const QString &room_id);
+        void clearRoomMessageCount(const QString &room_id);
 
 private:
         void calculateUnreadMessageCount();
diff --git a/include/TimelineItem.h b/include/TimelineItem.h
index d90810d5a2eeb63f0739d2f157732c1167f152a7..cd522308feda69c6a207997577f98eecd3cdc1b2 100644
--- a/include/TimelineItem.h
+++ b/include/TimelineItem.h
@@ -66,7 +66,8 @@ public:
                      QWidget *parent);
 
         void setUserAvatar(const QImage &pixmap);
-        DescInfo descriptionMessage() const { return descriptionMsg_; };
+        DescInfo descriptionMessage() const { return descriptionMsg_; }
+        QString eventId() const { return event_id_; }
 
         ~TimelineItem();
 
@@ -85,6 +86,7 @@ private:
         void setupSimpleLayout();
 
         QString replaceEmoji(const QString &body);
+        QString event_id_;
 
         DescInfo descriptionMsg_;
 
diff --git a/include/TimelineView.h b/include/TimelineView.h
index 78c31e8ee5240f81209b7b33ff8f3b361317a4cb..3f506002dbb31a9ab61f33c766c2ac68728ce544 100644
--- a/include/TimelineView.h
+++ b/include/TimelineView.h
@@ -121,15 +121,20 @@ private slots:
 
 signals:
         void updateLastTimelineMessage(const QString &user, const DescInfo &info);
+        void clearUnreadMessageCount(const QString &room_id);
 
 protected:
         void paintEvent(QPaintEvent *event) override;
+        void showEvent(QShowEvent *event) override;
+        bool event(QEvent *event) override;
 
 private:
         void init();
         void addTimelineItem(TimelineItem *item, TimelineDirection direction);
         void updateLastSender(const QString &user_id, TimelineDirection direction);
         void notifyForLastEvent();
+        void readLastEvent() const;
+        QString getLastEventId() const;
 
         // Used to determine whether or not we should prefix a message with the
         // sender's name.
diff --git a/include/TimelineViewManager.h b/include/TimelineViewManager.h
index 5bd3054f1d42f9ecd4eb175ae3edfe7d5a223391..d9fb730e5b172e6e43aa488bd5b961a796b9d933 100644
--- a/include/TimelineViewManager.h
+++ b/include/TimelineViewManager.h
@@ -58,6 +58,7 @@ public:
         static QMap<QString, QString> DISPLAY_NAMES;
 
 signals:
+        void clearRoomMessageCount(QString roomid);
         void unreadMessages(QString roomid, int count);
         void updateRoomsLastMessage(const QString &user, const DescInfo &info);
 
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 4dbda90dec7662a4588b4d9317de76ab7e957ad7..82e694a101cef0c0f6a4e716ac67f1d6e5308766 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -125,6 +125,11 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
         connect(
           room_list_, &RoomList::roomChanged, view_manager_, &TimelineViewManager::setHistoryView);
 
+        connect(view_manager_,
+                &TimelineViewManager::clearRoomMessageCount,
+                room_list_,
+                &RoomList::clearRoomMessageCount);
+
         connect(view_manager_,
                 &TimelineViewManager::unreadMessages,
                 this,
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index 5589bdc7fc1a73df6cce6c216b6fb0e7e2c412af..dcf241a66a7f9600bb18403652bd4c6d9c391b17 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -847,3 +847,31 @@ MatrixClient::removeTypingNotification(const QString &roomid)
 
         put(request, QJsonDocument(body).toJson(QJsonDocument::Compact));
 }
+
+void
+MatrixClient::readEvent(const QString &room_id, const QString &event_id)
+{
+        QUrlQuery query;
+        query.addQueryItem("access_token", token_);
+
+        QUrl endpoint(server_);
+        endpoint.setPath(clientApiUrl_ +
+                         QString("/rooms/%1/receipt/m.read/%2").arg(room_id).arg(event_id));
+        endpoint.setQuery(query);
+
+        QNetworkRequest request(QString(endpoint.toEncoded()));
+        request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
+
+        auto reply = post(request, "{}");
+
+        connect(reply, &QNetworkReply::finished, this, [this, reply]() {
+                reply->deleteLater();
+
+                int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+                if (status == 0 || status >= 400) {
+                        qWarning() << reply->errorString();
+                        return;
+                }
+        });
+}
diff --git a/src/RoomList.cc b/src/RoomList.cc
index b1d3a9ca925b21ae73dc1c2d696d76cca4ca7874..402633c368f830beed064b797ad3bcf5dfeb0a10 100644
--- a/src/RoomList.cc
+++ b/src/RoomList.cc
@@ -203,6 +203,18 @@ RoomList::sync(const QMap<QString, RoomState> &states,
         }
 }
 
+void
+RoomList::clearRoomMessageCount(const QString &room_id)
+{
+        if (!rooms_.contains(room_id))
+                return;
+
+        auto room = rooms_[room_id];
+        room->clearUnreadMessageCount();
+
+        calculateUnreadMessageCount();
+}
+
 void
 RoomList::highlightSelectedRoom(const QString &room_id)
 {
@@ -213,9 +225,7 @@ RoomList::highlightSelectedRoom(const QString &room_id)
                 return;
         }
 
-        // TODO: Send a read receipt for the last event.
-        auto room = rooms_[room_id];
-        room->clearUnreadMessageCount();
+        clearRoomMessageCount(room_id);
 
         calculateUnreadMessageCount();
 
diff --git a/src/TimelineItem.cc b/src/TimelineItem.cc
index 8c21e61dffd6b80814306858b27626e9de3ce724..263eb70d1a788bd74d1991c357cccab8e46981d0 100644
--- a/src/TimelineItem.cc
+++ b/src/TimelineItem.cc
@@ -154,6 +154,8 @@ TimelineItem::TimelineItem(ImageItem *image,
 {
         init();
 
+        event_id_ = event.eventId();
+
         auto timestamp   = QDateTime::fromMSecsSinceEpoch(event.timestamp());
         auto displayName = TimelineViewManager::displayName(event.sender());
 
@@ -193,6 +195,9 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Notice> &event,
   : QWidget(parent)
 {
         init();
+
+        event_id_ = event.eventId();
+
         descriptionMsg_ = {TimelineViewManager::displayName(event.sender()),
                            event.sender(),
                            " sent a notification",
@@ -234,6 +239,8 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Emote> &event,
 {
         init();
 
+        event_id_ = event.eventId();
+
         auto body        = event.content().body().trimmed();
         auto timestamp   = QDateTime::fromMSecsSinceEpoch(event.timestamp());
         auto displayName = TimelineViewManager::displayName(event.sender());
@@ -273,6 +280,8 @@ TimelineItem::TimelineItem(const events::MessageEvent<msgs::Text> &event,
 {
         init();
 
+        event_id_ = event.eventId();
+
         auto body        = event.content().body().trimmed();
         auto timestamp   = QDateTime::fromMSecsSinceEpoch(event.timestamp());
         auto displayName = TimelineViewManager::displayName(event.sender());
diff --git a/src/TimelineView.cc b/src/TimelineView.cc
index 267fbbffc54102c6f7ff1a7c39aa095cf6bb97ee..44f3b9d8193d6eed38177465de946a49d154518a 100644
--- a/src/TimelineView.cc
+++ b/src/TimelineView.cc
@@ -379,6 +379,9 @@ TimelineView::addEvents(const Timeline &timeline)
         if (!timeline.events().isEmpty() && scroll_layout_->count() > 1)
                 notifyForLastEvent();
 
+        if (isActiveWindow() && isVisible() && timeline.events().size() > 0)
+                readLastEvent();
+
         return message_count;
 }
 
@@ -648,3 +651,52 @@ TimelineView::paintEvent(QPaintEvent *)
         QPainter p(this);
         style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
 }
+
+void
+TimelineView::readLastEvent() const
+{
+        const auto eventId = getLastEventId();
+
+        if (!eventId.isEmpty())
+                client_->readEvent(room_id_, eventId);
+}
+
+QString
+TimelineView::getLastEventId() const
+{
+        auto index = scroll_layout_->count();
+
+        // Search backwards for the first event that has a valid event id.
+        while (index > 0) {
+                --index;
+
+                auto lastItem          = scroll_layout_->itemAt(index);
+                auto *lastTimelineItem = qobject_cast<TimelineItem *>(lastItem->widget());
+
+                if (lastTimelineItem && !lastTimelineItem->eventId().isEmpty())
+                        return lastTimelineItem->eventId();
+        }
+
+        return QString("");
+}
+
+void
+TimelineView::showEvent(QShowEvent *event)
+{
+        readLastEvent();
+
+        QWidget::showEvent(event);
+}
+
+bool
+TimelineView::event(QEvent *event)
+{
+        if (event->type() == QEvent::WindowActivate) {
+                QTimer::singleShot(1000, this, [=]() {
+                        emit clearUnreadMessageCount(room_id_);
+                        readLastEvent();
+                });
+        }
+
+        return QWidget::event(event);
+}
diff --git a/src/TimelineViewManager.cc b/src/TimelineViewManager.cc
index ec7b84465c9a5eab1736240c8fb17ba1f3836ca1..1f047d7c2e238e57bfa8fa508cf6add8bf7d297e 100644
--- a/src/TimelineViewManager.cc
+++ b/src/TimelineViewManager.cc
@@ -131,6 +131,10 @@ TimelineViewManager::addRoom(const JoinedRoom &room, const QString &room_id)
                 &TimelineView::updateLastTimelineMessage,
                 this,
                 &TimelineViewManager::updateRoomsLastMessage);
+        connect(view,
+                &TimelineView::clearUnreadMessageCount,
+                this,
+                &TimelineViewManager::clearRoomMessageCount);
 
         // Add the view in the widget stack.
         addWidget(view);
@@ -147,6 +151,10 @@ TimelineViewManager::addRoom(const QString &room_id)
                 &TimelineView::updateLastTimelineMessage,
                 this,
                 &TimelineViewManager::updateRoomsLastMessage);
+        connect(view,
+                &TimelineView::clearUnreadMessageCount,
+                this,
+                &TimelineViewManager::clearRoomMessageCount);
 
         // Add the view in the widget stack.
         addWidget(view);