diff --git a/CMakeLists.txt b/CMakeLists.txt
index c918d8343b43530a5cf4c0ac0a0b815d6c5f6d64..67a1dfb09c1074ddcdc27bbfb286669e809a9b79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -365,6 +365,7 @@ qt5_wrap_cpp(MOC_HEADERS
     src/CommunitiesList.h
     src/LoginPage.h
     src/MainWindow.h
+    src/MxcImageProvider.h
     src/InviteeItem.h
     src/QuickSwitcher.h
     src/RegisterPage.h
diff --git a/src/MxcImageProvider.cpp b/src/MxcImageProvider.cpp
index 556b019b4178ea6d8d0543bfc9255483854e9066..edf6ceb5cec2b5f04883616534706e946f339c36 100644
--- a/src/MxcImageProvider.cpp
+++ b/src/MxcImageProvider.cpp
@@ -5,7 +5,7 @@
 void
 MxcImageResponse::run()
 {
-        if (m_requestedSize.isValid()) {
+        if (m_requestedSize.isValid() && !m_encryptionInfo) {
                 QString fileName = QString("%1_%2x%3_crop")
                                      .arg(m_id)
                                      .arg(m_requestedSize.width())
@@ -65,7 +65,12 @@ MxcImageResponse::run()
                                   return;
                           }
 
-                          auto data = QByteArray(res.data(), res.size());
+                          auto temp = res;
+                          if (m_encryptionInfo)
+                                  temp = mtx::crypto::to_string(
+                                    mtx::crypto::decrypt_file(temp, m_encryptionInfo.value()));
+
+                          auto data = QByteArray(temp.data(), temp.size());
                           m_image.loadFromData(data);
                           m_image.setText("original filename",
                                           QString::fromStdString(originalFilename));
diff --git a/src/MxcImageProvider.h b/src/MxcImageProvider.h
index 19d8a74e23803b44101498502807699957343c60..2c197a13c552fe944be896c6f3c487210007c4e3 100644
--- a/src/MxcImageProvider.h
+++ b/src/MxcImageProvider.h
@@ -6,14 +6,21 @@
 #include <QImage>
 #include <QThreadPool>
 
+#include <mtx/common.hpp>
+
+#include <boost/optional.hpp>
+
 class MxcImageResponse
   : public QQuickImageResponse
   , public QRunnable
 {
 public:
-        MxcImageResponse(const QString &id, const QSize &requestedSize)
+        MxcImageResponse(const QString &id,
+                         const QSize &requestedSize,
+                         boost::optional<mtx::crypto::EncryptedFile> encryptionInfo)
           : m_id(id)
           , m_requestedSize(requestedSize)
+          , m_encryptionInfo(encryptionInfo)
         {
                 setAutoDelete(false);
         }
@@ -29,19 +36,34 @@ public:
         QString m_id, m_error;
         QSize m_requestedSize;
         QImage m_image;
+        boost::optional<mtx::crypto::EncryptedFile> m_encryptionInfo;
 };
 
-class MxcImageProvider : public QQuickAsyncImageProvider
+class MxcImageProvider
+  : public QObject
+  , public QQuickAsyncImageProvider
 {
-public:
+        Q_OBJECT
+public slots:
         QQuickImageResponse *requestImageResponse(const QString &id,
                                                   const QSize &requestedSize) override
         {
-                MxcImageResponse *response = new MxcImageResponse(id, requestedSize);
+                boost::optional<mtx::crypto::EncryptedFile> info;
+                auto temp = infos.find("mxc://" + id);
+                if (temp != infos.end())
+                        info = *temp;
+
+                MxcImageResponse *response = new MxcImageResponse(id, requestedSize, info);
                 pool.start(response);
                 return response;
         }
 
+        void addEncryptionInfo(mtx::crypto::EncryptedFile info)
+        {
+                infos.insert(QString::fromStdString(info.url), info);
+        }
+
 private:
         QThreadPool pool;
+        QHash<QString, mtx::crypto::EncryptedFile> infos;
 };
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index f606b603a87237defcc72a67b6daabb7ba5ea5ac..2c58e2f5803c742779d18e96abc22c3549748ccc 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -673,6 +673,19 @@ TimelineModel::internalAddEvents(
                         continue; // don't insert redaction into timeline
                 }
 
+                if (auto event =
+                      boost::get<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&e)) {
+                        auto temp    = decryptEvent(*event).event;
+                        auto encInfo = boost::apply_visitor(
+                          [](const auto &ev) -> boost::optional<mtx::crypto::EncryptedFile> {
+                                  return eventEncryptionInfo(ev);
+                          },
+                          temp);
+
+                        if (encInfo)
+                                emit newEncryptedImage(encInfo.value());
+                }
+
                 this->events.insert(id, e);
                 ids.push_back(id);
         }
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index f52091e6822fe69a68a2542c5c3ffc92bed5c3ce..06c64acf5475aaedd5be7630bae52013b752f9fb 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -6,6 +6,7 @@
 #include <QHash>
 #include <QSet>
 
+#include <mtx/common.hpp>
 #include <mtx/responses.hpp>
 
 #include "Cache.h"
@@ -188,6 +189,7 @@ signals:
         void nextPendingMessage();
         void newMessageToSend(mtx::events::collections::TimelineEvents event);
         void mediaCached(QString mxcUrl, QString cacheUrl);
+        void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo);
 
 private:
         DecryptionResult decryptEvent(
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index c44bcbbfd3b329465a3ab7f7d61748aaa7a6d5aa..25f72a6d2045d23560b53cfa749335d02dee82e0 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -102,9 +102,14 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
 void
 TimelineViewManager::addRoom(const QString &room_id)
 {
-        if (!models.contains(room_id))
-                models.insert(room_id,
-                              QSharedPointer<TimelineModel>(new TimelineModel(this, room_id)));
+        if (!models.contains(room_id)) {
+                QSharedPointer<TimelineModel> newRoom(new TimelineModel(this, room_id));
+                connect(newRoom.data(),
+                        &TimelineModel::newEncryptedImage,
+                        imgProvider,
+                        &MxcImageProvider::addEncryptionInfo);
+                models.insert(room_id, std::move(newRoom));
+        }
 }
 
 void