diff --git a/include/Cache.h b/include/Cache.h
index 994a6da7b585a832993a74b973d62eff3e4f4896..97133b0cfdb7bab872db01953208cb32d920eaa8 100644
--- a/include/Cache.h
+++ b/include/Cache.h
@@ -342,6 +342,8 @@ public:
 
         //! Mark a room that uses e2e encryption.
         void setEncryptedRoom(const std::string &room_id);
+        bool isRoomEncrypted(const std::string &room_id);
+
         //! Save the public keys for a device.
         void saveDeviceKeys(const std::string &device_id);
         void getDeviceKeys(const std::string &device_id);
diff --git a/include/dialogs/RoomSettings.hpp b/include/dialogs/RoomSettings.hpp
index 9a01d5c9fe154ca9fdc739b6d91523e4b1024f4f..6cab03b70ad85d17386a303d959ae960ab47bd98 100644
--- a/include/dialogs/RoomSettings.hpp
+++ b/include/dialogs/RoomSettings.hpp
@@ -5,16 +5,17 @@
 
 #include "Cache.h"
 
+class Avatar;
 class FlatButton;
-class TextField;
+class QComboBox;
 class QHBoxLayout;
-class Avatar;
-class QPixmap;
-class QLayout;
 class QLabel;
-class QComboBox;
-class TextField;
 class QLabel;
+class QLayout;
+class QPixmap;
+class TextField;
+class TextField;
+class Toggle;
 
 template<class T>
 class QSharedPointer;
@@ -84,6 +85,7 @@ public:
 
 signals:
         void closing();
+        void enableEncryptionError(const QString &msg);
 
 protected:
         void paintEvent(QPaintEvent *event) override;
@@ -98,13 +100,15 @@ private:
         void setupEditButton();
         //! Retrieve the current room information from cache.
         void retrieveRoomInfo();
+        void enableEncryption();
 
         //! Whether the user would be able to change the name or the topic of the room.
-        bool hasEditRights_ = true;
+        bool hasEditRights_  = true;
+        bool usesEncryption_ = false;
         QHBoxLayout *editLayout_;
 
         // Button section
-        FlatButton *saveBtn_;
+        FlatButton *okBtn_;
         FlatButton *cancelBtn_;
 
         FlatButton *editFieldsBtn_;
@@ -116,6 +120,7 @@ private:
         TopSection *topSection_;
 
         QComboBox *accessCombo;
+        Toggle *encryptionToggle_;
 };
 
 } // dialogs
diff --git a/src/Cache.cc b/src/Cache.cc
index 150990b7398e39aad26611525fb2d75bcf364dca..48b1fdafab7e3e144352547daef01b689d521fd4 100644
--- a/src/Cache.cc
+++ b/src/Cache.cc
@@ -60,7 +60,7 @@ constexpr auto DEVICES_DB("devices");
 //! device_id -> device keys
 constexpr auto DEVICE_KEYS_DB("device_keys");
 //! room_ids that have encryption enabled.
-// constexpr auto ENCRYPTED_ROOMS_DB("encrypted_rooms");
+constexpr auto ENCRYPTED_ROOMS_DB("encrypted_rooms");
 
 //! MegolmSessionIndex -> pickled OlmInboundGroupSession
 constexpr auto INBOUND_MEGOLM_SESSIONS_DB("inbound_megolm_sessions");
@@ -184,6 +184,30 @@ Cache::setup()
         txn.commit();
 }
 
+void
+Cache::setEncryptedRoom(const std::string &room_id)
+{
+        log::db()->info("mark room {} as encrypted", room_id);
+
+        auto txn = lmdb::txn::begin(env_);
+        auto db  = lmdb::dbi::open(txn, ENCRYPTED_ROOMS_DB, MDB_CREATE);
+        lmdb::dbi_put(txn, db, lmdb::val(room_id), lmdb::val("0"));
+        txn.commit();
+}
+
+bool
+Cache::isRoomEncrypted(const std::string &room_id)
+{
+        lmdb::val unused;
+
+        auto txn = lmdb::txn::begin(env_);
+        auto db  = lmdb::dbi::open(txn, ENCRYPTED_ROOMS_DB, MDB_CREATE);
+        auto res = lmdb::dbi_get(txn, db, lmdb::val(room_id), unused);
+        txn.commit();
+
+        return res;
+}
+
 //
 // Device Management
 //
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index a5a6a8c0fb58205e45a6368832cd604f5825820a..3f43831ce97af7b30a859d90c9f7a88826f14038 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -245,6 +245,10 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
                         return;
 
                 typingRefresher_->stop();
+
+                if (current_room_.isEmpty())
+                        return;
+
                 http::v2::client()->stop_typing(
                   current_room_.toStdString(), [](mtx::http::RequestErr err) {
                           if (err) {
diff --git a/src/dialogs/RoomSettings.cpp b/src/dialogs/RoomSettings.cpp
index 2396fc1949dc1dc0121cf2f66b9d5e17ecbcc0b2..a091c8bc118eeab831adebb3751769059b95a6c8 100644
--- a/src/dialogs/RoomSettings.cpp
+++ b/src/dialogs/RoomSettings.cpp
@@ -1,16 +1,20 @@
 #include "Avatar.h"
+#include "ChatPage.h"
 #include "Config.h"
 #include "FlatButton.h"
+#include "Logging.hpp"
 #include "MatrixClient.h"
 #include "Painter.h"
 #include "TextField.h"
 #include "Theme.h"
 #include "Utils.h"
 #include "dialogs/RoomSettings.hpp"
+#include "ui/ToggleButton.h"
 
 #include <QApplication>
 #include <QComboBox>
 #include <QLabel>
+#include <QMessageBox>
 #include <QPainter>
 #include <QPixmap>
 #include <QSettings>
@@ -188,8 +192,8 @@ RoomSettings::RoomSettings(const QString &room_id, QWidget *parent)
         layout->setSpacing(15);
         layout->setMargin(20);
 
-        saveBtn_ = new FlatButton("SAVE", this);
-        saveBtn_->setFontSize(conf::btn::fontSize);
+        okBtn_ = new FlatButton(tr("OK"), this);
+        okBtn_->setFontSize(conf::btn::fontSize);
         cancelBtn_ = new FlatButton(tr("CANCEL"), this);
         cancelBtn_->setFontSize(conf::btn::fontSize);
 
@@ -197,7 +201,7 @@ RoomSettings::RoomSettings(const QString &room_id, QWidget *parent)
         btnLayout->setSpacing(0);
         btnLayout->setMargin(0);
         btnLayout->addStretch(1);
-        btnLayout->addWidget(saveBtn_);
+        btnLayout->addWidget(okBtn_);
         btnLayout->addWidget(cancelBtn_);
 
         auto notifOptionLayout_ = new QHBoxLayout;
@@ -236,6 +240,61 @@ RoomSettings::RoomSettings(const QString &room_id, QWidget *parent)
         accessOptionLayout->addWidget(accessLabel);
         accessOptionLayout->addWidget(accessCombo);
 
+        auto encryptionOptionLayout = new QHBoxLayout;
+        encryptionOptionLayout->setMargin(SettingsMargin);
+        auto encryptionLabel = new QLabel(tr("Encryption"), this);
+        encryptionLabel->setStyleSheet("font-size: 15px;");
+        encryptionToggle_ = new Toggle(this);
+        connect(encryptionToggle_, &Toggle::toggled, this, [this](bool isOn) {
+                if (isOn)
+                        return;
+
+                QFont font;
+                font.setPixelSize(conf::fontSize);
+
+                QMessageBox msgBox;
+                msgBox.setIcon(QMessageBox::Question);
+                msgBox.setFont(font);
+                msgBox.setWindowTitle(tr("End-to-End Encryption"));
+                msgBox.setText(tr(
+                  "Encryption is currently experimental and things might break unexpectedly. <br>"
+                  "Please take note that it can't be disabled afterwards."));
+                msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+                msgBox.setDefaultButton(QMessageBox::Save);
+                int ret = msgBox.exec();
+
+                switch (ret) {
+                case QMessageBox::Ok: {
+                        encryptionToggle_->setState(false);
+                        encryptionToggle_->setEnabled(false);
+                        enableEncryption();
+                        break;
+                }
+                default: {
+                        encryptionToggle_->setState(true);
+                        encryptionToggle_->setEnabled(true);
+                        break;
+                }
+                }
+        });
+
+        encryptionOptionLayout->addWidget(encryptionLabel);
+        encryptionOptionLayout->addWidget(encryptionToggle_, 0, Qt::AlignBottom | Qt::AlignRight);
+
+        // Disable encryption button.
+        if (usesEncryption_) {
+                encryptionToggle_->setState(false);
+                encryptionToggle_->setEnabled(false);
+        } else {
+                encryptionToggle_->setState(true);
+        }
+
+        // Hide encryption option for public rooms.
+        if (!usesEncryption_ && (info_.join_rule == JoinRule::Public)) {
+                encryptionToggle_->hide();
+                encryptionLabel->hide();
+        }
+
         QFont font;
         font.setPixelSize(18);
         font.setWeight(70);
@@ -255,10 +314,18 @@ RoomSettings::RoomSettings(const QString &room_id, QWidget *parent)
         layout->addLayout(editLayout_);
         layout->addLayout(notifOptionLayout_);
         layout->addLayout(accessOptionLayout);
+        layout->addLayout(encryptionOptionLayout);
         layout->addLayout(btnLayout);
 
         connect(cancelBtn_, &QPushButton::clicked, this, &RoomSettings::closing);
-        connect(saveBtn_, &QPushButton::clicked, this, &RoomSettings::saveSettings);
+        connect(okBtn_, &QPushButton::clicked, this, &RoomSettings::saveSettings);
+
+        connect(this, &RoomSettings::enableEncryptionError, this, [this](const QString &msg) {
+                encryptionToggle_->setState(true);
+                encryptionToggle_->setEnabled(true);
+
+                emit ChatPage::instance()->showNotification(msg);
+        });
 }
 
 void
@@ -308,7 +375,8 @@ void
 RoomSettings::retrieveRoomInfo()
 {
         try {
-                info_ = cache::client()->singleRoomInfo(room_id_.toStdString());
+                usesEncryption_ = cache::client()->isRoomEncrypted(room_id_.toStdString());
+                info_           = cache::client()->singleRoomInfo(room_id_.toStdString());
                 setAvatar(QImage::fromData(cache::client()->image(info_.avatar_url)));
         } catch (const lmdb::error &e) {
                 qWarning() << "failed to retrieve room info from cache" << room_id_;
@@ -339,6 +407,28 @@ RoomSettings::saveSettings()
         closing();
 }
 
+void
+RoomSettings::enableEncryption()
+{
+        const auto room_id = room_id_.toStdString();
+        http::v2::client()->enable_encryption(
+          room_id, [room_id, this](const mtx::responses::EventId &, mtx::http::RequestErr err) {
+                  if (err) {
+                          int status_code = static_cast<int>(err->status_code);
+                          log::net()->warn("failed to enable encryption in room ({}): {} {}",
+                                           room_id,
+                                           err->matrix_error.error,
+                                           status_code);
+                          emit enableEncryptionError(
+                            tr("Failed to enable encryption: %1")
+                              .arg(QString::fromStdString(err->matrix_error.error)));
+                          return;
+                  }
+
+                  log::net()->info("enabled encryption on room ({})", room_id);
+          });
+}
+
 void
 RoomSettings::paintEvent(QPaintEvent *)
 {
diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc
index 9baa1f4a0b61cbdc865b98c840a6143fa740498b..d004a5430c4538524f5b5b925efc170e04b5b3c3 100644
--- a/src/timeline/TimelineView.cc
+++ b/src/timeline/TimelineView.cc
@@ -284,6 +284,8 @@ TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &
                 auto decrypted =
                   parseEncryptedEvent(mpark::get<EncryptedEvent<msg::Encrypted>>(event));
                 return parseMessageEvent(decrypted, direction);
+        } else if (mpark::holds_alternative<StateEvent<state::Encryption>>(event)) {
+                cache::client()->setEncryptedRoom(room_id_.toStdString());
         }
 
         return nullptr;