summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoseph Donofry <joedonofry@gmail.com>2021-08-10 19:29:58 -0400
committerJoseph Donofry <joedonofry@gmail.com>2021-08-10 19:29:58 -0400
commitc91e771d538d99c9353b779ac4dd0d93a77cca6b (patch)
treea0b583cf945b5cd09622ccb68b9fc8ad0e369794
parentMerge remote-tracking branch 'nheko-im/master' into video_player_enhancements (diff)
parentProtect against replay attacks (diff)
downloadnheko-c91e771d538d99c9353b779ac4dd0d93a77cca6b.tar.xz
Merge origin/master and fix conflicts
-rwxr-xr-x.ci/format.sh13
-rw-r--r--.gitlab-ci.yml18
-rw-r--r--CMakeLists.txt34
-rw-r--r--io.github.NhekoReborn.Nheko.yaml4
-rw-r--r--nheko-nightly.flatpakref1
-rw-r--r--nheko-nightly.flatpakrepo1
-rw-r--r--resources/langs/nheko_cs.ts320
-rw-r--r--resources/langs/nheko_de.ts323
-rw-r--r--resources/langs/nheko_el.ts317
-rw-r--r--resources/langs/nheko_en.ts327
-rw-r--r--resources/langs/nheko_eo.ts321
-rw-r--r--resources/langs/nheko_es.ts319
-rw-r--r--resources/langs/nheko_et.ts327
-rw-r--r--resources/langs/nheko_fi.ts319
-rw-r--r--resources/langs/nheko_fr.ts325
-rw-r--r--resources/langs/nheko_hu.ts326
-rw-r--r--resources/langs/nheko_it.ts319
-rw-r--r--resources/langs/nheko_ja.ts318
-rw-r--r--resources/langs/nheko_ml.ts321
-rw-r--r--resources/langs/nheko_nl.ts317
-rw-r--r--resources/langs/nheko_pl.ts320
-rw-r--r--resources/langs/nheko_pt_BR.ts319
-rw-r--r--resources/langs/nheko_pt_PT.ts319
-rw-r--r--resources/langs/nheko_ro.ts320
-rw-r--r--resources/langs/nheko_ru.ts322
-rw-r--r--resources/langs/nheko_si.ts317
-rw-r--r--resources/langs/nheko_sv.ts321
-rw-r--r--resources/langs/nheko_zh_CN.ts318
-rw-r--r--resources/qml/Avatar.qml6
-rw-r--r--resources/qml/ChatPage.qml1
-rw-r--r--resources/qml/Completer.qml2
-rw-r--r--resources/qml/ForwardCompleter.qml6
-rw-r--r--resources/qml/InviteDialog.qml159
-rw-r--r--resources/qml/MatrixText.qml4
-rw-r--r--resources/qml/MatrixTextField.qml4
-rw-r--r--resources/qml/MessageInput.qml41
-rw-r--r--resources/qml/MessageView.qml53
-rw-r--r--resources/qml/PrivacyScreen.qml5
-rw-r--r--resources/qml/QuickSwitcher.qml6
-rw-r--r--resources/qml/RawMessageDialog.qml52
-rw-r--r--resources/qml/ReadReceipts.qml130
-rw-r--r--resources/qml/RoomList.qml69
-rw-r--r--resources/qml/RoomMembers.qml148
-rw-r--r--resources/qml/RoomSettings.qml55
-rw-r--r--resources/qml/Root.qml107
-rw-r--r--resources/qml/ScrollHelper.qml6
-rw-r--r--resources/qml/StatusIndicator.qml2
-rw-r--r--resources/qml/TimelineRow.qml44
-rw-r--r--resources/qml/TimelineView.qml23
-rw-r--r--resources/qml/TopBar.qml12
-rw-r--r--resources/qml/UserProfile.qml19
-rw-r--r--resources/qml/components/AvatarListTile.qml133
-rw-r--r--resources/qml/delegates/Encrypted.qml48
-rw-r--r--resources/qml/delegates/MessageDelegate.qml24
-rw-r--r--resources/qml/delegates/PlayableMediaMessage.qml2
-rw-r--r--resources/qml/delegates/Reply.qml6
-rw-r--r--resources/qml/device-verification/DeviceVerification.qml7
-rw-r--r--resources/qml/dialogs/ImagePackEditorDialog.qml301
-rw-r--r--resources/qml/dialogs/ImagePackSettingsDialog.qml260
-rw-r--r--resources/qml/dialogs/InputDialog.qml1
-rw-r--r--resources/qml/emoji/StickerPicker.qml1
-rw-r--r--resources/qml/voip/CallInvite.qml5
-rw-r--r--resources/qml/voip/PlaceCall.qml4
-rw-r--r--resources/qml/voip/ScreenShare.qml2
-rw-r--r--resources/res.qrc28
-rw-r--r--src/Cache.cpp146
-rw-r--r--src/CacheCryptoStructs.h3
-rw-r--r--src/CacheStructs.h5
-rw-r--r--src/Cache_p.h54
-rw-r--r--src/ChatPage.cpp104
-rw-r--r--src/CombinedImagePackModel.cpp (renamed from src/ImagePackModel.cpp)17
-rw-r--r--src/CombinedImagePackModel.h (renamed from src/ImagePackModel.h)4
-rw-r--r--src/ImagePackListModel.cpp94
-rw-r--r--src/ImagePackListModel.h41
-rw-r--r--src/InviteeItem.cpp28
-rw-r--r--src/InviteeItem.h31
-rw-r--r--src/InviteesModel.cpp84
-rw-r--r--src/InviteesModel.h63
-rw-r--r--src/Logging.cpp10
-rw-r--r--src/MainWindow.cpp45
-rw-r--r--src/MainWindow.h2
-rw-r--r--src/MemberList.cpp100
-rw-r--r--src/MemberList.h67
-rw-r--r--src/MxcImageProvider.cpp27
-rw-r--r--src/MxcImageProvider.h7
-rw-r--r--src/Olm.cpp191
-rw-r--r--src/Olm.h13
-rw-r--r--src/ReadReceiptsModel.cpp131
-rw-r--r--src/ReadReceiptsModel.h73
-rw-r--r--src/RegisterPage.cpp548
-rw-r--r--src/RegisterPage.h36
-rw-r--r--src/SingleImagePackModel.cpp350
-rw-r--r--src/SingleImagePackModel.h93
-rw-r--r--src/UserSettingsPage.cpp115
-rw-r--r--src/UserSettingsPage.h7
-rw-r--r--src/Utils.cpp28
-rw-r--r--src/Utils.h9
-rw-r--r--src/dialogs/InviteUsers.cpp158
-rw-r--r--src/dialogs/InviteUsers.h45
-rw-r--r--src/dialogs/MemberList.cpp146
-rw-r--r--src/dialogs/MemberList.h57
-rw-r--r--src/dialogs/RawMessage.h60
-rw-r--r--src/dialogs/ReadReceipts.cpp179
-rw-r--r--src/dialogs/ReadReceipts.h61
-rw-r--r--src/notifications/ManagerLinux.cpp5
-rw-r--r--src/timeline/EventStore.cpp256
-rw-r--r--src/timeline/EventStore.h9
-rw-r--r--src/timeline/InputBar.cpp4
-rw-r--r--src/timeline/InputBar.h4
-rw-r--r--src/timeline/Permissions.cpp6
-rw-r--r--src/timeline/Permissions.h4
-rw-r--r--src/timeline/RoomlistModel.cpp2
-rw-r--r--src/timeline/TimelineModel.cpp59
-rw-r--r--src/timeline/TimelineModel.h33
-rw-r--r--src/timeline/TimelineViewManager.cpp90
-rw-r--r--src/timeline/TimelineViewManager.h17
-rw-r--r--src/ui/Avatar.cpp168
-rw-r--r--src/ui/Avatar.h48
-rw-r--r--src/ui/InfoMessage.cpp15
-rw-r--r--src/ui/NhekoGlobalObject.cpp7
-rw-r--r--src/ui/NhekoGlobalObject.h8
-rw-r--r--src/ui/Painter.h5
-rw-r--r--src/ui/RoomSettings.cpp13
-rw-r--r--src/ui/RoomSettings.h6
124 files changed, 8433 insertions, 4460 deletions
diff --git a/.ci/format.sh b/.ci/format.sh
index 2d922ee1..cc4a3b82 100755
--- a/.ci/format.sh
+++ b/.ci/format.sh
@@ -14,17 +14,4 @@ do
     clang-format -i "$f"
 done;
 
-QMLFORMAT_PATH=$(command -v qmlformat || true)
-
-if [ -n "$QMLFORMAT_PATH" ]; then
-    QML_FILES=$(find resources -type f -iname "*.qml")
-
-    for f in $QML_FILES
-    do
-        $QMLFORMAT_PATH -i "$f"
-    done;
-else
-    echo "qmlformat not found; skipping qml formatting"
-fi
-
 git diff --exit-code
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7ff92c17..cea6be7b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -52,14 +52,14 @@ build-macos:
   stage: build
   tags: [macos]
   before_script:
-    - brew update
-    - brew reinstall --force python3
-    - brew bundle --file=./.ci/macos/Brewfile --force --cleanup
+    #- brew update
+    #- brew reinstall --force python3
+    #- brew bundle --file=./.ci/macos/Brewfile --force --cleanup
     - pip3 install dmgbuild
     - rm -rf ../.hunter &&  mv .hunter ../.hunter || true
   script:
-    - export PATH=/usr/local/opt/qt/bin/:${PATH}
-    - export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
+    - export PATH=/usr/local/opt/qt@5/bin/:${PATH}
+    - export CMAKE_PREFIX_PATH=/usr/local/opt/qt@5
     - cmake -GNinja -H. -Bbuild
         -DCMAKE_BUILD_TYPE=RelWithDebInfo
         -DCMAKE_INSTALL_PREFIX=.deps/usr
@@ -91,7 +91,9 @@ build-flatpak-amd64:
   #image: 'registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master'
   tags: [docker]
   before_script:
-    - apt-get update && apt-get -y install flatpak-builder git python curl python3-aiohttp python3-tenacity gir1.2-ostree-1.0
+    # need flatpak 1.11.1 at least
+    - apt-get update && apt-get install -y software-properties-common
+    - add-apt-repository ppa:alexlarsson/flatpak && apt-get update && apt-get -y install flatpak-builder git python curl python3-aiohttp python3-tenacity gir1.2-ostree-1.0
     - flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
     - flatpak --noninteractive install --user flathub org.kde.Platform//5.15
     - flatpak --noninteractive install --user flathub org.kde.Sdk//5.15
@@ -119,7 +121,9 @@ build-flatpak-arm64:
   #image: 'registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master'
   tags: [docker-arm64]
   before_script:
-    - apt-get update && apt-get -y install flatpak-builder git python curl python3-aiohttp python3-tenacity gir1.2-ostree-1.0
+    # need flatpak 1.11.1 at least
+    - apt-get update && apt-get install -y software-properties-common
+    - add-apt-repository ppa:alexlarsson/flatpak && apt-get update && apt-get -y install flatpak-builder git python curl python3-aiohttp python3-tenacity gir1.2-ostree-1.0
     - flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
     - flatpak --noninteractive install --user flathub org.kde.Platform//5.15
     - flatpak --noninteractive install --user flathub org.kde.Sdk//5.15
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6b26b2e5..049ed8a3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -281,14 +281,11 @@ set(SRC_FILES
 	src/dialogs/CreateRoom.cpp
 	src/dialogs/FallbackAuth.cpp
 	src/dialogs/ImageOverlay.cpp
-	src/dialogs/InviteUsers.cpp
 	src/dialogs/JoinRoom.cpp
 	src/dialogs/LeaveRoom.cpp
 	src/dialogs/Logout.cpp
-	src/dialogs/MemberList.cpp
 	src/dialogs/PreviewUploadOverlay.cpp
 	src/dialogs/ReCaptcha.cpp
-	src/dialogs/ReadReceipts.cpp
 
 	# Emoji
 	src/emoji/EmojiModel.cpp
@@ -307,7 +304,6 @@ set(SRC_FILES
 	src/timeline/RoomlistModel.cpp
 
 	# UI components
-	src/ui/Avatar.cpp
 	src/ui/Badge.cpp
 	src/ui/DropShadow.cpp
 	src/ui/FlatButton.cpp
@@ -346,16 +342,20 @@ set(SRC_FILES
 	src/CompletionProxyModel.cpp
 	src/DeviceVerificationFlow.cpp
 	src/EventAccessors.cpp
-	src/InviteeItem.cpp
+	src/InviteesModel.cpp
 	src/Logging.cpp
 	src/LoginPage.cpp
 	src/MainWindow.cpp
 	src/MatrixClient.cpp
+	src/MemberList.cpp
 	src/MxcImageProvider.cpp
 	src/Olm.cpp
+    src/ReadReceiptsModel.cpp
 	src/RegisterPage.cpp
 	src/SSOHandler.cpp
-	src/ImagePackModel.cpp
+	src/CombinedImagePackModel.cpp
+	src/SingleImagePackModel.cpp
+	src/ImagePackListModel.cpp
 	src/TrayIcon.cpp
 	src/UserSettingsPage.cpp
 	src/UsersModel.cpp
@@ -381,7 +381,7 @@ if(USE_BUNDLED_MTXCLIENT)
 	FetchContent_Declare(
 		MatrixClient
 		GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
-		GIT_TAG        1c277e9ac69aafdaf6888ce595b21dc86e970f28
+		GIT_TAG        bcf363cb5e6c423f40c96123e227bc8c5f6d6f80
 		)
 	set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
 	set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")
@@ -492,15 +492,11 @@ qt5_wrap_cpp(MOC_HEADERS
 	src/dialogs/CreateRoom.h
 	src/dialogs/FallbackAuth.h
 	src/dialogs/ImageOverlay.h
-	src/dialogs/InviteUsers.h
 	src/dialogs/JoinRoom.h
 	src/dialogs/LeaveRoom.h
 	src/dialogs/Logout.h
-	src/dialogs/MemberList.h
 	src/dialogs/PreviewUploadOverlay.h
-	src/dialogs/RawMessage.h
 	src/dialogs/ReCaptcha.h
-	src/dialogs/ReadReceipts.h
 
 	# Emoji
 	src/emoji/EmojiModel.h
@@ -518,7 +514,6 @@ qt5_wrap_cpp(MOC_HEADERS
 	src/timeline/RoomlistModel.h
 
 	# UI components
-	src/ui/Avatar.h
 	src/ui/Badge.h
 	src/ui/FlatButton.h
 	src/ui/FloatingButton.h
@@ -546,28 +541,33 @@ qt5_wrap_cpp(MOC_HEADERS
 
 	src/AvatarProvider.h
 	src/BlurhashProvider.h
-	src/Cache_p.h
 	src/CacheCryptoStructs.h
+	src/Cache_p.h
 	src/CallDevices.h
 	src/CallManager.h
 	src/ChatPage.h
 	src/Clipboard.h
+	src/CombinedImagePackModel.h
 	src/CompletionProxyModel.h
 	src/DeviceVerificationFlow.h
-	src/InviteeItem.h
+	src/ImagePackListModel.h
+	src/InviteesModel.h
 	src/LoginPage.h
 	src/MainWindow.h
+	src/MemberList.h
 	src/MxcImageProvider.h
+	src/Olm.h
 	src/RegisterPage.h
+	src/RoomsModel.h
 	src/SSOHandler.h
-	src/ImagePackModel.h
+	src/SingleImagePackModel.h
 	src/TrayIcon.h
 	src/UserSettingsPage.h
 	src/UsersModel.h
-	src/RoomsModel.h
 	src/WebRTCSession.h
 	src/WelcomePage.h
-	)
+	src/ReadReceiptsModel.h
+)
 
 #
 # Bundle translations.
diff --git a/io.github.NhekoReborn.Nheko.yaml b/io.github.NhekoReborn.Nheko.yaml
index b6f468db..a0e57b09 100644
--- a/io.github.NhekoReborn.Nheko.yaml
+++ b/io.github.NhekoReborn.Nheko.yaml
@@ -19,6 +19,8 @@ finish-args:
   - --talk-name=org.freedesktop.secrets
   - --talk-name=org.freedesktop.StatusNotifierItem
   - --talk-name=org.kde.*
+  # needed for SingleApplication to work
+  - --allow=per-app-dev-shm
 cleanup:
   - /include
   - /bin/mdb*
@@ -161,7 +163,7 @@ modules:
     buildsystem: cmake-ninja
     name: mtxclient
     sources:
-      - commit: 1c277e9ac69aafdaf6888ce595b21dc86e970f28
+      - commit: bcf363cb5e6c423f40c96123e227bc8c5f6d6f80
         type: git
         url: https://github.com/Nheko-Reborn/mtxclient.git
   - config-opts:
diff --git a/nheko-nightly.flatpakref b/nheko-nightly.flatpakref
index 7d27bdfe..74e47ecd 100644
--- a/nheko-nightly.flatpakref
+++ b/nheko-nightly.flatpakref
@@ -3,6 +3,7 @@ Title=Nheko Nightly
 Name=io.github.NhekoReborn.Nheko
 Branch=master
 Url=https://flatpak.neko.dev/repo/nightly
+SuggestRemoteName=nheko-nightlies
 Homepage=https://nheko-reborn.github.io/
 Icon=https://nheko.im/nheko-reborn/nheko/-/raw/master/resources/nheko.svg
 RuntimeRepo=https://dl.flathub.org/repo/flathub.flatpakrepo
diff --git a/nheko-nightly.flatpakrepo b/nheko-nightly.flatpakrepo
index 4fb1bc55..680558af 100644
--- a/nheko-nightly.flatpakrepo
+++ b/nheko-nightly.flatpakrepo
@@ -1,6 +1,7 @@
 [Flatpak Repo]
 Title=Nheko Nightly
 Url=https://flatpak.neko.dev/repo/nightly
+SuggestRemoteName=nheko-nightlies
 Homepage=https://nheko.im/
 Comment=Nheko nightly release repository
 Description=Nheko nightly release repository
diff --git a/resources/langs/nheko_cs.ts b/resources/langs/nheko_cs.ts
index c544bf6d..99813f81 100644
--- a/resources/langs/nheko_cs.ts
+++ b/resources/langs/nheko_cs.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation type="unfinished"></translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation type="unfinished"></translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished"></translation>
     </message>
@@ -617,16 +655,42 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
@@ -741,27 +805,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+128"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+187"/>
         <source>Encryption enabled</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -771,7 +822,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -781,17 +832,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -806,23 +857,23 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+29"/>
+        <location line="+38"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-18"/>
+        <location line="-24"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-80"/>
+        <location line="-99"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+80"/>
+        <location line="+102"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished"></translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished"></translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,42 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1485,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1515,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1470,6 +1547,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1621,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1642,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1670,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1578,7 +1681,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1713,12 +1816,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1747,7 +1850,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1755,17 +1858,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1773,7 +1891,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1781,18 +1899,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1833,7 +1950,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1843,7 +1960,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1892,7 +2009,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1901,7 +2018,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2347,7 +2464,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2398,7 +2515,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2470,19 +2587,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2593,7 +2697,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_de.ts b/resources/langs/nheko_de.ts
index 58c209a2..cb5b54fb 100644
--- a/resources/langs/nheko_de.ts
+++ b/resources/langs/nheko_de.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Auf Bestätigung warten</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Wartet darauf, dass die andere Seite die Verifizierung abschließt.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Abbrechen</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Nutzer konnte nicht eingeladen werden: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Einladung bestätigen</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Nutzer %1 (%2) wirklich einladen?</translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Verbannung aufgehoben: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation>Möchtest du wirklich eine private Konversation mit %1 beginnen?</translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Migration des Caches fehlgeschlagen!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation>Geheimnisse entschlüsseln</translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Suche</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Leute</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Schließen</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation>Datei auswählen</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation>Alle Dateien (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation>Medienupload fehlgeschlagen. Bitte versuche es erneut.</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation>Lade Benutzer in %1 ein</translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation>Benutzer-ID, die eingeladen werden soll</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation>@joe:matrix.org</translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation>Hinzufügen</translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation>Einladen</translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Löschen</translation>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation>Abbrechen</translation>
     </message>
 </context>
 <context>
@@ -745,22 +809,9 @@ Beispiel: https://mein.server:8787</translation>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Teilnehmerliste</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>entfernt</translation>
@@ -771,7 +822,7 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Verschlüsselung aktiviert</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>Raumname wurde gändert auf: %1</translation>
     </message>
@@ -781,7 +832,7 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Raumname wurde entfernt</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>Raumthema wurde geändert auf: %1</translation>
     </message>
@@ -791,17 +842,17 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Thema wurde entfernt</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation>%1 hat dem Raumavatar geändert</translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 hat den Raum erstellt: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 hat einen Sprachanruf gestartet.</translation>
     </message>
@@ -816,17 +867,17 @@ Beispiel: https://mein.server:8787</translation>
         <translation>%1 hat angerufen.</translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation>%1 hat den Anruf angenommen.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation>%1 hat den Anruf beendet.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation>Wählt…</translation>
     </message>
@@ -834,7 +885,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation>Auflegen</translation>
     </message>
@@ -855,6 +906,11 @@ Beispiel: https://mein.server:8787</translation>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation>Sticker</translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>Emoji</translation>
     </message>
@@ -872,17 +928,17 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation>Bearbeiten</translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation>Reaktion senden</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation>Antworten</translation>
     </message>
@@ -892,7 +948,7 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Optionen</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation>&amp;Kopieren</translation>
     </message>
@@ -1100,7 +1156,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>Unimplementiertes Event: </translation>
     </message>
@@ -1220,7 +1276,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Schließen</translation>
     </message>
@@ -1233,7 +1289,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>keine Version gespeichert</translation>
     </message>
@@ -1241,7 +1297,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation>Neuer Tag</translation>
     </message>
@@ -1251,6 +1307,16 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Gib den Tag, den du verwenden willst, ein:</translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation>Raum verlassen</translation>
@@ -1281,17 +1347,7 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Neuen Tag erstellen...</translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation>Akzeptieren</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation>Ablehnen</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation>Statusnachricht</translation>
     </message>
@@ -1342,19 +1398,41 @@ Beispiel: https://mein.server:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation>Teilnehmer in %1</translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation>
+            <numerusform>%n Person in %1</numerusform>
+            <numerusform>%n Personen in %1</numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation>Lade mehr Leute ein</translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation>Raumeinstellungen</translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation>%1 Teilnehmer</translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation>EINSTELLUNGEN</translation>
     </message>
@@ -1410,19 +1488,22 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Verschlüsselung ist derzeit experimentell und könnte unerwartete Probleme verursachen.&lt;br&gt;Bitte beachte, dass dies später nicht mehr deaktiviert werden kann.</translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
-        <translation>Auf Schlüsselanfragen antworten</translation>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
-        <translation>Soll der Client automatisch mit den Sitzungsschlüsseln auf Anfragen antworten oder nicht? Bitte mit Vorsicht benutzen, dies ist eine vorübergehende Maßnahme, um die Ende-zu-Ende-Implementierung zu testen, bis die Geräteverifizierung abgeschlossen ist.</translation>
+        <location line="+4"/>
+        <source>Change</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation>INFO</translation>
     </message>
@@ -1437,12 +1518,7 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Raumversion</translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation>Aktivierung der Verschlüsselung fehlgeschlagen: %1</translation>
     </message>
@@ -1474,6 +1550,24 @@ Beispiel: https://mein.server:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation>Offene Einladung.</translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation>Vorschau dieses Raums</translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation>Keine Vorschau verfügbar</translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1530,7 +1624,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Fehlgeschlagen</translation>
     </message>
@@ -1551,6 +1645,14 @@ Beispiel: https://mein.server:8787</translation>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation>Suche</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1571,7 +1673,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Nachricht zurückziehen fehlgeschlagen: %1</translation>
     </message>
@@ -1582,7 +1684,7 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Event konnte nicht verschlüsselt werden, senden wurde abgebrochen!</translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Bild speichern</translation>
     </message>
@@ -1716,12 +1818,12 @@ Beispiel: https://mein.server:8787</translation>
         <translation>%1 hat das Anklopfen zurückgezogen.</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Du bist dem Raum beigetreten.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation>%1 hat den eigenen Avatar und Namen geändert zu %2.</translation>
     </message>
@@ -1750,7 +1852,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation>Bearbeitet</translation>
     </message>
@@ -1758,17 +1860,32 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>Kein Raum geöffnet</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation>%1 Teilnehmer</translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation>An der Unterhaltung teilnehmen</translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation>Einladung annehmen</translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation>Einladung ablehnen</translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation>Zurück zur Raumliste</translation>
     </message>
@@ -1776,7 +1893,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation>Keinen verschlüsselten Chat mit diesem User gefunden. Erstelle einen verschlüsselten 1:1 Chat mit diesem Nutzer und versuche es erneut.</translation>
     </message>
@@ -1784,18 +1901,17 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation>Zurück zur Raumliste</translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation>Kein Raum ausgewählt</translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation>Raumoptionen</translation>
     </message>
@@ -1836,7 +1952,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation>Globales Nutzerprofil</translation>
     </message>
@@ -1846,7 +1962,7 @@ Beispiel: https://mein.server:8787</translation>
         <translation>Raumspezifisches Nutzerprofil</translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation>Verifizieren</translation>
@@ -1895,7 +2011,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation>Standard</translation>
@@ -1904,7 +2020,7 @@ Beispiel: https://mein.server:8787</translation>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Ins Benachrichtigungsfeld minimieren</translation>
     </message>
@@ -2360,7 +2476,7 @@ Normalerweise animiert das den Taskbaricon oder färbt das Fenster orange ein.</
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation>Auf Gegenseite warten…</translation>
     </message>
@@ -2411,7 +2527,7 @@ Normalerweise animiert das den Taskbaricon oder färbt das Fenster orange ein.</
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Gestern</translation>
     </message>
@@ -2483,19 +2599,6 @@ Normalerweise animiert das den Taskbaricon oder färbt das Fenster orange ein.</
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Abbrechen</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>Benutzer-ID, die eingeladen werden soll</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2608,7 +2711,7 @@ Medien-Größe: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>Du hast eine Audiodatei gesendet</translation>
     </message>
diff --git a/resources/langs/nheko_el.ts b/resources/langs/nheko_el.ts
index d5d5f323..d40a6433 100644
--- a/resources/langs/nheko_el.ts
+++ b/resources/langs/nheko_el.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation type="unfinished">Άκυρο</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation type="unfinished"></translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished">Διάλεξε ένα αρχείο</translation>
     </message>
@@ -617,18 +655,44 @@
         <translation type="unfinished">Όλα τα αρχεία (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">Όνομα χρήστη</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
+        <location line="+58"/>
+        <source>Invite</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Άκυρο</translation>
+    </message>
 </context>
 <context>
     <name>LoginPage</name>
@@ -741,22 +805,9 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Μέλη</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
@@ -767,7 +818,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -777,7 +828,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -787,17 +838,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -812,17 +863,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished"></translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Βγές</translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Αποδοχή</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Απόρριψη</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,41 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1484,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1514,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1470,6 +1546,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1620,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1641,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1669,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1578,7 +1680,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished">Αποθήκευση Εικόνας</translation>
     </message>
@@ -1712,12 +1814,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1746,7 +1848,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1754,17 +1856,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1772,7 +1889,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1780,18 +1897,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1832,7 +1948,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1842,7 +1958,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1891,7 +2007,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1900,7 +2016,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Ελαχιστοποίηση</translation>
     </message>
@@ -2346,7 +2462,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2397,7 +2513,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2469,19 +2585,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation type="unfinished">Άκυρο</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>Όνομα χρήστη</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2592,7 +2695,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_en.ts b/resources/langs/nheko_en.ts
index 7d3f8276..1851fff1 100644
--- a/resources/langs/nheko_en.ts
+++ b/resources/langs/nheko_en.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Awaiting Confirmation</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Waiting for other side to complete verification.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Cancel</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Failed to invite user: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Confirm invite</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Do you really want to invite %1 (%2)?</translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Unbanned user: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation>Do you really want to start a private chat with %1?</translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Cache migration failed!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation>Decrypt secrets</translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Search</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>People</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Close</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation>Select a file</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation>All Files (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation>Failed to upload media. Please try again.</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation>Invite users to %1</translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation>User ID to invite</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation>@joe:matrix.org</translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation>Add</translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation>Invite</translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Remove</translation>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation>Cancel</translation>
     </message>
 </context>
 <context>
@@ -745,27 +809,14 @@ Example: https://server.my:8787</translation>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Room members</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+128"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+187"/>
         <source>Encryption enabled</source>
         <translation>Encryption enabled</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>room name changed to: %1</translation>
     </message>
@@ -775,7 +826,7 @@ Example: https://server.my:8787</translation>
         <translation>removed room name</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>topic changed to: %1</translation>
     </message>
@@ -785,17 +836,17 @@ Example: https://server.my:8787</translation>
         <translation>removed topic</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation>%1 changed the room avatar</translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 created and configured room: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 placed a voice call.</translation>
     </message>
@@ -810,23 +861,23 @@ Example: https://server.my:8787</translation>
         <translation>%1 placed a call.</translation>
     </message>
     <message>
-        <location line="+29"/>
+        <location line="+38"/>
         <source>Negotiating call...</source>
         <translation>Negotiating call…</translation>
     </message>
     <message>
-        <location line="-18"/>
+        <location line="-24"/>
         <source>%1 answered the call.</source>
         <translation>%1 answered the call.</translation>
     </message>
     <message>
-        <location line="-80"/>
+        <location line="-99"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>removed</translation>
     </message>
     <message>
-        <location line="+80"/>
+        <location line="+102"/>
         <source>%1 ended the call.</source>
         <translation>%1 ended the call.</translation>
     </message>
@@ -834,7 +885,7 @@ Example: https://server.my:8787</translation>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation>Hang up</translation>
     </message>
@@ -855,6 +906,11 @@ Example: https://server.my:8787</translation>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation>Stickers</translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>Emoji</translation>
     </message>
@@ -872,17 +928,17 @@ Example: https://server.my:8787</translation>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation>Edit</translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation>React</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation>Reply</translation>
     </message>
@@ -892,7 +948,7 @@ Example: https://server.my:8787</translation>
         <translation>Options</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation>&amp;Copy</translation>
     </message>
@@ -1100,7 +1156,7 @@ Example: https://server.my:8787</translation>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>unimplemented event: </translation>
     </message>
@@ -1220,7 +1276,7 @@ Example: https://server.my:8787</translation>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Close</translation>
     </message>
@@ -1233,7 +1289,7 @@ Example: https://server.my:8787</translation>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>no version stored</translation>
     </message>
@@ -1241,7 +1297,7 @@ Example: https://server.my:8787</translation>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation>New tag</translation>
     </message>
@@ -1251,6 +1307,16 @@ Example: https://server.my:8787</translation>
         <translation>Enter the tag you want to use:</translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation>Leave room</translation>
@@ -1281,17 +1347,7 @@ Example: https://server.my:8787</translation>
         <translation>Create new tag...</translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation>Accept</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation>Decline</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation>Status Message</translation>
     </message>
@@ -1342,19 +1398,41 @@ Example: https://server.my:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation>Members of %1</translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation>
+            <numerusform>%n person in %1</numerusform>
+            <numerusform>%n people in %1</numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation>Invite more people</translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation>Room Settings</translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation>%1 member(s)</translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation>SETTINGS</translation>
     </message>
@@ -1410,21 +1488,22 @@ Example: https://server.my:8787</translation>
         <translation>Encryption is currently experimental and things might break unexpectedly.&lt;br&gt;Please take note that it can&apos;t be disabled afterwards.</translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
-        <translation>Respond to key requests</translation>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
-        <translation>Whether or not the client should respond automatically with the session keys
-upon request. Use with caution, this is a temporary measure to test the
-E2E implementation until device verification is completed.</translation>
+        <location line="+4"/>
+        <source>Change</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation>INFO</translation>
     </message>
@@ -1439,12 +1518,7 @@ E2E implementation until device verification is completed.</translation>
         <translation>Room Version</translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation>Failed to enable encryption: %1</translation>
     </message>
@@ -1476,6 +1550,24 @@ E2E implementation until device verification is completed.</translation>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation>Pending invite.</translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation>Previewing this room</translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation>No preview available</translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1532,7 +1624,7 @@ E2E implementation until device verification is completed.</translation>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Failed</translation>
     </message>
@@ -1553,6 +1645,14 @@ E2E implementation until device verification is completed.</translation>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation>Search</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1573,7 +1673,7 @@ E2E implementation until device verification is completed.</translation>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Message redaction failed: %1</translation>
     </message>
@@ -1584,7 +1684,7 @@ E2E implementation until device verification is completed.</translation>
         <translation>Failed to encrypt event, sending aborted!</translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Save image</translation>
     </message>
@@ -1718,12 +1818,12 @@ E2E implementation until device verification is completed.</translation>
         <translation>%1 redacted their knock.</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>You joined this room.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation>%1 has changed their avatar and changed their display name to %2.</translation>
     </message>
@@ -1752,7 +1852,7 @@ E2E implementation until device verification is completed.</translation>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation>Edited</translation>
     </message>
@@ -1760,17 +1860,32 @@ E2E implementation until device verification is completed.</translation>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>No room open</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation>%1 member(s)</translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation>join the conversation</translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation>accept invite</translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation>decline invite</translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation>Back to room list</translation>
     </message>
@@ -1778,7 +1893,7 @@ E2E implementation until device verification is completed.</translation>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</translation>
     </message>
@@ -1786,18 +1901,17 @@ E2E implementation until device verification is completed.</translation>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation>Back to room list</translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation>No room selected</translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation>Room options</translation>
     </message>
@@ -1838,7 +1952,7 @@ E2E implementation until device verification is completed.</translation>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation>Global User Profile</translation>
     </message>
@@ -1848,7 +1962,7 @@ E2E implementation until device verification is completed.</translation>
         <translation>Room User Profile</translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation>Verify</translation>
@@ -1897,7 +2011,7 @@ E2E implementation until device verification is completed.</translation>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation>Default</translation>
@@ -1906,7 +2020,7 @@ E2E implementation until device verification is completed.</translation>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Minimize to tray</translation>
     </message>
@@ -2363,7 +2477,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation>Waiting for other party…</translation>
     </message>
@@ -2414,7 +2528,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Yesterday</translation>
     </message>
@@ -2486,19 +2600,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Cancel</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>User ID to invite</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2611,7 +2712,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>You sent an audio clip</translation>
     </message>
diff --git a/resources/langs/nheko_eo.ts b/resources/langs/nheko_eo.ts
index 26a67d49..a77c5c25 100644
--- a/resources/langs/nheko_eo.ts
+++ b/resources/langs/nheko_eo.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Atendante konfirmon</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Atendante kontrolon venontan de la alia flanko.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Nuligi</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Malsukcesis inviti uzanton: %1</translation>
     </message>
@@ -158,12 +158,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Konfirmu inviton</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Ĉu vi certe volas inviti uzanton %1 (%2)?</translation>
     </message>
@@ -228,12 +228,12 @@
         <translation>Malforbaris uzanton: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation>Ĉu vi certe volas komenci privatan babilon kun %1?</translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Malsukcesis migrado de kaŝmemoro!</translation>
     </message>
@@ -353,7 +353,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation>Malĉifri sekretojn</translation>
     </message>
@@ -427,12 +427,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Serĉu</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Homoj</translation>
     </message>
@@ -606,9 +606,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Fermi</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished">Elektu dosieron</translation>
     </message>
@@ -618,17 +656,43 @@
         <translation>Ĉiuj dosieroj (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation>Malsukcesis alŝuti vidaŭdaĵojn. Bonvolu reprovi.</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Forigi</translation>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Nuligi</translation>
     </message>
 </context>
 <context>
@@ -748,22 +812,9 @@ Ekzemplo: https://servilo.mia:8787</translation>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Membroj de la ĉambro</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>Bone</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>forigita</translation>
@@ -774,7 +825,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>Nomo da ĉambro ŝanĝiĝis al: %1</translation>
     </message>
@@ -784,7 +835,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -794,17 +845,17 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 metis voĉvokon.</translation>
     </message>
@@ -819,17 +870,17 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation>%1 metis vokon.</translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation>%1 respondis la vokon.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation>%1 finis la vokon.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation>Traktante vokon…</translation>
     </message>
@@ -837,7 +888,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -858,6 +909,11 @@ Ekzemplo: https://servilo.mia:8787</translation>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>Bildosignoj</translation>
     </message>
@@ -875,17 +931,17 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation>Redakti</translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation>Reagi</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation>Respondi</translation>
     </message>
@@ -895,7 +951,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation>Elektebloj</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1103,7 +1159,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>neprogramita okazo: </translation>
     </message>
@@ -1223,7 +1279,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Fermi</translation>
     </message>
@@ -1236,7 +1292,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1244,7 +1300,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1254,6 +1310,16 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Eliri el ĉambro</translation>
@@ -1284,17 +1350,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Akcepti</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Rifuzi</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1345,19 +1401,41 @@ Ekzemplo: https://servilo.mia:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation>Agordoj de ĉambro</translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation>%1 ĉambrano(j)</translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation>AGORDOJ</translation>
     </message>
@@ -1413,19 +1491,22 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation>Ĉifrado nun estas eksperimenta kaj povus rompiĝi neatendite.&lt;br&gt;Bonvole sciu, ke ne eblas ĝin malŝalti poste.</translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
-        <translation>Respondi al petoj de ŝlosiloj</translation>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation>INFORMOJ</translation>
     </message>
@@ -1440,12 +1521,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation>Versio de ĉambro</translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation>Bone</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation>Malsukcesis ŝalti ĉifradon: %1</translation>
     </message>
@@ -1477,6 +1553,24 @@ Ekzemplo: https://servilo.mia:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1533,7 +1627,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Estas malsukcesa</translation>
     </message>
@@ -1555,6 +1649,14 @@ Ekzemplo: https://servilo.mia:8787</translation>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">Serĉu</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1575,7 +1677,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1586,7 +1688,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Konservi bildon</translation>
     </message>
@@ -1722,12 +1824,12 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Vi aliĝis ĉi tiun ĉambron.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1756,7 +1858,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation>Redaktita</translation>
     </message>
@@ -1764,17 +1866,32 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished">%1 ĉambrano(j)</translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1782,7 +1899,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1790,18 +1907,17 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1842,7 +1958,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1852,7 +1968,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1901,7 +2017,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1910,7 +2026,7 @@ Ekzemplo: https://servilo.mia:8787</translation>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2373,7 +2489,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2425,7 +2541,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Hieraŭ</translation>
     </message>
@@ -2497,19 +2613,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation type="unfinished">Nuligi</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2620,7 +2723,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_es.ts b/resources/langs/nheko_es.ts
index 6318b9c4..922e0500 100644
--- a/resources/langs/nheko_es.ts
+++ b/resources/langs/nheko_es.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Esperando confirmación</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Esperando a que la otra parte complete la verificación.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Cancelar</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>No se pudo invitar al usuario: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Confirmar invitación</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation type="unfinished"></translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished"></translation>
     </message>
@@ -617,18 +655,44 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Cancelar</translation>
+    </message>
 </context>
 <context>
     <name>LoginPage</name>
@@ -741,27 +805,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+128"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+187"/>
         <source>Encryption enabled</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -771,7 +822,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -781,17 +832,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -806,23 +857,23 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+29"/>
+        <location line="+38"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-18"/>
+        <location line="-24"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-80"/>
+        <location line="-99"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+80"/>
+        <location line="+102"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished"></translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished"></translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Aceptar</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Rechazar</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,41 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1484,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1514,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1470,6 +1546,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1620,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1641,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1669,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1578,7 +1680,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1722,12 +1824,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished">Te has unido a esta sala.</translation>
     </message>
     <message>
-        <location line="+885"/>
+        <location line="+886"/>
         <source>Rejected the knock from %1.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1746,7 +1848,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1754,17 +1856,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1772,7 +1889,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1780,18 +1897,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1832,7 +1948,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1842,7 +1958,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1891,7 +2007,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1900,7 +2016,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2346,7 +2462,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2397,7 +2513,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2469,19 +2585,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation type="unfinished">Cancelar</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2592,7 +2695,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_et.ts b/resources/langs/nheko_et.ts
index 198ec332..57eac1c3 100644
--- a/resources/langs/nheko_et.ts
+++ b/resources/langs/nheko_et.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Ootan kinnitust</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Ootan et teine osapool lõpetaks verifitseerimise.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Katkesta</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Kutse saatmine kasutajale ei õnnestunud: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Kinnita kutse</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Kas sa tõesti soovid saata kutset kasutajale %1 (%2)?</translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Suhtluskeeld eemaldatud: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation>Kas sa kindlasti soovid alustada otsevestlust kasutajaga %1?</translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Puhvri versiooniuuendus ebaõnnestus!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation>Dekrüpti andmed</translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Otsi</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Inimesed</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Sulge</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation>Vali fail</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation>Kõik failid (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation>Meediafailide üleslaadimine ei õnnestunud. Palun proovi uuesti.</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">Kasutajatunnus, kellele soovid kutset saata</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Eemalda</translation>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
@@ -745,27 +809,14 @@ Näiteks: https://server.minu:8787</translation>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Jututoa liikmed</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>Sobib</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+128"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+187"/>
         <source>Encryption enabled</source>
         <translation>Krüptimine on kasutusel</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>jututoa uus nimi on: %1</translation>
     </message>
@@ -775,7 +826,7 @@ Näiteks: https://server.minu:8787</translation>
         <translation>eemaldas jututoa nime</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>jututoa uus teema on: %1</translation>
     </message>
@@ -785,17 +836,17 @@ Näiteks: https://server.minu:8787</translation>
         <translation>teema on eemaldatud</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation>%1 muutis jututoa tunnuspilti</translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 lõi ja seadistas jututoa: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 helistas.</translation>
     </message>
@@ -810,23 +861,23 @@ Näiteks: https://server.minu:8787</translation>
         <translation>%1 helistas.</translation>
     </message>
     <message>
-        <location line="+29"/>
+        <location line="+38"/>
         <source>Negotiating call...</source>
         <translation>Ühendan kõnet…</translation>
     </message>
     <message>
-        <location line="-18"/>
+        <location line="-24"/>
         <source>%1 answered the call.</source>
         <translation>%1 vastas kõnele.</translation>
     </message>
     <message>
-        <location line="-80"/>
+        <location line="-99"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>eemaldatud</translation>
     </message>
     <message>
-        <location line="+80"/>
+        <location line="+102"/>
         <source>%1 ended the call.</source>
         <translation>%1 lõpetas kõne.</translation>
     </message>
@@ -834,7 +885,7 @@ Näiteks: https://server.minu:8787</translation>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation>Lõpeta kõne</translation>
     </message>
@@ -855,6 +906,11 @@ Näiteks: https://server.minu:8787</translation>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>Emoji</translation>
     </message>
@@ -872,17 +928,17 @@ Näiteks: https://server.minu:8787</translation>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation>Muuda</translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation>Reageeri</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation>Vasta</translation>
     </message>
@@ -892,7 +948,7 @@ Näiteks: https://server.minu:8787</translation>
         <translation>Valikud</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation>&amp;Kopeeri</translation>
     </message>
@@ -1100,7 +1156,7 @@ Näiteks: https://server.minu:8787</translation>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>implementeerimata sündmus: </translation>
     </message>
@@ -1220,7 +1276,7 @@ Näiteks: https://server.minu:8787</translation>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Sulge</translation>
     </message>
@@ -1233,7 +1289,7 @@ Näiteks: https://server.minu:8787</translation>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>salvestatud versiooni ei leidu</translation>
     </message>
@@ -1241,7 +1297,7 @@ Näiteks: https://server.minu:8787</translation>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation>Uus silt</translation>
     </message>
@@ -1251,6 +1307,16 @@ Näiteks: https://server.minu:8787</translation>
         <translation>Kirjuta silt, mida soovid kasutada:</translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation>Lahku jututoast</translation>
@@ -1281,17 +1347,7 @@ Näiteks: https://server.minu:8787</translation>
         <translation>Loo uus silt...</translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation>Nõustu</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation>Keeldu</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation>Olekuteade</translation>
     </message>
@@ -1342,19 +1398,41 @@ Näiteks: https://server.minu:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation>Jututoa seadistused</translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation>%1 liige(t)</translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation>SEADISTUSED</translation>
     </message>
@@ -1410,21 +1488,22 @@ Näiteks: https://server.minu:8787</translation>
         <translation>Krüptimine on nhekos hetkel veel katseline ning nii mõndagi võib ootamatult katki minna. &lt;br&gt;Palun arvesta, et krüptimist ei saa hiljem enam välja lülitada.</translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
-        <translation>Vasta krüptovõtmete päringutele</translation>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
-        <translation>Kas klient peaks automaatselt vastama või mitte vastama sessioonivõtmete päringule.
-Kasuta seda võimalust ettevaatlikult. Tegemist on ajutise lahendusega läbiva krüptimise
-testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.</translation>
+        <location line="+4"/>
+        <source>Change</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation>TEAVE</translation>
     </message>
@@ -1439,12 +1518,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
         <translation>Jututoa versioon</translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation>Sobib</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation>Krüptimise kasutuselevõtmine ei õnnestunud: %1</translation>
     </message>
@@ -1476,6 +1550,24 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1532,7 +1624,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Ebaõnnestus</translation>
     </message>
@@ -1553,6 +1645,14 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">Otsi</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1573,7 +1673,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Sõnumi ümbersõnastamine ebaõnnestus: %1</translation>
     </message>
@@ -1584,7 +1684,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
         <translation>Sündmuse krüptimine ei õnnestunud, katkestame saatmise!</translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Salvesta pilt</translation>
     </message>
@@ -1718,12 +1818,12 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
         <translation>%1 muutis oma koputust jututoa uksele.</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Sa liitusid jututoaga.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation>%1 muutis oma tunnuspilti ja seadistas uueks kuvatavaks nimeks %2.</translation>
     </message>
@@ -1752,7 +1852,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation>Muudetud</translation>
     </message>
@@ -1760,17 +1860,32 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>Ühtegi jututuba pole avatud</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation>%1 liige(t)</translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation>Tagasi jututubade loendisse</translation>
     </message>
@@ -1778,7 +1893,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation>Ühtegi krüptitud vestlust selle kasutajaga ei leidunud. Palun loo temaga krüptitud vestlus ja proovi uuesti.</translation>
     </message>
@@ -1786,18 +1901,17 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation>Tagasi jututubade loendisse</translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation>Jututuba on valimata</translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation>Jututoa valikud</translation>
     </message>
@@ -1838,7 +1952,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation>Üldine kasutajaprofiil</translation>
     </message>
@@ -1848,7 +1962,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
         <translation>Kasutajaprofiil jututoas</translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation>Verifitseeri</translation>
@@ -1897,7 +2011,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation>Vaikimisi</translation>
@@ -1906,7 +2020,7 @@ testimiseks seni, kuni terviklik seadmete verifitseerimine on implementeeritud.<
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Vähenda tegumiribale</translation>
     </message>
@@ -2363,7 +2477,7 @@ See tavaliselt tähendab, et rakenduse ikoon tegumiribal annab mingit sorti anim
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation>Ootan teise osapoole tegevust…</translation>
     </message>
@@ -2414,7 +2528,7 @@ See tavaliselt tähendab, et rakenduse ikoon tegumiribal annab mingit sorti anim
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Eile</translation>
     </message>
@@ -2486,19 +2600,6 @@ See tavaliselt tähendab, et rakenduse ikoon tegumiribal annab mingit sorti anim
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Tühista</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>Kasutajatunnus, kellele soovid kutset saata</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2611,7 +2712,7 @@ Meedia suurus: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>Sa saatsid helifaili</translation>
     </message>
diff --git a/resources/langs/nheko_fi.ts b/resources/langs/nheko_fi.ts
index 9c48e98f..422c2957 100644
--- a/resources/langs/nheko_fi.ts
+++ b/resources/langs/nheko_fi.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Odotetaan vahvistusta</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Peruuta</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation type="unfinished"></translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Hae</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Ihmiset</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Sulje</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation>Valitse tiedosto</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation>Kaikki Tiedostot (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Poista</translation>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">Käyttäjätunnus kutsuttavaksi</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Peruuta</translation>
     </message>
 </context>
 <context>
@@ -741,22 +805,9 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Huoneen jäsenet</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
@@ -767,7 +818,7 @@ Example: https://server.my:8787</source>
         <translation>Salaus on käytössä</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>huoneen nimi muutettu: %1</translation>
     </message>
@@ -777,7 +828,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -787,17 +838,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -812,17 +863,17 @@ Example: https://server.my:8787</source>
         <translation>%1 soitti puhelun.</translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation>%1 vastasi puheluun.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>Emoji</translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation>Muokkaa</translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation>Reagoi</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation>Vastaa</translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation>Asetukset</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Sulje</translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>ei tallennettua versiota</translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation>Poistu huoneesta</translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation>Hyväksy</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation>Hylkää</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,41 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1484,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1514,7 @@ Example: https://server.my:8787</source>
         <translation>Huoneen versio</translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation>Salauksen aktivointi epäonnistui: %1</translation>
     </message>
@@ -1470,6 +1546,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1620,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1641,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">Hae</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1669,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Viestin muokkaus epäonnistui: %1</translation>
     </message>
@@ -1578,7 +1680,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Tallenna kuva</translation>
     </message>
@@ -1712,12 +1814,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Sinä liityit tähän huoneeseen.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1746,7 +1848,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation>Muokattu</translation>
     </message>
@@ -1754,17 +1856,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1772,7 +1889,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1780,18 +1897,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation>Huoneen asetukset</translation>
     </message>
@@ -1832,7 +1948,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1842,7 +1958,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1891,7 +2007,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1900,7 +2016,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Pienennä ilmoitusalueelle</translation>
     </message>
@@ -2346,7 +2462,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2397,7 +2513,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Eilen</translation>
     </message>
@@ -2469,19 +2585,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Peruuta</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>Käyttäjätunnus kutsuttavaksi</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2594,7 +2697,7 @@ Median koko: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>Lähetit äänileikkeen</translation>
     </message>
diff --git a/resources/langs/nheko_fr.ts b/resources/langs/nheko_fr.ts
index b6345d62..c6d42299 100644
--- a/resources/langs/nheko_fr.ts
+++ b/resources/langs/nheko_fr.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Attente de confirmation</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Attente de la vérification par le correspondant.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Annuler</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Échec lors de l&apos;invitation de %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Confirmer l&apos;invitation</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Voulez-vous vraiment inviter %1 (%2)&#x202f;?</translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>%1 n&apos;est plus banni(e)</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation>Voulez-vous vraimer commencer une discussion privée avec %1 ?</translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Échec de la migration du cache&#x202f;!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation>Déchiffrer les secrets</translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Chercher</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Personnes</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Fermer</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation>Sélectionnez un fichier</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation>Tous les types de fichiers (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation>Échec de l&apos;envoi du média. Veuillez réessayer.</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">Identifiant d&apos;utilisateur à inviter</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Retirer</translation>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Annuler</translation>
     </message>
 </context>
 <context>
@@ -745,22 +809,9 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Membres du salon</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>retiré</translation>
@@ -771,7 +822,7 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
         <translation>Chiffrement activé</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>nom du salon changé en&#xa0;: %1</translation>
     </message>
@@ -781,7 +832,7 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
         <translation>nom du salon retiré</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>sujet changé pour&#xa0;: %1</translation>
     </message>
@@ -791,17 +842,17 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
         <translation>sujet retiré</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 a créé et configuré le salon&#xa0;: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 a effectué un appel vocal.</translation>
     </message>
@@ -816,17 +867,17 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
         <translation>%1 a appelé.</translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation>%1 a répondu à l&apos;appel.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation>%1 a terminé l&apos;appel.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation>Négociation de l&apos;appel…</translation>
     </message>
@@ -834,7 +885,7 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation>Raccrocher</translation>
     </message>
@@ -855,6 +906,11 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>Émoji</translation>
     </message>
@@ -872,17 +928,17 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation>Modifier</translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation>Réagir</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation>Répondre</translation>
     </message>
@@ -892,7 +948,7 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
         <translation>Options</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1100,7 +1156,7 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>Évènement non implémenté&#xa0;: </translation>
     </message>
@@ -1220,7 +1276,7 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Fermer</translation>
     </message>
@@ -1233,7 +1289,7 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>pas de version enregistrée</translation>
     </message>
@@ -1241,7 +1297,7 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1251,6 +1307,16 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Quitter le salon</translation>
@@ -1281,17 +1347,7 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1342,19 +1398,41 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation>Configuration du salon</translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation>%1 membre(s)</translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation>CONFIGURATION</translation>
     </message>
@@ -1410,21 +1488,22 @@ Exemple&#xa0;: https&#x202f;://monserveur.example.com&#x202f;:8787</translation>
         <translation>Le chiffrement actuellement expérimental et des comportements inattendus peuvent être rencontrés. &lt;br&gt;Veuillez noter qu&apos;il n&apos;est pas possible de le désactiver par la suite.</translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
-        <translation>Répondre aux demandes de clé</translation>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
-        <translation>Si le client doit répondre automatiquement avec les clés de session lorsqu&apos;elles sont
-demandées. Utiliser précautionneusement, il s&apos;agit d&apos;une mesure temporaire afin de
-tester le chiffrement de bout en bout tant que la vérification des appareils n&apos;est pas au point.</translation>
+        <location line="+4"/>
+        <source>Change</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation>INFO</translation>
     </message>
@@ -1439,12 +1518,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
         <translation>Version du salon</translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation>Échec de l&apos;activation du chiffrement&#xa0;&#xa0;: %1</translation>
     </message>
@@ -1476,6 +1550,24 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1532,7 +1624,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Échec</translation>
     </message>
@@ -1553,6 +1645,14 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">Chercher</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1573,7 +1673,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Échec de la suppression du message&#xa0;: %1</translation>
     </message>
@@ -1584,7 +1684,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
         <translation>Échec du chiffrement de l&apos;évènement, envoi abandonné&#x202f;!</translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Enregistrer l&apos;image</translation>
     </message>
@@ -1718,12 +1818,12 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
         <translation>%1 ne frappe plus au salon.</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Vous avez rejoint ce salon.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1752,7 +1852,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation>Modifié</translation>
     </message>
@@ -1760,17 +1860,32 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>Aucun salon ouvert</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished">%1 membre(s)</translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished">Revenir à la liste des salons</translation>
     </message>
@@ -1778,7 +1893,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation>Pas de discussion privée et chiffrée trouvée avec cet utilisateur. Créez-en une et réessayez.</translation>
     </message>
@@ -1786,18 +1901,17 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation>Revenir à la liste des salons</translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation>Pas de salon sélectionné</translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation>Options du salon</translation>
     </message>
@@ -1838,7 +1952,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation>Profil général de l&apos;utilisateur</translation>
     </message>
@@ -1848,7 +1962,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
         <translation>Profil utilisateur spécifique au salon</translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation>Vérifier</translation>
@@ -1897,7 +2011,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation>Défaut</translation>
@@ -1906,7 +2020,7 @@ tester le chiffrement de bout en bout tant que la vérification des appareils n&
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Réduire à la barre des tâches</translation>
     </message>
@@ -2365,7 +2479,7 @@ Cela met l&apos;application en évidence dans la barre des tâches.</translation
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation>Attente du correspondant…</translation>
     </message>
@@ -2416,7 +2530,7 @@ Cela met l&apos;application en évidence dans la barre des tâches.</translation
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Hier</translation>
     </message>
@@ -2488,19 +2602,6 @@ Cela met l&apos;application en évidence dans la barre des tâches.</translation
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Annuler</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>Identifiant d&apos;utilisateur à inviter</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2613,7 +2714,7 @@ Taille du média : %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>Vous avez envoyé un message audio</translation>
     </message>
diff --git a/resources/langs/nheko_hu.ts b/resources/langs/nheko_hu.ts
index e989a6ce..7c29338c 100644
--- a/resources/langs/nheko_hu.ts
+++ b/resources/langs/nheko_hu.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Várakozás megerősítésre</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Várakozás a másik oldalra a hitelesítés befejezéséhez.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Mégse</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Nem sikerült meghívni a felhasználót: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Meghívás megerősítése</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Biztos, hogy meg akarod hívni a következő felhasználót: %1 (%2)?</translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Kitiltás feloldva a felhasználónak: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation>Biztosan privát csevegést akarsz indítani %1 felhasználóval?</translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Gyorsítótár migráció nem sikerült!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation>Titkos tároló feloldása</translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Keresés</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Emberek</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Bezárás</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation>Fájl kiválasztása</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation>Minden fájl (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation>Nem sikerült feltölteni a médiafájlt. Kérlek, próbáld újra!</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Eltávolítás</translation>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">Meghívandó felhasználó azonosítója</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Mégse</translation>
     </message>
 </context>
 <context>
@@ -745,27 +809,14 @@ Példa: https://szerver.em:8787</translation>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Szobatagok</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+128"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+187"/>
         <source>Encryption enabled</source>
         <translation>Titkosítás bekapcsolva</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>a szoba neve megváltoztatva erre: %1</translation>
     </message>
@@ -775,7 +826,7 @@ Példa: https://szerver.em:8787</translation>
         <translation>szobanév eltávolítva</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>a téma megváltoztatva erre: %1</translation>
     </message>
@@ -785,17 +836,17 @@ Példa: https://szerver.em:8787</translation>
         <translation>téma eltávolítva</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 létrehozta és beállította a következő szobát: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 hanghívást kezdeményezett.</translation>
     </message>
@@ -810,23 +861,23 @@ Példa: https://szerver.em:8787</translation>
         <translation>%1 hívást kezdeményezett.</translation>
     </message>
     <message>
-        <location line="+29"/>
+        <location line="+38"/>
         <source>Negotiating call...</source>
         <translation>Hívás előkészítése…</translation>
     </message>
     <message>
-        <location line="-18"/>
+        <location line="-24"/>
         <source>%1 answered the call.</source>
         <translation>%1 fogadta a hívást.</translation>
     </message>
     <message>
-        <location line="-80"/>
+        <location line="-99"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>eltávolítva</translation>
     </message>
     <message>
-        <location line="+80"/>
+        <location line="+102"/>
         <source>%1 ended the call.</source>
         <translation>%1 befejezte a hívást.</translation>
     </message>
@@ -834,7 +885,7 @@ Példa: https://szerver.em:8787</translation>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation>Hívás befejezése</translation>
     </message>
@@ -855,6 +906,11 @@ Példa: https://szerver.em:8787</translation>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>Hangulatjelek</translation>
     </message>
@@ -872,17 +928,17 @@ Példa: https://szerver.em:8787</translation>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation>Szerkesztés</translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation>Reakció</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation>Válasz</translation>
     </message>
@@ -892,7 +948,7 @@ Példa: https://szerver.em:8787</translation>
         <translation>Műveletek</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1100,7 +1156,7 @@ Példa: https://szerver.em:8787</translation>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>nem implementált esemény: </translation>
     </message>
@@ -1220,7 +1276,7 @@ Példa: https://szerver.em:8787</translation>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Bezárás</translation>
     </message>
@@ -1233,7 +1289,7 @@ Példa: https://szerver.em:8787</translation>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>nincs tárolva verzió</translation>
     </message>
@@ -1241,7 +1297,7 @@ Példa: https://szerver.em:8787</translation>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1251,6 +1307,16 @@ Példa: https://szerver.em:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Szoba elhagyása</translation>
@@ -1281,17 +1347,7 @@ Példa: https://szerver.em:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Elfogadás</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Elutasítás</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1342,19 +1398,40 @@ Példa: https://szerver.em:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation>Szobabeállítások</translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation>%1 tag</translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation>BEÁLLÍTÁSOK</translation>
     </message>
@@ -1410,21 +1487,22 @@ Példa: https://szerver.em:8787</translation>
         <translation>A titkosítás jelenleg kísérleti stádiumú és váratlan furcsaságok történhetnek.&lt;br&gt;Kérlek, vedd vigyelembe, hogy ha egyszer aktiváltad, nem lehet utána kikapcsolni.</translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
-        <translation>Válasz kulcskérelmekre</translation>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
-        <translation>Válaszoljon-e kérés esetén a kliens munkamenetkulcsokkal automatikusan.
-Óvatosan használandó, mivel ez csak egy átmeneti megoldás a végponttól
-végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközhitelesítés.</translation>
+        <location line="+4"/>
+        <source>Change</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation>INFÓ</translation>
     </message>
@@ -1439,12 +1517,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
         <translation>Szoba verziója</translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation>Nem sikerült a titkosítás aktiválása: %1</translation>
     </message>
@@ -1476,6 +1549,24 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1532,7 +1623,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Sikertelen</translation>
     </message>
@@ -1553,6 +1644,14 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">Keresés</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1573,7 +1672,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Az üzenet visszavonása nem sikerült: %1</translation>
     </message>
@@ -1584,7 +1683,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
         <translation>Nem sikerült titkosítani az eseményt, küldés megszakítva!</translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Kép mentése</translation>
     </message>
@@ -1717,12 +1816,12 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
         <translation>%1 visszavonta a kopogását.</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Csatlakoztál ehhez a szobához.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1751,7 +1850,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation>Szerkesztve</translation>
     </message>
@@ -1759,17 +1858,32 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>Nincs nyitott szoba</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished">%1 tag</translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished">Vissza a szobák listájára</translation>
     </message>
@@ -1777,7 +1891,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation>Nem található titkosított privát csevegés ezzel a felhasználóval. Hozz létre egy titkosított privát csevegést vele, és próbáld újra!</translation>
     </message>
@@ -1785,18 +1899,17 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation>Vissza a szobák listájára</translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation>Nincs kiválasztva szoba</translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation>Szoba beállításai</translation>
     </message>
@@ -1837,7 +1950,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation>Globális felhasználói profil</translation>
     </message>
@@ -1847,7 +1960,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
         <translation>Szobai felhasználói profil</translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation>Hitelesítés</translation>
@@ -1896,7 +2009,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation>Alapértelmezett</translation>
@@ -1905,7 +2018,7 @@ végpontig (E2E) titkosítás tesztelésére, amíg be nincs fejezve az eszközh
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Kicsinyítés a tálcára</translation>
     </message>
@@ -2363,7 +2476,7 @@ Ettől általában animálttá válik az alkalmazásablakok listáján szereplő
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation>Várakozás a másik félre…</translation>
     </message>
@@ -2414,7 +2527,7 @@ Ettől általában animálttá válik az alkalmazásablakok listáján szereplő
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Tegnap</translation>
     </message>
@@ -2486,19 +2599,6 @@ Ettől általában animálttá válik az alkalmazásablakok listáján szereplő
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Mégse</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>Meghívandó felhasználó azonosítója</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2611,7 +2711,7 @@ Média mérete: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>Küldtél egy hangfájlt</translation>
     </message>
diff --git a/resources/langs/nheko_it.ts b/resources/langs/nheko_it.ts
index b0b8ec48..5a1d45f8 100644
--- a/resources/langs/nheko_it.ts
+++ b/resources/langs/nheko_it.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>In attesa di conferma</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>In attesa della conferma dall&apos;altra parte per la verifica.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Annulla</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Impossibile invitare l&apos;utente: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Conferma Invito</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Vuoi davvero inviare %1 (%2)?</translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Rimosso il ban dall&apos;utente: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation>Sei sicuro di voler avviare una chat privata con %1?</translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Migrazione della cache fallita!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation>Decifra i segreti</translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Cerca</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Membri</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Chiudi</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished">Seleziona un file</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation type="unfinished">Tutti i File (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished">Impossibile inviare il file multimediale. Per favore riprova.</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">ID utente da invitare</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Rimuovi</translation>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Annulla</translation>
     </message>
 </context>
 <context>
@@ -745,22 +809,9 @@ Esempio: https://server.mio:8787</translation>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Membri della stanza</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>rimosso</translation>
@@ -771,7 +822,7 @@ Esempio: https://server.mio:8787</translation>
         <translation>Crittografia abilitata</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>nome della stanza cambiato in: %1</translation>
     </message>
@@ -781,7 +832,7 @@ Esempio: https://server.mio:8787</translation>
         <translation>nome della stanza rimosso</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>argomento cambiato in: %1</translation>
     </message>
@@ -791,17 +842,17 @@ Esempio: https://server.mio:8787</translation>
         <translation>argomento rimosso</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 creato e configurata stanza: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 ha avviato una chiamata audio.</translation>
     </message>
@@ -816,17 +867,17 @@ Esempio: https://server.mio:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation>%1 ha risposto alla chiamata.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation>%1 ha terminato la chiamata.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -834,7 +885,7 @@ Esempio: https://server.mio:8787</translation>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation>Termina</translation>
     </message>
@@ -855,6 +906,11 @@ Esempio: https://server.mio:8787</translation>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished">Emoji</translation>
     </message>
@@ -872,17 +928,17 @@ Esempio: https://server.mio:8787</translation>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation>Modifica</translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation>Reagisci</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished">Risposta</translation>
     </message>
@@ -892,7 +948,7 @@ Esempio: https://server.mio:8787</translation>
         <translation type="unfinished">Opzioni</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1101,7 +1157,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>evento non implementato: </translation>
     </message>
@@ -1221,7 +1277,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Chiudi</translation>
     </message>
@@ -1234,7 +1290,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>nessuna versione memorizzata</translation>
     </message>
@@ -1242,7 +1298,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1252,6 +1308,16 @@ Verificare %1 adesso?</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Lascia la stanza</translation>
@@ -1282,17 +1348,7 @@ Verificare %1 adesso?</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Accetta</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Rifiuta</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1343,19 +1399,41 @@ Verificare %1 adesso?</translation>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1411,19 +1489,22 @@ Verificare %1 adesso?</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1438,12 +1519,7 @@ Verificare %1 adesso?</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished">OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished">Impossibile abilitare la crittografia: %1</translation>
     </message>
@@ -1475,6 +1551,24 @@ Verificare %1 adesso?</translation>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1531,7 +1625,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Fallito</translation>
     </message>
@@ -1552,6 +1646,14 @@ Verificare %1 adesso?</translation>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">Cerca</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1572,7 +1674,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Oscuramento del messaggio fallito: %1</translation>
     </message>
@@ -1583,7 +1685,7 @@ Verificare %1 adesso?</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Salva immagine</translation>
     </message>
@@ -1717,12 +1819,12 @@ Verificare %1 adesso?</translation>
         <translation>%1 ha oscurato la sua bussata.</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Sei entrato in questa stanza.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1751,7 +1853,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1759,17 +1861,32 @@ Verificare %1 adesso?</translation>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>Nessuna stanza aperta</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1777,7 +1894,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1785,18 +1902,17 @@ Verificare %1 adesso?</translation>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished">Opzioni della stanza</translation>
     </message>
@@ -1837,7 +1953,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1847,7 +1963,7 @@ Verificare %1 adesso?</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1896,7 +2012,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1905,7 +2021,7 @@ Verificare %1 adesso?</translation>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Minimizza nella tray</translation>
     </message>
@@ -2351,7 +2467,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2402,7 +2518,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Ieri</translation>
     </message>
@@ -2474,19 +2590,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Annulla</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>ID utente da invitare</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2599,7 +2702,7 @@ Peso media: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>Hai inviato una clip audio</translation>
     </message>
diff --git a/resources/langs/nheko_ja.ts b/resources/langs/nheko_ja.ts
index c7872ce0..1ec862b0 100644
--- a/resources/langs/nheko_ja.ts
+++ b/resources/langs/nheko_ja.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation type="unfinished">キャンセル</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>ユーザーを招待できませんでした: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>永久追放を解除されたユーザー: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation type="unfinished"></translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">閉じる</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished">ファイルを選択</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation type="unfinished">全てのファイル (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished">メディアをアップロードできませんでした。やり直して下さい。</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">招待するユーザーのID</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>削除</translation>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">キャンセル</translation>
     </message>
 </context>
 <context>
@@ -741,22 +805,9 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>部屋の参加者</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
@@ -767,7 +818,7 @@ Example: https://server.my:8787</source>
         <translation>暗号化が有効です</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>部屋名が変更されました: %1</translation>
     </message>
@@ -777,7 +828,7 @@ Example: https://server.my:8787</source>
         <translation>部屋名が削除されました</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>話題が変更されました: %1</translation>
     </message>
@@ -787,17 +838,17 @@ Example: https://server.my:8787</source>
         <translation>話題が削除されました</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -812,17 +863,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished">絵文字</translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished">返信</translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished">オプション</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>未実装のイベント: </translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished">閉じる</translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>バージョンが保存されていません</translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">部屋を出る</translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">容認</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">拒否</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,40 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1483,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1513,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished">OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished">暗号化を有効にできませんでした: %1</translation>
     </message>
@@ -1470,6 +1545,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1619,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>失敗</translation>
     </message>
@@ -1547,6 +1640,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1668,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>メッセージを編集できませんでした: %1</translation>
     </message>
@@ -1578,7 +1679,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>画像を保存</translation>
     </message>
@@ -1711,12 +1812,12 @@ Example: https://server.my:8787</source>
         <translation>%1がノックを編集しました。</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1745,7 +1846,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1753,17 +1854,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>部屋が開いていません</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1771,7 +1887,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1779,18 +1895,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished">部屋のオプション</translation>
     </message>
@@ -1831,7 +1946,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1841,7 +1956,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1890,7 +2005,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1899,7 +2014,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>トレイへ最小化</translation>
     </message>
@@ -2345,7 +2460,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2396,7 +2511,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>昨日</translation>
     </message>
@@ -2468,19 +2583,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>キャンセル</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>招待するユーザーのID</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2593,7 +2695,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>音声データを送信しました</translation>
     </message>
diff --git a/resources/langs/nheko_ml.ts b/resources/langs/nheko_ml.ts
index 0bdf3b63..011107c2 100644
--- a/resources/langs/nheko_ml.ts
+++ b/resources/langs/nheko_ml.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>സ്ഥിരീകരണത്തിനായി കാത്തിരിക്കുന്നു</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>റദ്ദാക്കു</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>ഉപയോക്താവിനെ ക്ഷണിക്കുന്നതിൽ പരാജയപ്പെട്ടു: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>ക്ഷണം ഉറപ്പാക്കു</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation type="unfinished"></translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>തിരയുക</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>ആളുകൾ</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">അടയ്‌ക്കുക</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation>ഒരു ഫയൽ തിരഞ്ഞെടുക്കുക</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>നീക്കംചെയ്യുക</translation>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">റദ്ദാക്കു</translation>
     </message>
 </context>
 <context>
@@ -741,27 +805,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>ശരി</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+128"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+187"/>
         <source>Encryption enabled</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -771,7 +822,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -781,17 +832,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -806,23 +857,23 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+29"/>
+        <location line="+38"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-18"/>
+        <location line="-24"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-80"/>
+        <location line="-99"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>നീക്കംചെയ്‌തു</translation>
     </message>
     <message>
-        <location line="+80"/>
+        <location line="+102"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>ഇമോജി</translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished">അടയ്‌ക്കുക</translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished"></translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">നിരസിക്കുക</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,41 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1484,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1514,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished">ശരി</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1470,6 +1546,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1620,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1641,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">തിരയുക</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1669,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1578,7 +1680,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1712,12 +1814,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished">നിങ്ങൾ ഈ മുറിയിൽ ചേർന്നു.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1746,7 +1848,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1754,17 +1856,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1772,7 +1889,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1780,18 +1897,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1832,7 +1948,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1842,7 +1958,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1891,7 +2007,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1900,7 +2016,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2346,7 +2462,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2397,7 +2513,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2469,19 +2585,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation type="unfinished">റദ്ദാക്കു</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2592,7 +2695,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_nl.ts b/resources/langs/nheko_nl.ts
index 3f2a147f..b21db075 100644
--- a/resources/langs/nheko_nl.ts
+++ b/resources/langs/nheko_nl.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation type="unfinished">Annuleren</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Gebruiker uitnodigen mislukt: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation type="unfinished"></translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished">Kies een bestand</translation>
     </message>
@@ -617,18 +655,44 @@
         <translation type="unfinished">Alle bestanden (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">Uit te nodigen gebruikers-id</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
+        <location line="+58"/>
+        <source>Invite</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Annuleren</translation>
+    </message>
 </context>
 <context>
     <name>LoginPage</name>
@@ -741,22 +805,9 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Kamerleden</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
@@ -767,7 +818,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -777,7 +828,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -787,17 +838,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -812,17 +863,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished"></translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Kamer verlaten</translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Accepteren</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Afwijzen</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,41 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1484,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1514,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1470,6 +1546,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1620,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1641,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1669,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1578,7 +1680,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished">Afbeelding opslaan</translation>
     </message>
@@ -1712,12 +1814,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished">Je bent lid geworden van deze kamer.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1746,7 +1848,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1754,17 +1856,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1772,7 +1889,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1780,18 +1897,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1832,7 +1948,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1842,7 +1958,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1891,7 +2007,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1900,7 +2016,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Minimaliseren naar systeemvak</translation>
     </message>
@@ -2346,7 +2462,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2397,7 +2513,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2469,19 +2585,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation type="unfinished">Annuleren</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>Uit te nodigen gebruikers-id</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2594,7 +2697,7 @@ Mediagrootte: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_pl.ts b/resources/langs/nheko_pl.ts
index 6abcd147..72e4e771 100644
--- a/resources/langs/nheko_pl.ts
+++ b/resources/langs/nheko_pl.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Oczekiwanie na potwierdzenie</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Oczekiwanie na dokończenie weryfikacji przez drugą stronę.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Anuluj</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Nie udało się zaprosić użytkownika: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Czy na pewno chcesz zaprosić %1 (%2)?</translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Odblokowano użytkownika: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Nie udało się przenieść pamięci podręcznej!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Szukaj</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Ludzie</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Zamknij</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished">Wybierz plik</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation type="unfinished">Wszystkie pliki (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">ID użytkownika do zaproszenia</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Usuń</translation>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Anuluj</translation>
     </message>
 </context>
 <context>
@@ -743,22 +807,9 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Członkowie pokoju</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
@@ -769,7 +820,7 @@ Example: https://server.my:8787</source>
         <translation>Szyfrowanie włączone</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>Nazwa pokoju zmieniona na: %1</translation>
     </message>
@@ -779,7 +830,7 @@ Example: https://server.my:8787</source>
         <translation>usunięto nazwę pokoju</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>temat zmieniono na: %1</translation>
     </message>
@@ -789,17 +840,17 @@ Example: https://server.my:8787</source>
         <translation>usunięto temat</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 utworzył i skonfigurował pokój: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 rozpoczął(-ęła) połączenie głosowe.</translation>
     </message>
@@ -814,17 +865,17 @@ Example: https://server.my:8787</source>
         <translation>%1 rozpoczął(-ęła) połączenie.</translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation>%1 odebrał(a) połączenie.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation>%1 zakończył(a) połączenie.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation>Negocjowanie połączenia…</translation>
     </message>
@@ -832,7 +883,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -853,6 +904,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished">Emoji</translation>
     </message>
@@ -870,17 +926,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -890,7 +946,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1098,7 +1154,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>Niezaimplementowane wydarzenie: </translation>
     </message>
@@ -1218,7 +1274,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Zamknij</translation>
     </message>
@@ -1231,7 +1287,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1239,7 +1295,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1249,6 +1305,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Opuść pokój</translation>
@@ -1279,17 +1345,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Akceptuj</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Odrzuć</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1340,19 +1396,42 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1408,19 +1487,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1435,12 +1517,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished">OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished">Nie udało się włączyć szyfrowania: %1</translation>
     </message>
@@ -1472,6 +1549,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1528,7 +1623,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1549,6 +1644,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">Szukaj</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1569,7 +1672,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished">Redagowanie wiadomości nie powiodło się: %1</translation>
     </message>
@@ -1580,7 +1683,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished">Zapisz obraz</translation>
     </message>
@@ -1715,12 +1818,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished">Dołączyłeś(-łaś) do tego pokoju.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1749,7 +1852,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1757,17 +1860,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1775,7 +1893,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1783,18 +1901,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished">Ustawienia pokoju</translation>
     </message>
@@ -1835,7 +1952,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1845,7 +1962,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1894,7 +2011,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1903,7 +2020,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Zminimalizuj do paska zadań</translation>
     </message>
@@ -2349,7 +2466,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2400,7 +2517,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2472,19 +2589,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Anuluj</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>ID użytkownika do zaproszenia</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2597,7 +2701,7 @@ Rozmiar multimediów: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_pt_BR.ts b/resources/langs/nheko_pt_BR.ts
index fa0ea193..b83585fb 100644
--- a/resources/langs/nheko_pt_BR.ts
+++ b/resources/langs/nheko_pt_BR.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Esperando o outro lado completar a verificação.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Cancelar</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Falha ao convidar usuário: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Confirmar convite</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Usuário desbanido: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Migração do cache falhou!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished"></translation>
     </message>
@@ -617,18 +655,44 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Cancelar</translation>
+    </message>
 </context>
 <context>
     <name>LoginPage</name>
@@ -741,27 +805,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+128"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+187"/>
         <source>Encryption enabled</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -771,7 +822,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -781,17 +832,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -806,23 +857,23 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+29"/>
+        <location line="+38"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-18"/>
+        <location line="-24"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-80"/>
+        <location line="-99"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+80"/>
+        <location line="+102"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished"></translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished"></translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Aceitar</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Rejeitar</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,41 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1484,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1514,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1470,6 +1546,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1620,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1641,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1669,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1578,7 +1680,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1712,12 +1814,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished">Você entrou nessa sala.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1746,7 +1848,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1754,17 +1856,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1772,7 +1889,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1780,18 +1897,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1832,7 +1948,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1842,7 +1958,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1891,7 +2007,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1900,7 +2016,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2346,7 +2462,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2397,7 +2513,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2469,19 +2585,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation type="unfinished">Cancelar</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2592,7 +2695,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_pt_PT.ts b/resources/langs/nheko_pt_PT.ts
index 81343f97..a0a8c8a8 100644
--- a/resources/langs/nheko_pt_PT.ts
+++ b/resources/langs/nheko_pt_PT.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation type="unfinished"></translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation type="unfinished"></translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished"></translation>
     </message>
@@ -617,16 +655,42 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
@@ -741,27 +805,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+128"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+187"/>
         <source>Encryption enabled</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -771,7 +822,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -781,17 +832,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -806,23 +857,23 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+29"/>
+        <location line="+38"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-18"/>
+        <location line="-24"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-80"/>
+        <location line="-99"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+80"/>
+        <location line="+102"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished"></translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished"></translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,41 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1484,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1514,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1470,6 +1546,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1620,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1641,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1669,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1578,7 +1680,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1712,12 +1814,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1746,7 +1848,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1754,17 +1856,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1772,7 +1889,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1780,18 +1897,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1832,7 +1948,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1842,7 +1958,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1891,7 +2007,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1900,7 +2016,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2346,7 +2462,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2397,7 +2513,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2469,19 +2585,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2592,7 +2695,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_ro.ts b/resources/langs/nheko_ro.ts
index c21bb069..6ea496f1 100644
--- a/resources/langs/nheko_ro.ts
+++ b/resources/langs/nheko_ro.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation type="unfinished"></translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Nu s-a putut invita utilizatorul: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Utilizator dezinterzis: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Nu s-a putut migra cache-ul!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Închide</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished"></translation>
     </message>
@@ -617,17 +655,43 @@
         <translation type="unfinished">Toate fișierele (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">IDul utilizatorului de invitat</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Ștergere</translation>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
@@ -745,22 +809,9 @@ Exemplu: https://serverul.meu:8787</translation>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Membrii camerei</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
@@ -771,7 +822,7 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation>Criptare activată</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>numele camerei schimbat la: %1</translation>
     </message>
@@ -781,7 +832,7 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation>numele camerei șters</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>subiect schimbat la: %1</translation>
     </message>
@@ -791,17 +842,17 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation>subiect șters</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 a creat și configurat camera: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -816,17 +867,17 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation>%1 a răspuns apelului.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation>%1 a închis apelul.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -834,7 +885,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -855,6 +906,11 @@ Exemplu: https://serverul.meu:8787</translation>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished"></translation>
     </message>
@@ -872,17 +928,17 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished">Răspuns</translation>
     </message>
@@ -892,7 +948,7 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation type="unfinished">Opțiuni</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1100,7 +1156,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>eveniment neimplementat: </translation>
     </message>
@@ -1220,7 +1276,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished">Închide</translation>
     </message>
@@ -1233,7 +1289,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>nicio versiune stocată</translation>
     </message>
@@ -1241,7 +1297,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1251,6 +1307,16 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Părăsește camera</translation>
@@ -1281,17 +1347,7 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Acceptare</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Refuzare</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1342,19 +1398,42 @@ Exemplu: https://serverul.meu:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1410,19 +1489,22 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1437,12 +1519,7 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished">OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished">Nu s-a putut activa criptarea: %1</translation>
     </message>
@@ -1474,6 +1551,24 @@ Exemplu: https://serverul.meu:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1530,7 +1625,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Eșuat</translation>
     </message>
@@ -1551,6 +1646,14 @@ Exemplu: https://serverul.meu:8787</translation>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1571,7 +1674,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Redactare mesaj eșuată: %1</translation>
     </message>
@@ -1582,7 +1685,7 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Salvați imaginea</translation>
     </message>
@@ -1717,12 +1820,12 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation>%1 și-a redactat ciocănitul.</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Te-ai alăturat camerei.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1751,7 +1854,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1759,17 +1862,32 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>Nicio cameră deschisă</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1777,7 +1895,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1785,18 +1903,17 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1837,7 +1954,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1847,7 +1964,7 @@ Exemplu: https://serverul.meu:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1896,7 +2013,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1905,7 +2022,7 @@ Exemplu: https://serverul.meu:8787</translation>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Minimizează în bara de notificări</translation>
     </message>
@@ -2351,7 +2468,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2402,7 +2519,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Ieri</translation>
     </message>
@@ -2474,19 +2591,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Anulare</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>IDul utilizatorului de invitat</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2599,7 +2703,7 @@ Dimensiune media: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>Ai trimis un clip audio</translation>
     </message>
diff --git a/resources/langs/nheko_ru.ts b/resources/langs/nheko_ru.ts
index 6f2b19af..67a306f2 100644
--- a/resources/langs/nheko_ru.ts
+++ b/resources/langs/nheko_ru.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Ожидание Подтверждения</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Ожидание подтверждения у собеседника.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Отмена</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Не удалось пригласить пользователя: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Подтвердите приглашение</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Вы точно хотите пригласить %1 (%2)?</translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Разблокированный пользователь: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation>Вы действительно хотите начать личную переписку с %1?</translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Миграция кэша не удалась!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation>Расшифровать секреты</translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Поиск</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Люди</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Закрыть</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation>Выберите файл</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation>Все файлы (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation>Не удалось загрузить медиа. Пожалуйста попробуйте ещё раз</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Удалить</translation>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">Идентификатор пользователя</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
@@ -745,22 +809,9 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Участники комнаты</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>ОК</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>убрано</translation>
@@ -771,7 +822,7 @@ Example: https://server.my:8787</source>
         <translation>Шифрование включено</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>имя комнаты изменено на: %1</translation>
     </message>
@@ -781,7 +832,7 @@ Example: https://server.my:8787</source>
         <translation>название комнаты убрано</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>тема изменена на: %1</translation>
     </message>
@@ -791,17 +842,17 @@ Example: https://server.my:8787</source>
         <translation>тема убрана</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 создал и настроил комнату: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 начал голосовой звонок.</translation>
     </message>
@@ -816,17 +867,17 @@ Example: https://server.my:8787</source>
         <translation>%1 начал вызов.</translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation>%1 ответил на звонок.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation>%1 завершил вызов.</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation>Совершение звонка...</translation>
     </message>
@@ -834,7 +885,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation>Завершить звонок</translation>
     </message>
@@ -855,6 +906,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>Эмоджи</translation>
     </message>
@@ -872,17 +928,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation>Редактировать</translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation>Реакция</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation>Ответить</translation>
     </message>
@@ -892,7 +948,7 @@ Example: https://server.my:8787</source>
         <translation>Опции</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1100,7 +1156,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>не реализованное событие</translation>
     </message>
@@ -1220,7 +1276,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Закрыть</translation>
     </message>
@@ -1233,7 +1289,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>нет сохраненной версии</translation>
     </message>
@@ -1241,7 +1297,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1251,6 +1307,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Покинуть комнату</translation>
@@ -1281,17 +1347,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Принять</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">Отказаться</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1342,19 +1398,42 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation>Настройки комнаты</translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation>%1 участник(ов)</translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation>НАЙСТРОЙКИ</translation>
     </message>
@@ -1410,19 +1489,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
-        <translation>Отвечать на запрос ключей</translation>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation>ИНФОРМАЦИЯ</translation>
     </message>
@@ -1437,12 +1519,7 @@ Example: https://server.my:8787</source>
         <translation>Версия Комнаты</translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation>ОК</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation>Не удалось включить шифрование: %1</translation>
     </message>
@@ -1474,6 +1551,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1530,7 +1625,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Не удалоcь</translation>
     </message>
@@ -1551,6 +1646,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">Поиск</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1571,7 +1674,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Ошибка редактирования сообщения: %1</translation>
     </message>
@@ -1582,7 +1685,7 @@ Example: https://server.my:8787</source>
         <translation>Не удалось зашифровать сообщение, отправка отменена!</translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Сохранить изображение</translation>
     </message>
@@ -1717,12 +1820,12 @@ Example: https://server.my:8787</source>
         <translation>%1 отредактировал его &quot;стук&quot;.</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Вы присоединились к этой комнате.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1751,7 +1854,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation>Изменено</translation>
     </message>
@@ -1759,17 +1862,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>Комната не выбрана</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished">%1 участник(ов)</translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished">Вернуться к списку комнат</translation>
     </message>
@@ -1777,7 +1895,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation>Не найдено личного чата с этим пользователем. Создайте зашифрованный личный чат с этим пользователем и попытайтесь еще раз.</translation>
     </message>
@@ -1785,18 +1903,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation>Вернуться к списку комнат</translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation>Комнаты не выбраны</translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation>Настройки комнаты</translation>
     </message>
@@ -1837,7 +1954,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation>Глобальный Пользовательский Профиль</translation>
     </message>
@@ -1847,7 +1964,7 @@ Example: https://server.my:8787</source>
         <translation>Поользовательский Профиль в Комнате</translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation>Верифицировать</translation>
@@ -1896,7 +2013,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation>По умолчанию</translation>
@@ -1905,7 +2022,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Сворачивать в системную панель</translation>
     </message>
@@ -2357,7 +2474,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2408,7 +2525,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Вчера</translation>
     </message>
@@ -2480,19 +2597,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Отмена</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>Идентификатор пользователя</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2605,7 +2709,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>Вы отправили аудио запись</translation>
     </message>
diff --git a/resources/langs/nheko_si.ts b/resources/langs/nheko_si.ts
index a80adb1b..cf425990 100644
--- a/resources/langs/nheko_si.ts
+++ b/resources/langs/nheko_si.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation type="unfinished"></translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation type="unfinished"></translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished"></translation>
     </message>
@@ -617,16 +655,42 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
+        <location line="+7"/>
+        <source>Cancel</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
@@ -741,22 +805,9 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
@@ -767,7 +818,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -777,7 +828,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -787,17 +838,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -812,17 +863,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished"></translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished"></translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,41 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1484,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1514,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1470,6 +1546,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1620,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1641,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1669,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1578,7 +1680,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1712,12 +1814,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1746,7 +1848,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1754,17 +1856,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1772,7 +1889,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1780,18 +1897,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1832,7 +1948,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1842,7 +1958,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1891,7 +2007,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1900,7 +2016,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2346,7 +2462,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2397,7 +2513,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2469,19 +2585,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2592,7 +2695,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/langs/nheko_sv.ts b/resources/langs/nheko_sv.ts
index 8069dcea..25db1c4b 100644
--- a/resources/langs/nheko_sv.ts
+++ b/resources/langs/nheko_sv.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation>Inväntar Bekräftelse</translation>
     </message>
@@ -48,7 +48,7 @@
         <translation>Väntar på att motparten ska slutföra verifikationen.</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>Avbryt</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>Kunde inte bjuda in användare: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation>Bekräfta inbjudan</translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation>Är du säker på att du vill bjuda in %1 (%2)?</translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>Hävde bannlysningen av användare: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>Cache-migration misslyckades!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation>Dekryptera hemliga nycklar</translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation>Sök</translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation>Personer</translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished">Stäng</translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation>Välj en fil</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation>Alla Filer (*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation>Kunde inte ladda upp media. Vänligen försök igen.</translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>Ta bort</translation>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">Användar-ID att bjuda in</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">Avbryt</translation>
     </message>
 </context>
 <context>
@@ -745,27 +809,14 @@ Exempel: https://server.my:8787</translation>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>Rumsmedlemmar</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation>OK</translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+128"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+187"/>
         <source>Encryption enabled</source>
         <translation>Kryptering aktiverad</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation>rummets namn ändrat till: %1</translation>
     </message>
@@ -775,7 +826,7 @@ Exempel: https://server.my:8787</translation>
         <translation>tog bort rummets namn</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation>ämne ändrat till: %1</translation>
     </message>
@@ -785,17 +836,17 @@ Exempel: https://server.my:8787</translation>
         <translation>tog bort ämne</translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation>%1 skapade och konfigurerade rum: %2</translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation>%1 påbörjade ett röstsamtal.</translation>
     </message>
@@ -810,23 +861,23 @@ Exempel: https://server.my:8787</translation>
         <translation>%1 påbörjade ett samtal.</translation>
     </message>
     <message>
-        <location line="+29"/>
+        <location line="+38"/>
         <source>Negotiating call...</source>
         <translation>Förhandlar samtal…</translation>
     </message>
     <message>
-        <location line="-18"/>
+        <location line="-24"/>
         <source>%1 answered the call.</source>
         <translation>%1 besvarade samtalet.</translation>
     </message>
     <message>
-        <location line="-80"/>
+        <location line="-99"/>
         <location line="+9"/>
         <source>removed</source>
         <translation>borttagen</translation>
     </message>
     <message>
-        <location line="+80"/>
+        <location line="+102"/>
         <source>%1 ended the call.</source>
         <translation>%1 avslutade samtalet.</translation>
     </message>
@@ -834,7 +885,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation>Lägg på</translation>
     </message>
@@ -855,6 +906,11 @@ Exempel: https://server.my:8787</translation>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation>Emoji</translation>
     </message>
@@ -872,17 +928,17 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished">Reagera</translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished">Svara</translation>
     </message>
@@ -892,7 +948,7 @@ Exempel: https://server.my:8787</translation>
         <translation type="unfinished">Alternativ</translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1100,7 +1156,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation>ej implementerat event: </translation>
     </message>
@@ -1220,7 +1276,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation>Stäng</translation>
     </message>
@@ -1233,7 +1289,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation>ingen version lagrad</translation>
     </message>
@@ -1241,7 +1297,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1251,6 +1307,16 @@ Exempel: https://server.my:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">Lämna rum</translation>
@@ -1281,17 +1347,7 @@ Exempel: https://server.my:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">Godkänn</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1342,19 +1398,41 @@ Exempel: https://server.my:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1410,19 +1488,22 @@ Exempel: https://server.my:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1437,12 +1518,7 @@ Exempel: https://server.my:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished">OK</translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished">Kunde inte aktivera kryptering: %1</translation>
     </message>
@@ -1474,6 +1550,24 @@ Exempel: https://server.my:8787</translation>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1530,7 +1624,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation>Misslyckat</translation>
     </message>
@@ -1551,6 +1645,14 @@ Exempel: https://server.my:8787</translation>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished">Sök</translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1571,7 +1673,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation>Kunde inte maskera meddelande: %1</translation>
     </message>
@@ -1582,7 +1684,7 @@ Exempel: https://server.my:8787</translation>
         <translation>Kunde inte kryptera event, sändning avbruten!</translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation>Spara bild</translation>
     </message>
@@ -1716,12 +1818,12 @@ Exempel: https://server.my:8787</translation>
         <translation>%1 maskerade sin knackning.</translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation>Du gick med i detta rum.</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1750,7 +1852,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1758,17 +1860,32 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation>Inget rum öppet</translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished">Tillbaka till rumlista</translation>
     </message>
@@ -1776,7 +1893,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation>Ingen krypterad privat chatt med denna användare kunde hittas. Skapa en krypterad privat chatt med användaren och försök igen.</translation>
     </message>
@@ -1784,18 +1901,17 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation>Tillbaka till rumlista</translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation>Inget rum markerat</translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation>Alternativ för rum</translation>
     </message>
@@ -1836,7 +1952,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1846,7 +1962,7 @@ Exempel: https://server.my:8787</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation>Bekräfta</translation>
@@ -1895,7 +2011,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1904,7 +2020,7 @@ Exempel: https://server.my:8787</translation>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>Minimera till systemtråg</translation>
     </message>
@@ -2358,7 +2474,7 @@ Detta gör vanligtvis att ikonen i aktivitetsfältet animeras på något sätt.<
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation>Väntar på motparten…</translation>
     </message>
@@ -2409,7 +2525,7 @@ Detta gör vanligtvis att ikonen i aktivitetsfältet animeras på något sätt.<
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation>Igår</translation>
     </message>
@@ -2481,19 +2597,6 @@ Detta gör vanligtvis att ikonen i aktivitetsfältet animeras på något sätt.<
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>Avbryt</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>Användar-ID att bjuda in</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2606,7 +2709,7 @@ Mediastorlek: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation>Du skickade ett ljudklipp</translation>
     </message>
diff --git a/resources/langs/nheko_zh_CN.ts b/resources/langs/nheko_zh_CN.ts
index d468dfaa..75e9db95 100644
--- a/resources/langs/nheko_zh_CN.ts
+++ b/resources/langs/nheko_zh_CN.ts
@@ -38,7 +38,7 @@
 <context>
     <name>AwaitingVerificationConfirmation</name>
     <message>
-        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+11"/>
+        <location filename="../qml/device-verification/AwaitingVerificationConfirmation.qml" line="+12"/>
         <source>Awaiting Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
@@ -48,7 +48,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+13"/>
         <source>Cancel</source>
         <translation>取消</translation>
     </message>
@@ -125,7 +125,7 @@
 <context>
     <name>ChatPage</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+133"/>
+        <location filename="../../src/ChatPage.cpp" line="+135"/>
         <source>Failed to invite user: %1</source>
         <translation>邀请用户失败: %1</translation>
     </message>
@@ -157,12 +157,12 @@
     </message>
     <message>
         <location line="+34"/>
-        <location line="+280"/>
+        <location line="+286"/>
         <source>Confirm invite</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-279"/>
+        <location line="-285"/>
         <source>Do you really want to invite %1 (%2)?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -227,12 +227,12 @@
         <translation>解禁用户: %1</translation>
     </message>
     <message>
-        <location line="+183"/>
+        <location line="+189"/>
         <source>Do you really want to start a private chat with %1?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-721"/>
+        <location line="-727"/>
         <source>Cache migration failed!</source>
         <translation>缓存迁移失败!</translation>
     </message>
@@ -352,7 +352,7 @@
 <context>
     <name>CrossSigningSecrets</name>
     <message>
-        <location filename="../../src/ChatPage.cpp" line="+183"/>
+        <location filename="../../src/ChatPage.cpp" line="+189"/>
         <source>Decrypt secrets</source>
         <translation type="unfinished"></translation>
     </message>
@@ -426,12 +426,12 @@
 <context>
     <name>EmojiPicker</name>
     <message>
-        <location filename="../qml/emoji/EmojiPicker.qml" line="+59"/>
+        <location filename="../qml/emoji/EmojiPicker.qml" line="+68"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+172"/>
+        <location line="+186"/>
         <source>People</source>
         <translation type="unfinished"></translation>
     </message>
@@ -605,9 +605,47 @@
     </message>
 </context>
 <context>
+    <name>ImagePackSettingsDialog</name>
+    <message>
+        <location filename="../qml/dialogs/ImagePackSettingsDialog.qml" line="+22"/>
+        <source>Image pack settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+151"/>
+        <source>Private pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Pack from this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+2"/>
+        <source>Globally enabled pack</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+59"/>
+        <source>Enable globally</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+4"/>
+        <source>Enables this pack to be used in all rooms</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+62"/>
+        <source>Close</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>InputBar</name>
     <message>
-        <location filename="../../src/timeline/InputBar.cpp" line="+233"/>
+        <location filename="../../src/timeline/InputBar.cpp" line="+234"/>
         <source>Select a file</source>
         <translation type="unfinished">选择一个文件</translation>
     </message>
@@ -617,17 +655,43 @@
         <translation type="unfinished">所有文件(*)</translation>
     </message>
     <message>
-        <location line="+417"/>
+        <location line="+442"/>
         <source>Failed to upload media. Please try again.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
-    <name>InviteeItem</name>
+    <name>InviteDialog</name>
+    <message>
+        <location filename="../qml/InviteDialog.qml" line="+32"/>
+        <source>Invite users to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
+        <source>User ID to invite</source>
+        <translation type="unfinished">要邀请的用户 ID</translation>
+    </message>
+    <message>
+        <location line="+14"/>
+        <source>@joe:matrix.org</source>
+        <comment>Example user id. The name &apos;joe&apos; can be localized however you want.</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+17"/>
+        <source>Add</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
-        <location filename="../../src/InviteeItem.cpp" line="+22"/>
-        <source>Remove</source>
-        <translation>移除</translation>
+        <location line="+58"/>
+        <source>Invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>Cancel</source>
+        <translation type="unfinished">取消</translation>
     </message>
 </context>
 <context>
@@ -741,22 +805,9 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
-    <name>MemberList</name>
-    <message>
-        <location filename="../../src/dialogs/MemberList.cpp" line="+94"/>
-        <source>Room members</source>
-        <translation>聊天室成员</translation>
-    </message>
-    <message>
-        <location line="+4"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
     <name>MessageDelegate</name>
     <message>
-        <location filename="../qml/delegates/MessageDelegate.qml" line="+110"/>
+        <location filename="../qml/delegates/MessageDelegate.qml" line="+169"/>
         <location line="+9"/>
         <source>removed</source>
         <translation type="unfinished"></translation>
@@ -767,7 +818,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>room name changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -777,7 +828,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>topic changed to: %1</source>
         <translation type="unfinished"></translation>
     </message>
@@ -787,17 +838,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 changed the room avatar</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+8"/>
+        <location line="+12"/>
         <source>%1 created and configured room: %2</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
+        <location line="+15"/>
         <source>%1 placed a voice call.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -812,17 +863,17 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+11"/>
+        <location line="+14"/>
         <source>%1 answered the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>%1 ended the call.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+9"/>
+        <location line="+12"/>
         <source>Negotiating call...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -830,7 +881,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageInput</name>
     <message>
-        <location filename="../qml/MessageInput.qml" line="+43"/>
+        <location filename="../qml/MessageInput.qml" line="+44"/>
         <source>Hang up</source>
         <translation type="unfinished"></translation>
     </message>
@@ -851,6 +902,11 @@ Example: https://server.my:8787</source>
     </message>
     <message>
         <location line="+214"/>
+        <source>Stickers</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+24"/>
         <source>Emoji</source>
         <translation type="unfinished"></translation>
     </message>
@@ -868,17 +924,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>MessageView</name>
     <message>
-        <location filename="../qml/MessageView.qml" line="+83"/>
+        <location filename="../qml/MessageView.qml" line="+87"/>
         <source>Edit</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+15"/>
+        <location line="+16"/>
         <source>React</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
+        <location line="+16"/>
         <source>Reply</source>
         <translation type="unfinished"></translation>
     </message>
@@ -888,7 +944,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+329"/>
+        <location line="+405"/>
         <source>&amp;Copy</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1096,7 +1152,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>Placeholder</name>
     <message>
-        <location filename="../qml/delegates/Placeholder.qml" line="+9"/>
+        <location filename="../qml/delegates/Placeholder.qml" line="+11"/>
         <source>unimplemented event: </source>
         <translation type="unfinished"></translation>
     </message>
@@ -1216,7 +1272,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>ReplyPopup</name>
     <message>
-        <location filename="../qml/ReplyPopup.qml" line="+47"/>
+        <location filename="../qml/ReplyPopup.qml" line="+62"/>
         <source>Close</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1229,7 +1285,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomInfo</name>
     <message>
-        <location filename="../../src/Cache.cpp" line="+4009"/>
+        <location filename="../../src/Cache.cpp" line="+4180"/>
         <source>no version stored</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1237,7 +1293,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>RoomList</name>
     <message>
-        <location filename="../qml/RoomList.qml" line="+56"/>
+        <location filename="../qml/RoomList.qml" line="+57"/>
         <source>New tag</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1247,6 +1303,16 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
+        <location line="+9"/>
+        <source>Leave Room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+1"/>
+        <source>Are you sure you want to leave this room?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location line="+7"/>
         <source>Leave room</source>
         <translation type="unfinished">离开聊天室</translation>
@@ -1277,17 +1343,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+222"/>
-        <source>Accept</source>
-        <translation type="unfinished">接受</translation>
-    </message>
-    <message>
-        <location line="+21"/>
-        <source>Decline</source>
-        <translation type="unfinished">拒绝</translation>
-    </message>
-    <message>
-        <location line="+68"/>
+        <location line="+268"/>
         <source>Status Message</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1338,19 +1394,40 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomMembers</name>
+    <message>
+        <location filename="../qml/RoomMembers.qml" line="+17"/>
+        <source>Members of %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location line="+32"/>
+        <source>%n people in %1</source>
+        <comment>Summary above list of members</comment>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location line="+10"/>
+        <source>Invite more people</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>RoomSettings</name>
     <message>
-        <location filename="../qml/RoomSettings.qml" line="+25"/>
+        <location filename="../qml/RoomSettings.qml" line="+26"/>
         <source>Room Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+79"/>
+        <location line="+80"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+43"/>
+        <location line="+55"/>
         <source>SETTINGS</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1406,19 +1483,22 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+17"/>
-        <source>Respond to key requests</source>
+        <location line="+16"/>
+        <source>Sticker &amp; Emote Settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+5"/>
-        <source>Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.</source>
+        <location line="+4"/>
+        <source>Change</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+21"/>
+        <location line="+1"/>
+        <source>Change what packs are enabled, remove packs or create new ones</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+16"/>
         <source>INFO</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1433,12 +1513,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+13"/>
-        <source>OK</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../../src/ui/RoomSettings.cpp" line="+268"/>
+        <location filename="../../src/ui/RoomSettings.cpp" line="+255"/>
         <source>Failed to enable encryption: %1</source>
         <translation type="unfinished">启用加密失败:%1</translation>
     </message>
@@ -1470,6 +1545,24 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>RoomlistModel</name>
+    <message>
+        <location filename="../../src/timeline/RoomlistModel.cpp" line="+143"/>
+        <source>Pending invite.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+30"/>
+        <source>Previewing this room</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+34"/>
+        <source>No preview available</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ScreenShare</name>
     <message>
         <location filename="../qml/voip/ScreenShare.qml" line="+30"/>
@@ -1526,7 +1619,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>StatusIndicator</name>
     <message>
-        <location filename="../qml/StatusIndicator.qml" line="+21"/>
+        <location filename="../qml/StatusIndicator.qml" line="+24"/>
         <source>Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1547,6 +1640,14 @@ Example: https://server.my:8787</source>
     </message>
 </context>
 <context>
+    <name>StickerPicker</name>
+    <message>
+        <location filename="../qml/emoji/StickerPicker.qml" line="+70"/>
+        <source>Search</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>Success</name>
     <message>
         <location filename="../qml/device-verification/Success.qml" line="+11"/>
@@ -1567,7 +1668,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineModel</name>
     <message>
-        <location filename="../../src/timeline/TimelineModel.cpp" line="+1095"/>
+        <location filename="../../src/timeline/TimelineModel.cpp" line="+1107"/>
         <source>Message redaction failed: %1</source>
         <translation type="unfinished">删除消息失败:%1</translation>
     </message>
@@ -1578,7 +1679,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+164"/>
+        <location line="+173"/>
         <source>Save image</source>
         <translation type="unfinished">保存图像</translation>
     </message>
@@ -1711,12 +1812,12 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="-883"/>
+        <location line="-884"/>
         <source>You joined this room.</source>
         <translation type="unfinished">您已加入此房间</translation>
     </message>
     <message>
-        <location line="+849"/>
+        <location line="+850"/>
         <source>%1 has changed their avatar and changed their display name to %2.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1745,7 +1846,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineRow</name>
     <message>
-        <location filename="../qml/TimelineRow.qml" line="+106"/>
+        <location filename="../qml/TimelineRow.qml" line="+180"/>
         <source>Edited</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1753,17 +1854,32 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineView</name>
     <message>
-        <location filename="../qml/TimelineView.qml" line="+27"/>
+        <location filename="../qml/TimelineView.qml" line="+30"/>
         <source>No room open</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+127"/>
+        <location line="+139"/>
         <source>%1 member(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+46"/>
+        <location line="+33"/>
+        <source>join the conversation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>accept invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+7"/>
+        <source>decline invite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location line="+27"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1771,7 +1887,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>TimelineViewManager</name>
     <message>
-        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+461"/>
+        <location filename="../../src/timeline/TimelineViewManager.cpp" line="+527"/>
         <source>No encrypted private chat found with this user. Create an encrypted private chat with this user and try again.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1779,18 +1895,17 @@ Example: https://server.my:8787</source>
 <context>
     <name>TopBar</name>
     <message>
-        <location filename="../qml/TopBar.qml" line="+51"/>
+        <location filename="../qml/TopBar.qml" line="+54"/>
         <source>Back to room list</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+12"/>
-        <location line="+10"/>
+        <location line="-39"/>
         <source>No room selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+24"/>
+        <location line="+90"/>
         <source>Room options</source>
         <translation type="unfinished">聊天室选项</translation>
     </message>
@@ -1831,7 +1946,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserProfile</name>
     <message>
-        <location filename="../qml/UserProfile.qml" line="+24"/>
+        <location filename="../qml/UserProfile.qml" line="+25"/>
         <source>Global User Profile</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1841,7 +1956,7 @@ Example: https://server.my:8787</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location line="+114"/>
+        <location line="+115"/>
         <location line="+107"/>
         <source>Verify</source>
         <translation type="unfinished"></translation>
@@ -1890,7 +2005,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettings</name>
     <message>
-        <location filename="../../src/UserSettingsPage.cpp" line="+362"/>
+        <location filename="../../src/UserSettingsPage.cpp" line="+363"/>
         <location filename="../../src/UserSettingsPage.h" line="+194"/>
         <source>Default</source>
         <translation type="unfinished"></translation>
@@ -1899,7 +2014,7 @@ Example: https://server.my:8787</source>
 <context>
     <name>UserSettingsPage</name>
     <message>
-        <location line="+524"/>
+        <location line="+525"/>
         <source>Minimize to tray</source>
         <translation>最小化至托盘</translation>
     </message>
@@ -2345,7 +2460,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>Waiting</name>
     <message>
-        <location filename="../qml/device-verification/Waiting.qml" line="+11"/>
+        <location filename="../qml/device-verification/Waiting.qml" line="+12"/>
         <source>Waiting for other party…</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2396,7 +2511,7 @@ This usually causes the application icon in the task bar to animate in some fash
 <context>
     <name>descriptiveTime</name>
     <message>
-        <location filename="../../src/Utils.cpp" line="+207"/>
+        <location filename="../../src/Utils.cpp" line="+184"/>
         <source>Yesterday</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2468,19 +2583,6 @@ This usually causes the application icon in the task bar to animate in some fash
     </message>
 </context>
 <context>
-    <name>dialogs::InviteUsers</name>
-    <message>
-        <location filename="../../src/dialogs/InviteUsers.cpp" line="+46"/>
-        <source>Cancel</source>
-        <translation>取消</translation>
-    </message>
-    <message>
-        <location line="+8"/>
-        <source>User ID to invite</source>
-        <translation>要邀请的用户 ID</translation>
-    </message>
-</context>
-<context>
     <name>dialogs::JoinRoom</name>
     <message>
         <location filename="../../src/dialogs/JoinRoom.cpp" line="+34"/>
@@ -2593,7 +2695,7 @@ Media size: %2
 <context>
     <name>message-description sent:</name>
     <message>
-        <location filename="../../src/Utils.h" line="+124"/>
+        <location filename="../../src/Utils.h" line="+115"/>
         <source>You sent an audio clip</source>
         <translation type="unfinished"></translation>
     </message>
diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index 6c12952a..9685dde1 100644
--- a/resources/qml/Avatar.qml
+++ b/resources/qml/Avatar.qml
@@ -11,10 +11,11 @@ import im.nheko 1.0
 Rectangle {
     id: avatar
 
-    property alias url: img.source
+    property string url
     property string userid
     property string displayName
     property alias textColor: label.color
+    property bool crop: true
 
     signal clicked(var mouse)
 
@@ -44,12 +45,13 @@ Rectangle {
 
         anchors.fill: parent
         asynchronous: true
-        fillMode: Image.PreserveAspectCrop
+        fillMode: avatar.crop ? Image.PreserveAspectCrop : Image.PreserveAspectFit
         mipmap: true
         smooth: true
         sourceSize.width: avatar.width
         sourceSize.height: avatar.height
         layer.enabled: true
+        source: avatar.url + ((avatar.crop || !avatar.url) ? "" : "?scale")
 
         MouseArea {
             id: mouseArea
diff --git a/resources/qml/ChatPage.qml b/resources/qml/ChatPage.qml
index ae37187b..e56d7d46 100644
--- a/resources/qml/ChatPage.qml
+++ b/resources/qml/ChatPage.qml
@@ -56,6 +56,7 @@ Rectangle {
             RoomList {
                 id: roomlist
 
+                implicitHeight: chatPage.height
                 collapsed: parent.collapsed
             }
 
diff --git a/resources/qml/Completer.qml b/resources/qml/Completer.qml
index 333fb11d..00fc3216 100644
--- a/resources/qml/Completer.qml
+++ b/resources/qml/Completer.qml
@@ -70,7 +70,7 @@ Popup {
     onCompleterNameChanged: {
         if (completerName) {
             if (completerName == "user")
-                completer = TimelineManager.completerFor(completerName, room.roomId());
+                completer = TimelineManager.completerFor(completerName, room.roomId);
             else
                 completer = TimelineManager.completerFor(completerName);
             completer.setSearchString("");
diff --git a/resources/qml/ForwardCompleter.qml b/resources/qml/ForwardCompleter.qml
index 127b59c2..525477cd 100644
--- a/resources/qml/ForwardCompleter.qml
+++ b/resources/qml/ForwardCompleter.qml
@@ -109,15 +109,17 @@ Popup {
     }
 
     Connections {
-        onCompletionSelected: {
+        function onCompletionSelected(id) {
             room.forwardMessage(messageContextMenu.eventId, id);
             forwardMessagePopup.close();
         }
-        onCountChanged: {
+
+        function onCountChanged() {
             if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
                 completerPopup.currentIndex = 0;
 
         }
+
         target: completerPopup
     }
 
diff --git a/resources/qml/InviteDialog.qml b/resources/qml/InviteDialog.qml
new file mode 100644
index 00000000..2c0e15a7
--- /dev/null
+++ b/resources/qml/InviteDialog.qml
@@ -0,0 +1,159 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import im.nheko 1.0
+
+ApplicationWindow {
+    id: inviteDialogRoot
+
+    property string roomId
+    property string plainRoomName
+    property InviteesModel invitees
+
+    function addInvite() {
+        if (inviteeEntry.isValidMxid) {
+            invitees.addUser(inviteeEntry.text);
+            inviteeEntry.clear();
+        }
+    }
+
+    function cleanUpAndClose() {
+        if (inviteeEntry.isValidMxid)
+            addInvite();
+
+        invitees.accept();
+        close();
+    }
+
+    title: qsTr("Invite users to %1").arg(plainRoomName)
+    height: 380
+    width: 340
+    palette: Nheko.colors
+    color: Nheko.colors.window
+    flags: Qt.Dialog | Qt.WindowCloseButtonHint
+    Component.onCompleted: Nheko.reparent(inviteDialogRoot)
+
+    Shortcut {
+        sequence: "Ctrl+Enter"
+        onActivated: cleanUpAndClose()
+    }
+
+    Shortcut {
+        sequence: StandardKey.Cancel
+        onActivated: inviteDialogRoot.close()
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+        anchors.margins: Nheko.paddingMedium
+        spacing: Nheko.paddingMedium
+
+        Label {
+            text: qsTr("User ID to invite")
+            Layout.fillWidth: true
+            color: Nheko.colors.text
+        }
+
+        RowLayout {
+            spacing: Nheko.paddingMedium
+
+            MatrixTextField {
+                id: inviteeEntry
+
+                property bool isValidMxid: text.match("@.+?:.{3,}")
+
+                backgroundColor: Nheko.colors.window
+                placeholderText: qsTr("@joe:matrix.org", "Example user id. The name 'joe' can be localized however you want.")
+                Layout.fillWidth: true
+                onAccepted: {
+                    if (isValidMxid)
+                        addInvite();
+
+                }
+                Component.onCompleted: forceActiveFocus()
+                Keys.onShortcutOverride: event.accepted = ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers & Qt.ControlModifier))
+                Keys.onPressed: {
+                    if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && (event.modifiers === Qt.ControlModifier))
+                        cleanUpAndClose();
+
+                }
+            }
+
+            Button {
+                text: qsTr("Add")
+                enabled: inviteeEntry.isValidMxid
+                onClicked: addInvite()
+            }
+
+        }
+
+        ListView {
+            id: inviteesList
+
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+            model: invitees
+
+            delegate: RowLayout {
+                spacing: Nheko.paddingMedium
+
+                Avatar {
+                    width: Nheko.avatarSize
+                    height: Nheko.avatarSize
+                    userid: model.mxid
+                    url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
+                    displayName: model.displayName
+                    onClicked: TimelineManager.openGlobalUserProfile(model.mxid)
+                }
+
+                ColumnLayout {
+                    spacing: Nheko.paddingSmall
+
+                    Label {
+                        text: model.displayName
+                        color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window)
+                        font.pointSize: fontMetrics.font.pointSize
+                    }
+
+                    Label {
+                        text: model.mxid
+                        color: Nheko.colors.buttonText
+                        font.pointSize: fontMetrics.font.pointSize * 0.9
+                    }
+
+                    Item {
+                        Layout.fillHeight: true
+                        Layout.fillWidth: true
+                    }
+
+                }
+
+            }
+
+        }
+
+    }
+
+    footer: DialogButtonBox {
+        id: buttons
+
+        Button {
+            text: qsTr("Invite")
+            DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
+            enabled: invitees.count > 0
+            onClicked: cleanUpAndClose()
+        }
+
+        Button {
+            text: qsTr("Cancel")
+            DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
+            onClicked: inviteDialogRoot.close()
+        }
+
+    }
+
+}
diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml
index 35e5f7e7..7253cbe6 100644
--- a/resources/qml/MatrixText.qml
+++ b/resources/qml/MatrixText.qml
@@ -18,8 +18,8 @@ TextEdit {
     //enabled: selectByMouse
     color: Nheko.colors.text
     onLinkActivated: Nheko.openLink(link)
-    ToolTip.visible: hoveredLink
-    ToolTip.text: hoveredLink
+    ToolTip.visible: hoveredLink || false
+    ToolTip.text: hoveredLink || ""
     Component.onCompleted: {
         TimelineManager.fixImageRendering(r.textDocument, r);
     }
diff --git a/resources/qml/MatrixTextField.qml b/resources/qml/MatrixTextField.qml
index 3c660bac..80732b27 100644
--- a/resources/qml/MatrixTextField.qml
+++ b/resources/qml/MatrixTextField.qml
@@ -10,6 +10,8 @@ import im.nheko 1.0
 TextField {
     id: input
 
+    property alias backgroundColor: backgroundRect.color
+
     palette: Nheko.colors
     color: Nheko.colors.text
 
@@ -62,6 +64,8 @@ TextField {
     }
 
     background: Rectangle {
+        id: backgroundRect
+
         color: Nheko.colors.base
     }
 
diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml
index 415d67a7..7fb09684 100644
--- a/resources/qml/MessageInput.qml
+++ b/resources/qml/MessageInput.qml
@@ -7,7 +7,7 @@ import "./voip"
 import QtQuick 2.12
 import QtQuick.Controls 2.3
 import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
 import im.nheko 1.0
 
 Rectangle {
@@ -261,18 +261,24 @@ Rectangle {
                 background: null
 
                 Connections {
-                    onRoomChanged: {
+                    function onRoomChanged() {
                         messageInput.clear();
-                        messageInput.append(room.input.text());
+                        if (room)
+                            messageInput.append(room.input.text());
+
                         messageInput.completerTriggeredAt = -1;
                         popup.completerName = "";
                         messageInput.forceActiveFocus();
                     }
+
                     target: timelineView
                 }
 
                 Connections {
-                    onCompletionClicked: messageInput.insertCompletion(completion)
+                    function onCompletionClicked(completion) {
+                        messageInput.insertCompletion(completion);
+                    }
+
                     target: popup
                 }
 
@@ -284,28 +290,39 @@ Rectangle {
                 }
 
                 Connections {
-                    ignoreUnknownSignals: true
-                    onInsertText: {
+                    function onInsertText(text) {
                         messageInput.remove(messageInput.selectionStart, messageInput.selectionEnd);
                         messageInput.insert(messageInput.cursorPosition, text);
                     }
-                    onTextChanged: {
+
+                    function onTextChanged(newText) {
                         messageInput.text = newText;
                         messageInput.cursorPosition = newText.length;
                     }
+
+                    ignoreUnknownSignals: true
                     target: room ? room.input : null
                 }
 
                 Connections {
+                    function onReplyChanged() {
+                        messageInput.forceActiveFocus();
+                    }
+
+                    function onEditChanged() {
+                        messageInput.forceActiveFocus();
+                    }
+
                     ignoreUnknownSignals: true
-                    onReplyChanged: messageInput.forceActiveFocus()
-                    onEditChanged: messageInput.forceActiveFocus()
                     target: room
                 }
 
                 Connections {
+                    function onFocusInput() {
+                        messageInput.forceActiveFocus();
+                    }
+
                     target: TimelineManager
-                    onFocusInput: messageInput.forceActiveFocus()
                 }
 
                 MouseArea {
@@ -331,7 +348,7 @@ Rectangle {
             image: ":/icons/icons/ui/sticky-note-solid.svg"
             ToolTip.visible: hovered
             ToolTip.text: qsTr("Stickers")
-            onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId(), function(row) {
+            onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function(row) {
                 room.input.sticker(stickerPopup.model.sourceModel, row);
                 TimelineManager.focusMessageInput();
             })
@@ -355,7 +372,7 @@ Rectangle {
             image: ":/icons/icons/ui/smile.png"
             ToolTip.visible: hovered
             ToolTip.text: qsTr("Emoji")
-            onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(function(emoji) {
+            onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, function(emoji) {
                 messageInput.insert(messageInput.cursorPosition, emoji);
                 TimelineManager.focusMessageInput();
             })
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index f56af237..79cbd700 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -10,7 +10,7 @@ import QtGraphicalEffects 1.0
 import QtQuick 2.15
 import QtQuick.Controls 2.15
 import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
 import im.nheko 1.0
 
 ScrollView {
@@ -200,15 +200,22 @@ ScrollView {
         }
 
         Connections {
+            function onFocusChanged() {
+                readTimer.running = TimelineManager.isWindowFocused;
+            }
+
             target: TimelineManager
-            onFocusChanged: readTimer.running = TimelineManager.isWindowFocused
         }
 
         Timer {
             id: readTimer
 
             // force current read index to update
-            onTriggered: chat.model.setCurrentIndex(chat.model.currentIndex)
+            onTriggered: {
+                if (chat.model)
+                    chat.model.setCurrentIndex(chat.model.currentIndex);
+
+            }
             interval: 1000
         }
 
@@ -265,11 +272,15 @@ ScrollView {
                     }
 
                     Connections {
-                        target: chat.model
-                        onRoomAvatarUrlChanged: {
+                        function onRoomAvatarUrlChanged() {
                             messageUserAvatar.url = chat.model.avatarUrl(userId).replace("mxc://", "image://MxcImage/");
                         }
-                        onScrollToIndex: chat.positionViewAtIndex(index, ListView.Visible)
+
+                        function onScrollToIndex(index) {
+                            chat.positionViewAtIndex(index, ListView.Visible);
+                        }
+
+                        target: chat.model
                     }
 
                     Label {
@@ -338,9 +349,11 @@ ScrollView {
             required property string callType
             required property var reactions
             required property int trustlevel
+            required property int encryptionError
             required property var timestamp
             required property int status
             required property int index
+            required property int relatedEventCacheBuster
             required property string previousMessageUserId
             required property string day
             required property string previousMessageDay
@@ -444,8 +457,10 @@ ScrollView {
                 callType: wrapper.callType
                 reactions: wrapper.reactions
                 trustlevel: wrapper.trustlevel
+                encryptionError: wrapper.encryptionError
                 timestamp: wrapper.timestamp
                 status: wrapper.status
+                relatedEventCacheBuster: wrapper.relatedEventCacheBuster
                 y: section.visible && section.active ? section.y + section.height : 0
 
                 HoverHandler {
@@ -465,22 +480,34 @@ ScrollView {
             }
 
             Connections {
-                target: chat
-                onMovementEnded: {
+                function onMovementEnded() {
                     if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height)
                         chat.model.currentIndex = index;
 
                 }
+
+                target: chat
             }
 
         }
 
-        footer: Spinner {
+        footer: Item {
             anchors.horizontalCenter: parent.horizontalCenter
-            running: chat.model && chat.model.paginationInProgress
-            foreground: Nheko.colors.mid
+            anchors.margins: Nheko.paddingLarge
             visible: chat.model && chat.model.paginationInProgress
-            z: 3
+            // hacky, but works
+            height: loadingSpinner.height + 2 * Nheko.paddingLarge
+
+            Spinner {
+                id: loadingSpinner
+
+                anchors.centerIn: parent
+                anchors.margins: Nheko.paddingLarge
+                running: chat.model && chat.model.paginationInProgress
+                foreground: Nheko.colors.mid
+                z: 3
+            }
+
         }
 
     }
@@ -555,7 +582,7 @@ ScrollView {
 
         Platform.MenuItem {
             text: qsTr("Read receip&ts")
-            onTriggered: room.readReceiptsAction(messageContextMenu.eventId)
+            onTriggered: room.showReadReceipts(messageContextMenu.eventId)
         }
 
         Platform.MenuItem {
diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml
index ca73cfd7..5f18a1ce 100644
--- a/resources/qml/PrivacyScreen.qml
+++ b/resources/qml/PrivacyScreen.qml
@@ -13,8 +13,7 @@ Item {
     property int screenTimeout
 
     Connections {
-        target: TimelineManager
-        onFocusChanged: {
+        function onFocusChanged() {
             if (TimelineManager.isWindowFocused) {
                 screenSaverTimer.stop();
                 screenSaver.state = "Invisible";
@@ -24,6 +23,8 @@ Item {
 
             }
         }
+
+        target: TimelineManager
     }
 
     Timer {
diff --git a/resources/qml/QuickSwitcher.qml b/resources/qml/QuickSwitcher.qml
index 8c4f47ca..61155acf 100644
--- a/resources/qml/QuickSwitcher.qml
+++ b/resources/qml/QuickSwitcher.qml
@@ -71,15 +71,17 @@ Popup {
     }
 
     Connections {
-        onCompletionSelected: {
+        function onCompletionSelected(id) {
             Rooms.setCurrentRoom(id);
             quickSwitcher.close();
         }
-        onCountChanged: {
+
+        function onCountChanged() {
             if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
                 completerPopup.currentIndex = 0;
 
         }
+
         target: completerPopup
     }
 
diff --git a/resources/qml/RawMessageDialog.qml b/resources/qml/RawMessageDialog.qml
new file mode 100644
index 00000000..e2a476cd
--- /dev/null
+++ b/resources/qml/RawMessageDialog.qml
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import im.nheko 1.0
+
+ApplicationWindow {
+    id: rawMessageRoot
+
+    property alias rawMessage: rawMessageView.text
+
+    height: 420
+    width: 420
+    palette: Nheko.colors
+    color: Nheko.colors.window
+    flags: Qt.Tool | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint
+    Component.onCompleted: Nheko.reparent(rawMessageRoot)
+
+    Shortcut {
+        sequence: StandardKey.Cancel
+        onActivated: rawMessageRoot.close()
+    }
+
+    ScrollView {
+        anchors.margins: Nheko.paddingMedium
+        anchors.fill: parent
+        palette: Nheko.colors
+        padding: Nheko.paddingMedium
+
+        TextArea {
+            id: rawMessageView
+
+            font: Nheko.monospaceFont()
+            color: Nheko.colors.text
+            readOnly: true
+
+            background: Rectangle {
+                color: Nheko.colors.base
+            }
+
+        }
+
+    }
+
+    footer: DialogButtonBox {
+        standardButtons: DialogButtonBox.Ok
+        onAccepted: rawMessageRoot.close()
+    }
+
+}
diff --git a/resources/qml/ReadReceipts.qml b/resources/qml/ReadReceipts.qml
new file mode 100644
index 00000000..9adbfd5c
--- /dev/null
+++ b/resources/qml/ReadReceipts.qml
@@ -0,0 +1,130 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+import im.nheko 1.0
+
+ApplicationWindow {
+    id: readReceiptsRoot
+
+    property ReadReceiptsProxy readReceipts
+    property Room room
+
+    height: 380
+    width: 340
+    minimumHeight: 380
+    minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium
+    palette: Nheko.colors
+    color: Nheko.colors.window
+    flags: Qt.Dialog | Qt.WindowCloseButtonHint
+    Component.onCompleted: Nheko.reparent(readReceiptsRoot)
+
+    Shortcut {
+        sequence: StandardKey.Cancel
+        onActivated: readReceiptsRoot.close()
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+        anchors.margins: Nheko.paddingMedium
+        spacing: Nheko.paddingMedium
+
+        Label {
+            id: headerTitle
+
+            color: Nheko.colors.text
+            Layout.alignment: Qt.AlignCenter
+            text: qsTr("Read receipts")
+            font.pointSize: fontMetrics.font.pointSize * 1.5
+        }
+
+        ScrollView {
+            palette: Nheko.colors
+            padding: Nheko.paddingMedium
+            ScrollBar.horizontal.visible: false
+            Layout.fillHeight: true
+            Layout.minimumHeight: 200
+            Layout.fillWidth: true
+
+            ListView {
+                id: readReceiptsList
+
+                clip: true
+                spacing: Nheko.paddingMedium
+                boundsBehavior: Flickable.StopAtBounds
+                model: readReceipts
+
+                delegate: RowLayout {
+                    spacing: Nheko.paddingMedium
+
+                    Avatar {
+                        width: Nheko.avatarSize
+                        height: Nheko.avatarSize
+                        userid: model.mxid
+                        url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
+                        displayName: model.displayName
+                        onClicked: room.openUserProfile(model.mxid)
+                        ToolTip.visible: avatarHover.hovered
+                        ToolTip.text: model.mxid
+
+                        HoverHandler {
+                            id: avatarHover
+                        }
+
+                    }
+
+                    ColumnLayout {
+                        spacing: Nheko.paddingSmall
+
+                        Label {
+                            text: model.displayName
+                            color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window)
+                            font.pointSize: fontMetrics.font.pointSize
+                            ToolTip.visible: displayNameHover.hovered
+                            ToolTip.text: model.mxid
+
+                            TapHandler {
+                                onSingleTapped: room.openUserProfile(userId)
+                            }
+
+                            CursorShape {
+                                anchors.fill: parent
+                                cursorShape: Qt.PointingHandCursor
+                            }
+
+                            HoverHandler {
+                                id: displayNameHover
+                            }
+
+                        }
+
+                        Label {
+                            text: model.timestamp
+                            color: Nheko.colors.buttonText
+                            font.pointSize: fontMetrics.font.pointSize * 0.9
+                        }
+
+                        Item {
+                            Layout.fillHeight: true
+                            Layout.fillWidth: true
+                        }
+
+                    }
+
+                }
+
+            }
+
+        }
+
+    }
+
+    footer: DialogButtonBox {
+        standardButtons: DialogButtonBox.Ok
+        onAccepted: readReceiptsRoot.close()
+    }
+
+}
diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml
index a1ce8d7e..98532606 100644
--- a/resources/qml/RoomList.qml
+++ b/resources/qml/RoomList.qml
@@ -32,11 +32,12 @@ Page {
         }
 
         Connections {
-            onActiveTimelineChanged: {
-                roomlist.positionViewAtIndex(Rooms.roomidToIndex(Rooms.currentRoom.roomId()), ListView.Contain);
-                console.log("Test" + Rooms.currentRoom.roomId() + " " + Rooms.roomidToIndex(Rooms.currentRoom.roomId()));
+            function onCurrentRoomChanged() {
+                roomlist.positionViewAtIndex(Rooms.roomidToIndex(Rooms.currentRoom.roomId), ListView.Contain);
+                console.log("Test" + Rooms.currentRoom.roomId + " " + Rooms.roomidToIndex(Rooms.currentRoom.roomId));
             }
-            target: TimelineManager
+
+            target: Rooms
         }
 
         Platform.Menu {
@@ -61,9 +62,19 @@ Page {
                 }
             }
 
+            Platform.MessageDialog {
+                id: leaveRoomDialog
+
+                title: qsTr("Leave Room")
+                text: qsTr("Are you sure you want to leave this room?")
+                modality: Qt.ApplicationModal
+                onAccepted: Rooms.leave(roomContextMenu.roomid)
+                buttons: Dialog.Ok | Dialog.Cancel
+            }
+
             Platform.MenuItem {
                 text: qsTr("Leave room")
-                onTriggered: Rooms.leave(roomContextMenu.roomid)
+                onTriggered: leaveRoomDialog.open()
             }
 
             Platform.MenuSeparator {
@@ -133,7 +144,7 @@ Page {
             states: [
                 State {
                     name: "highlight"
-                    when: hovered.hovered && !((Rooms.currentRoom && roomId == Rooms.currentRoom.roomId()) || Rooms.currentRoomPreview.roomid == roomId)
+                    when: hovered.hovered && !((Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId)
 
                     PropertyChanges {
                         target: roomItem
@@ -147,7 +158,7 @@ Page {
                 },
                 State {
                     name: "selected"
-                    when: (Rooms.currentRoom && roomId == Rooms.currentRoom.roomId()) || Rooms.currentRoomPreview.roomid == roomId
+                    when: (Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId
 
                     PropertyChanges {
                         target: roomItem
@@ -161,31 +172,38 @@ Page {
                 }
             ]
 
-            TapHandler {
-                margin: -Nheko.paddingSmall
-                acceptedButtons: Qt.RightButton
-                onSingleTapped: {
-                    if (!TimelineManager.isInvite)
-                        roomContextMenu.show(roomId, tags);
+            // NOTE(Nico): We want to prevent the touch areas from overlapping. For some reason we need to add 1px of padding for that...
+            Item {
+                anchors.fill: parent
+                anchors.margins: 1
 
+                TapHandler {
+                    acceptedButtons: Qt.RightButton
+                    onSingleTapped: {
+                        if (!TimelineManager.isInvite)
+                            roomContextMenu.show(roomId, tags);
+
+                    }
+                    gesturePolicy: TapHandler.ReleaseWithinBounds
+                    acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
                 }
-                gesturePolicy: TapHandler.ReleaseWithinBounds
-            }
 
-            TapHandler {
-                margin: -Nheko.paddingSmall
-                onSingleTapped: Rooms.setCurrentRoom(roomId)
-                onLongPressed: {
-                    if (!isInvite)
-                        roomContextMenu.show(roomId, tags);
+                TapHandler {
+                    margin: -Nheko.paddingSmall
+                    onSingleTapped: Rooms.setCurrentRoom(roomId)
+                    onLongPressed: {
+                        if (!isInvite)
+                            roomContextMenu.show(roomId, tags);
 
+                    }
                 }
-            }
 
-            HoverHandler {
-                id: hovered
+                HoverHandler {
+                    id: hovered
+
+                    acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
+                }
 
-                margin: -Nheko.paddingSmall
             }
 
             RowLayout {
@@ -421,6 +439,7 @@ Page {
                     url: (userInfoGrid.profile ? userInfoGrid.profile.avatarUrl : "").replace("mxc://", "image://MxcImage/")
                     displayName: userInfoGrid.profile ? userInfoGrid.profile.displayName : ""
                     userid: userInfoGrid.profile ? userInfoGrid.profile.userid : ""
+                    enabled: false
                 }
 
                 ColumnLayout {
diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml
new file mode 100644
index 00000000..447e6fd1
--- /dev/null
+++ b/resources/qml/RoomMembers.qml
@@ -0,0 +1,148 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import "./ui"
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import QtQuick.Window 2.13
+import im.nheko 1.0
+
+ApplicationWindow {
+    id: roomMembersRoot
+
+    property MemberList members
+
+    title: qsTr("Members of %1").arg(members.roomName)
+    height: 650
+    width: 420
+    minimumHeight: 420
+    palette: Nheko.colors
+    color: Nheko.colors.window
+    flags: Qt.Dialog | Qt.WindowCloseButtonHint
+    Component.onCompleted: Nheko.reparent(roomMembersRoot)
+
+    Shortcut {
+        sequence: StandardKey.Cancel
+        onActivated: roomMembersRoot.close()
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+        anchors.margins: Nheko.paddingMedium
+        spacing: Nheko.paddingMedium
+
+        Avatar {
+            id: roomAvatar
+
+            width: 130
+            height: width
+            displayName: members.roomName
+            Layout.alignment: Qt.AlignHCenter
+            url: members.avatarUrl.replace("mxc://", "image://MxcImage/")
+            onClicked: TimelineManager.openRoomSettings(members.roomId)
+        }
+
+        ElidedLabel {
+            font.pixelSize: fontMetrics.font.pixelSize * 2
+            fullText: qsTr("%n people in %1", "Summary above list of members", members.memberCount).arg(members.roomName)
+            Layout.alignment: Qt.AlignHCenter
+            elideWidth: parent.width - Nheko.paddingMedium
+        }
+
+        ImageButton {
+            Layout.alignment: Qt.AlignHCenter
+            image: ":/icons/icons/ui/add-square-button.png"
+            hoverEnabled: true
+            ToolTip.visible: hovered
+            ToolTip.text: qsTr("Invite more people")
+            onClicked: TimelineManager.openInviteUsers(members.roomId)
+        }
+
+        ScrollView {
+            palette: Nheko.colors
+            padding: Nheko.paddingMedium
+            ScrollBar.horizontal.visible: false
+            Layout.fillHeight: true
+            Layout.minimumHeight: 200
+            Layout.fillWidth: true
+
+            ListView {
+                id: memberList
+
+                clip: true
+                spacing: Nheko.paddingMedium
+                boundsBehavior: Flickable.StopAtBounds
+                model: members
+
+                ScrollHelper {
+                    flickable: parent
+                    anchors.fill: parent
+                    enabled: !Settings.mobileMode
+                }
+
+                delegate: RowLayout {
+                    spacing: Nheko.paddingMedium
+
+                    Avatar {
+                        width: Nheko.avatarSize
+                        height: Nheko.avatarSize
+                        userid: model.mxid
+                        url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
+                        displayName: model.displayName
+                        onClicked: Rooms.currentRoom.openUserProfile(model.mxid)
+                    }
+
+                    ColumnLayout {
+                        spacing: Nheko.paddingSmall
+
+                        Label {
+                            text: model.displayName
+                            color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window)
+                            font.pointSize: fontMetrics.font.pointSize
+                        }
+
+                        Label {
+                            text: model.mxid
+                            color: Nheko.colors.buttonText
+                            font.pointSize: fontMetrics.font.pointSize * 0.9
+                        }
+
+                        Item {
+                            Layout.fillHeight: true
+                            Layout.fillWidth: true
+                        }
+
+                    }
+
+                }
+
+                footer: Item {
+                    width: parent.width
+                    visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers
+                    // use the default height if it's visible, otherwise no height at all
+                    height: membersLoadingSpinner.height
+                    anchors.margins: Nheko.paddingMedium
+
+                    Spinner {
+                        id: membersLoadingSpinner
+
+                        anchors.centerIn: parent
+                        height: visible ? 35 : 0
+                    }
+
+                }
+
+            }
+
+        }
+
+    }
+
+    footer: DialogButtonBox {
+        standardButtons: DialogButtonBox.Ok
+        onAccepted: roomMembersRoot.close()
+    }
+
+}
diff --git a/resources/qml/RoomSettings.qml b/resources/qml/RoomSettings.qml
index c852b837..69cf427c 100644
--- a/resources/qml/RoomSettings.qml
+++ b/resources/qml/RoomSettings.qml
@@ -4,10 +4,10 @@
 
 import "./ui"
 import Qt.labs.platform 1.1 as Platform
-import QtQuick 2.9
+import QtQuick 2.15
 import QtQuick.Controls 2.3
 import QtQuick.Layouts 1.2
-import QtQuick.Window 2.3
+import QtQuick.Window 2.13
 import im.nheko 1.0
 
 ApplicationWindow {
@@ -15,14 +15,13 @@ ApplicationWindow {
 
     property var roomSettings
 
-    x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
-    y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
     minimumWidth: 420
     minimumHeight: 650
     palette: Nheko.colors
     color: Nheko.colors.window
     modality: Qt.NonModal
-    flags: Qt.Dialog
+    flags: Qt.Dialog | Qt.WindowCloseButtonHint
+    Component.onCompleted: Nheko.reparent(roomSettingsDialog)
     title: qsTr("Room Settings")
 
     Shortcut {
@@ -98,13 +97,23 @@ ApplicationWindow {
 
             MatrixText {
                 text: roomSettings.roomName
-                font.pixelSize: 24
+                font.pixelSize: fontMetrics.font.pixelSize * 2
                 Layout.alignment: Qt.AlignHCenter
             }
 
             MatrixText {
                 text: qsTr("%1 member(s)").arg(roomSettings.memberCount)
                 Layout.alignment: Qt.AlignHCenter
+
+                TapHandler {
+                    onTapped: TimelineManager.openRoomMembers(roomSettings.roomId)
+                }
+
+                CursorShape {
+                    cursorShape: Qt.PointingHandCursor
+                    anchors.fill: parent
+                }
+
             }
 
         }
@@ -145,7 +154,7 @@ ApplicationWindow {
 
         GridLayout {
             columns: 2
-            rowSpacing: 10
+            rowSpacing: Nheko.paddingLarge
 
             MatrixText {
                 text: qsTr("SETTINGS")
@@ -171,7 +180,7 @@ ApplicationWindow {
             }
 
             MatrixText {
-                text: "Room access"
+                text: qsTr("Room access")
                 Layout.fillWidth: true
             }
 
@@ -209,7 +218,7 @@ ApplicationWindow {
                 title: qsTr("End-to-End Encryption")
                 text: qsTr("Encryption is currently experimental and things might break unexpectedly. <br>
                             Please take note that it can't be disabled afterwards.")
-                modality: Qt.NonModal
+                modality: Qt.Modal
                 onAccepted: {
                     if (roomSettings.isEncryptionEnabled)
                         return ;
@@ -223,19 +232,13 @@ ApplicationWindow {
             }
 
             MatrixText {
-                visible: roomSettings.isEncryptionEnabled
-                text: qsTr("Respond to key requests")
+                text: qsTr("Sticker & Emote Settings")
             }
 
-            ToggleButton {
-                visible: roomSettings.isEncryptionEnabled
-                ToolTip.text: qsTr("Whether or not the client should respond automatically with the session keys
-                                upon request. Use with caution, this is a temporary measure to test the
-                                E2E implementation until device verification is completed.")
-                checked: roomSettings.respondsToKeyRequests
-                onClicked: {
-                    roomSettings.changeKeyRequestsPreference(checked);
-                }
+            Button {
+                text: qsTr("Change")
+                ToolTip.text: qsTr("Change what packs are enabled, remove packs or create new ones")
+                onClicked: TimelineManager.openImagePackSettings(roomSettings.roomId)
                 Layout.alignment: Qt.AlignRight
             }
 
@@ -264,7 +267,7 @@ ApplicationWindow {
 
             MatrixText {
                 text: roomSettings.roomId
-                font.pixelSize: 14
+                font.pixelSize: fontMetrics.font.pixelSize * 1.2
                 Layout.alignment: Qt.AlignRight
             }
 
@@ -274,16 +277,16 @@ ApplicationWindow {
 
             MatrixText {
                 text: roomSettings.roomVersion
-                font.pixelSize: 14
+                font.pixelSize: fontMetrics.font.pixelSize * 1.2
                 Layout.alignment: Qt.AlignRight
             }
 
         }
 
-        Button {
-            Layout.alignment: Qt.AlignRight
-            text: qsTr("OK")
-            onClicked: close()
+        DialogButtonBox {
+            Layout.fillWidth: true
+            standardButtons: DialogButtonBox.Ok
+            onAccepted: close()
         }
 
     }
diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml
index 5316e20d..b229acda 100644
--- a/resources/qml/Root.qml
+++ b/resources/qml/Root.qml
@@ -4,14 +4,15 @@
 
 import "./delegates"
 import "./device-verification"
+import "./dialogs"
 import "./emoji"
 import "./voip"
 import Qt.labs.platform 1.1 as Platform
 import QtGraphicalEffects 1.0
-import QtQuick 2.9
-import QtQuick.Controls 2.5
+import QtQuick 2.15
+import QtQuick.Controls 2.15
 import QtQuick.Layouts 1.3
-import QtQuick.Window 2.2
+import QtQuick.Window 2.15
 import im.nheko 1.0
 import im.nheko.EmojiModel 1.0
 
@@ -48,6 +49,14 @@ Page {
     }
 
     Component {
+        id: roomMembersComponent
+
+        RoomMembers {
+        }
+
+    }
+
+    Component {
         id: mobileCallInviteDialog
 
         CallInvite {
@@ -63,6 +72,46 @@ Page {
 
     }
 
+    Component {
+        id: deviceVerificationDialog
+
+        DeviceVerification {
+        }
+
+    }
+
+    Component {
+        id: inviteDialog
+
+        InviteDialog {
+        }
+
+    }
+
+    Component {
+        id: packSettingsComponent
+
+        ImagePackSettingsDialog {
+        }
+
+    }
+
+    Component {
+        id: readReceiptsDialog
+
+        ReadReceipts {
+        }
+
+    }
+
+    Component {
+        id: rawMessageDialog
+
+        RawMessageDialog {
+        }
+
+    }
+
     Shortcut {
         sequence: "Ctrl+K"
         onActivated: {
@@ -82,38 +131,64 @@ Page {
         onActivated: Rooms.previousRoom()
     }
 
-    Component {
-        id: deviceVerificationDialog
-
-        DeviceVerification {
-        }
-
-    }
-
     Connections {
-        target: TimelineManager
-        onNewDeviceVerificationRequest: {
+        function onNewDeviceVerificationRequest(flow) {
             var dialog = deviceVerificationDialog.createObject(timelineRoot, {
                 "flow": flow
             });
             dialog.show();
         }
-        onOpenProfile: {
+
+        function onOpenProfile(profile) {
             var userProfile = userProfileComponent.createObject(timelineRoot, {
                 "profile": profile
             });
             userProfile.show();
         }
+
+        function onShowImagePackSettings(packlist) {
+            var packSet = packSettingsComponent.createObject(timelineRoot, {
+                "packlist": packlist
+            });
+            packSet.show();
+        }
+
+        function onOpenRoomMembersDialog(members) {
+            var membersDialog = roomMembersComponent.createObject(timelineRoot, {
+                "members": members,
+                "roomName": Rooms.currentRoom.roomName
+            });
+            membersDialog.show();
+        }
+
+        function onOpenRoomSettingsDialog(settings) {
+            var roomSettings = roomSettingsComponent.createObject(timelineRoot, {
+                "roomSettings": settings
+            });
+            roomSettings.show();
+        }
+
+        function onOpenInviteUsersDialog(invitees) {
+            var dialog = inviteDialog.createObject(timelineRoot, {
+                "roomId": Rooms.currentRoom.roomId,
+                "plainRoomName": Rooms.currentRoom.plainRoomName,
+                "invitees": invitees
+            });
+            dialog.show();
+        }
+
+        target: TimelineManager
     }
 
     Connections {
-        target: CallManager
-        onNewInviteState: {
+        function onNewInviteState() {
             if (CallManager.haveCallInvite && Settings.mobileMode) {
                 var dialog = mobileCallInviteDialog.createObject(msgView);
                 dialog.open();
             }
         }
+
+        target: CallManager
     }
 
     ChatPage {
diff --git a/resources/qml/ScrollHelper.qml b/resources/qml/ScrollHelper.qml
index 2dd56f27..e84e67fd 100644
--- a/resources/qml/ScrollHelper.qml
+++ b/resources/qml/ScrollHelper.qml
@@ -23,6 +23,9 @@ MouseArea {
     // console.warn("Delta: ", wheel.pixelDelta.y);
     // console.warn("Old position: ", flickable.contentY);
     // console.warn("New position: ", newPos);
+    // breaks ListView's with headers...
+    //if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
+    //    minYExtent += flickableItem.headerItem.height;
 
     id: root
 
@@ -55,9 +58,6 @@ MouseArea {
 
         var minYExtent = flickableItem.originY + flickableItem.topMargin;
         var maxYExtent = (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY) - flickableItem.height;
-        if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
-            minYExtent += flickableItem.headerItem.height;
-
         //Avoid overscrolling
         return Math.max(minYExtent, Math.min(maxYExtent, flickableItem.contentY - pixelDelta));
     }
diff --git a/resources/qml/StatusIndicator.qml b/resources/qml/StatusIndicator.qml
index 7e471d69..0af02b3c 100644
--- a/resources/qml/StatusIndicator.qml
+++ b/resources/qml/StatusIndicator.qml
@@ -34,7 +34,7 @@ ImageButton {
     }
     onClicked: {
         if (status == MtxEvent.Read)
-            room.readReceiptsAction(eventId);
+            room.showReadReceipts(eventId);
 
     }
     image: {
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 70db08e7..c612479a 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -7,7 +7,7 @@ import "./emoji"
 import QtQuick 2.12
 import QtQuick.Controls 2.3
 import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
 import im.nheko 1.0
 
 Item {
@@ -38,8 +38,10 @@ Item {
     required property string callType
     required property var reactions
     required property int trustlevel
+    required property int encryptionError
     required property var timestamp
     required property int status
+    required property int relatedEventCacheBuster
 
     anchors.left: parent.left
     anchors.right: parent.right
@@ -90,25 +92,27 @@ Item {
                 }
 
                 visible: replyTo
-                userColor: replyTo, TimelineManager.userColor(userId, Nheko.colors.base)
-                blurhash: replyTo, fromModel(Room.Blurhash) ?? ""
-                body: replyTo, fromModel(Room.Body) ?? ""
-                formattedBody: replyTo, fromModel(Room.FormattedBody) ?? ""
+                userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, Nheko.colors.base)
+                blurhash: r.relatedEventCacheBuster, fromModel(Room.Blurhash) ?? ""
+                body: r.relatedEventCacheBuster, fromModel(Room.Body) ?? ""
+                formattedBody: r.relatedEventCacheBuster, fromModel(Room.FormattedBody) ?? ""
                 eventId: fromModel(Room.EventId) ?? ""
-                filename: replyTo, fromModel(Room.Filename) ?? ""
-                filesize: replyTo, fromModel(Room.Filesize) ?? ""
-                proportionalHeight: replyTo, fromModel(Room.ProportionalHeight) ?? 1
-                type: replyTo, fromModel(Room.Type) ?? MtxEvent.UnknownMessage
-                typeString: replyTo, fromModel(Room.TypeString) ?? ""
-                url: replyTo, fromModel(Room.Url) ?? ""
-                originalWidth: replyTo, fromModel(Room.OriginalWidth) ?? 0
-                isOnlyEmoji: replyTo, fromModel(Room.IsOnlyEmoji) ?? false
-                userId: replyTo, fromModel(Room.UserId) ?? ""
-                userName: replyTo, fromModel(Room.UserName) ?? ""
-                thumbnailUrl: replyTo, fromModel(Room.ThumbnailUrl) ?? ""
-                roomTopic: replyTo, fromModel(Room.RoomTopic) ?? ""
-                roomName: replyTo, fromModel(Room.RoomName) ?? ""
-                callType: replyTo, fromModel(Room.CallType) ?? ""
+                filename: r.relatedEventCacheBuster, fromModel(Room.Filename) ?? ""
+                filesize: r.relatedEventCacheBuster, fromModel(Room.Filesize) ?? ""
+                proportionalHeight: r.relatedEventCacheBuster, fromModel(Room.ProportionalHeight) ?? 1
+                type: r.relatedEventCacheBuster, fromModel(Room.Type) ?? MtxEvent.UnknownMessage
+                typeString: r.relatedEventCacheBuster, fromModel(Room.TypeString) ?? ""
+                url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? ""
+                originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0
+                isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false
+                userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? ""
+                userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? ""
+                thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? ""
+                roomTopic: r.relatedEventCacheBuster, fromModel(Room.RoomTopic) ?? ""
+                roomName: r.relatedEventCacheBuster, fromModel(Room.RoomName) ?? ""
+                callType: r.relatedEventCacheBuster, fromModel(Room.CallType) ?? ""
+                encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? ""
+                relatedEventCacheBuster: r.relatedEventCacheBuster, fromModel(Room.RelatedEventCacheBuster) ?? 0
             }
 
             // actual message content
@@ -134,6 +138,8 @@ Item {
                 roomTopic: r.roomTopic
                 roomName: r.roomName
                 callType: r.callType
+                encryptionError: r.encryptionError
+                relatedEventCacheBuster: r.relatedEventCacheBuster
                 isReply: false
             }
 
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index 148a5817..6fc9d51b 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -13,7 +13,7 @@ import QtGraphicalEffects 1.0
 import QtQuick 2.9
 import QtQuick.Controls 2.5
 import QtQuick.Layouts 1.3
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
 import im.nheko 1.0
 import im.nheko.EmojiModel 1.0
 
@@ -246,17 +246,26 @@ Item {
 
     NhekoDropArea {
         anchors.fill: parent
-        roomid: room ? room.roomId() : ""
+        roomid: room ? room.roomId : ""
     }
 
     Connections {
-        target: room
-        onOpenRoomSettingsDialog: {
-            var roomSettings = roomSettingsComponent.createObject(timelineRoot, {
-                "roomSettings": settings
+        function onOpenReadReceiptsDialog(rr) {
+            var dialog = readReceiptsDialog.createObject(timelineRoot, {
+                "readReceipts": rr,
+                "room": room
+            });
+            dialog.show();
+        }
+
+        function onShowRawMessageDialog(rawMessage) {
+            var dialog = rawMessageDialog.createObject(timelineRoot, {
+                "rawMessage": rawMessage
             });
-            roomSettings.show();
+            dialog.show();
         }
+
+        target: room
     }
 
 }
diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml
index 58aba0c7..8543d02a 100644
--- a/resources/qml/TopBar.qml
+++ b/resources/qml/TopBar.qml
@@ -24,7 +24,7 @@ Rectangle {
     TapHandler {
         onSingleTapped: {
             if (room)
-                room.openRoomSettings();
+                TimelineManager.openRoomSettings(room.roomId);
 
             eventPoint.accepted = true;
         }
@@ -66,7 +66,7 @@ Rectangle {
             displayName: roomName
             onClicked: {
                 if (room)
-                    room.openRoomSettings();
+                    TimelineManager.openRoomSettings(room.roomId);
 
             }
         }
@@ -111,22 +111,22 @@ Rectangle {
                 Platform.MenuItem {
                     visible: room ? room.permissions.canInvite() : false
                     text: qsTr("Invite users")
-                    onTriggered: TimelineManager.openInviteUsersDialog()
+                    onTriggered: TimelineManager.openInviteUsers(room.roomId)
                 }
 
                 Platform.MenuItem {
                     text: qsTr("Members")
-                    onTriggered: TimelineManager.openMemberListDialog(room.roomId())
+                    onTriggered: TimelineManager.openRoomMembers(room.roomId)
                 }
 
                 Platform.MenuItem {
                     text: qsTr("Leave room")
-                    onTriggered: TimelineManager.openLeaveRoomDialog(room.roomId())
+                    onTriggered: TimelineManager.openLeaveRoomDialog(room.roomId)
                 }
 
                 Platform.MenuItem {
                     text: qsTr("Settings")
-                    onTriggered: room.openRoomSettings()
+                    onTriggered: TimelineManager.openRoomSettings(room.roomId)
                 }
 
             }
diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml
index 826d3165..767d2317 100644
--- a/resources/qml/UserProfile.qml
+++ b/resources/qml/UserProfile.qml
@@ -4,19 +4,20 @@
 
 import "./device-verification"
 import "./ui"
-import QtQuick 2.9
-import QtQuick.Controls 2.3
+import QtQuick 2.15
+import QtQuick.Controls 2.15
 import QtQuick.Layouts 1.2
-import QtQuick.Window 2.3
+import QtQuick.Window 2.13
 import im.nheko 1.0
 
 ApplicationWindow {
+    // this does not work in ApplicationWindow, just in Window
+    //transientParent: Nheko.mainwindow()
+
     id: userProfileDialog
 
     property var profile
 
-    x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
-    y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
     height: 650
     width: 420
     minimumHeight: 420
@@ -24,7 +25,8 @@ ApplicationWindow {
     color: Nheko.colors.window
     title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile")
     modality: Qt.NonModal
-    flags: Qt.Dialog
+    flags: Qt.Dialog | Qt.WindowCloseButtonHint
+    Component.onCompleted: Nheko.reparent(userProfileDialog)
 
     Shortcut {
         sequence: StandardKey.Cancel
@@ -83,12 +85,13 @@ ApplicationWindow {
         }
 
         Connections {
-            target: profile
-            onDisplayError: {
+            function onDisplayError(errorMessage) {
                 errorText.text = errorMessage;
                 errorText.opacity = 1;
                 hideErrorAnimation.restart();
             }
+
+            target: profile
         }
 
         TextInput {
diff --git a/resources/qml/components/AvatarListTile.qml b/resources/qml/components/AvatarListTile.qml
new file mode 100644
index 00000000..36c26a97
--- /dev/null
+++ b/resources/qml/components/AvatarListTile.qml
@@ -0,0 +1,133 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import ".."
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+import im.nheko 1.0
+
+Rectangle {
+    id: tile
+
+    property color background: Nheko.colors.window
+    property color importantText: Nheko.colors.text
+    property color unimportantText: Nheko.colors.buttonText
+    property color bubbleBackground: Nheko.colors.highlight
+    property color bubbleText: Nheko.colors.highlightedText
+    property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
+    required property string avatarUrl
+    required property string title
+    required property string subtitle
+    required property int index
+    required property int selectedIndex
+    property bool crop: true
+
+    color: background
+    height: avatarSize + 2 * Nheko.paddingMedium
+    width: ListView.view.width
+    state: "normal"
+    states: [
+        State {
+            name: "highlight"
+            when: hovered.hovered && !(index == selectedIndex)
+
+            PropertyChanges {
+                target: tile
+                background: Nheko.colors.dark
+                importantText: Nheko.colors.brightText
+                unimportantText: Nheko.colors.brightText
+                bubbleBackground: Nheko.colors.highlight
+                bubbleText: Nheko.colors.highlightedText
+            }
+
+        },
+        State {
+            name: "selected"
+            when: index == selectedIndex
+
+            PropertyChanges {
+                target: tile
+                background: Nheko.colors.highlight
+                importantText: Nheko.colors.highlightedText
+                unimportantText: Nheko.colors.highlightedText
+                bubbleBackground: Nheko.colors.highlightedText
+                bubbleText: Nheko.colors.highlight
+            }
+
+        }
+    ]
+
+    HoverHandler {
+        id: hovered
+    }
+
+    RowLayout {
+        spacing: Nheko.paddingMedium
+        anchors.fill: parent
+        anchors.margins: Nheko.paddingMedium
+
+        Avatar {
+            id: avatar
+
+            enabled: false
+            Layout.alignment: Qt.AlignVCenter
+            height: avatarSize
+            width: avatarSize
+            url: tile.avatarUrl.replace("mxc://", "image://MxcImage/")
+            displayName: title
+            crop: tile.crop
+        }
+
+        ColumnLayout {
+            id: textContent
+
+            Layout.alignment: Qt.AlignLeft
+            Layout.fillWidth: true
+            Layout.minimumWidth: 100
+            width: parent.width - avatar.width
+            Layout.preferredWidth: parent.width - avatar.width
+            spacing: Nheko.paddingSmall
+
+            RowLayout {
+                Layout.fillWidth: true
+                spacing: 0
+
+                ElidedLabel {
+                    Layout.alignment: Qt.AlignBottom
+                    color: tile.importantText
+                    elideWidth: textContent.width - Nheko.paddingMedium
+                    fullText: title
+                    textFormat: Text.PlainText
+                }
+
+                Item {
+                    Layout.fillWidth: true
+                }
+
+            }
+
+            RowLayout {
+                Layout.fillWidth: true
+                spacing: 0
+
+                ElidedLabel {
+                    color: tile.unimportantText
+                    font.pixelSize: fontMetrics.font.pixelSize * 0.9
+                    elideWidth: textContent.width - Nheko.paddingSmall
+                    fullText: subtitle
+                    textFormat: Text.PlainText
+                }
+
+                Item {
+                    Layout.fillWidth: true
+                }
+
+            }
+
+        }
+
+    }
+
+}
diff --git a/resources/qml/delegates/Encrypted.qml b/resources/qml/delegates/Encrypted.qml
new file mode 100644
index 00000000..cd00a9d4
--- /dev/null
+++ b/resources/qml/delegates/Encrypted.qml
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import ".."
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.2
+import im.nheko 1.0
+
+ColumnLayout {
+    id: r
+
+    required property int encryptionError
+    required property string eventId
+
+    width: parent ? parent.width : undefined
+
+    MatrixText {
+        text: {
+            switch (encryptionError) {
+            case Olm.MissingSession:
+                return qsTr("There is no key to unlock this message. We requested the key automatically, but you can try requesting it again if you are impatient.");
+            case Olm.MissingSessionIndex:
+                return qsTr("This message couldn't be decrypted, because we only have a key for newer messages. You can try requesting access to this message.");
+            case Olm.DbError:
+                return qsTr("There was an internal error reading the decryption key from the database.");
+            case Olm.DecryptionFailed:
+                return qsTr("There was an error decrypting this message.");
+            case Olm.ParsingFailed:
+                return qsTr("The message couldn't be parsed.");
+            case Olm.ReplayAttack:
+                return qsTr("The encryption key was reused! Someone is possibly trying to insert false messages into this chat!");
+            default:
+                return qsTr("Unknown decryption error");
+            }
+        }
+        color: Nheko.colors.buttonText
+        width: r ? r.width : undefined
+    }
+
+    Button {
+        palette: Nheko.colors
+        visible: encryptionError == Olm.MissingSession || encryptionError == Olm.MissingSessionIndex
+        text: qsTr("Request key")
+        onClicked: room.requestKeyForEvent(eventId)
+    }
+
+}
diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml
index 0b060629..a8bdf183 100644
--- a/resources/qml/delegates/MessageDelegate.qml
+++ b/resources/qml/delegates/MessageDelegate.qml
@@ -29,6 +29,8 @@ Item {
     required property string roomTopic
     required property string roomName
     required property string callType
+    required property int encryptionError
+    required property int relatedEventCacheBuster
 
     height: chooser.childrenRect.height
 
@@ -189,6 +191,16 @@ Item {
         }
 
         DelegateChoice {
+            roleValue: MtxEvent.Encrypted
+
+            Encrypted {
+                encryptionError: d.encryptionError
+                eventId: d.eventId
+            }
+
+        }
+
+        DelegateChoice {
             roleValue: MtxEvent.Name
 
             NoticeMessage {
@@ -231,7 +243,7 @@ Item {
                 body: formatted
                 isOnlyEmoji: false
                 isReply: d.isReply
-                formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId())
+                formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId)
             }
 
         }
@@ -301,7 +313,7 @@ Item {
                 body: formatted
                 isOnlyEmoji: false
                 isReply: d.isReply
-                formatted: room.formatPowerLevelEvent(d.eventId)
+                formatted: d.relatedEventCacheBuster, room.formatPowerLevelEvent(d.eventId)
             }
 
         }
@@ -313,7 +325,7 @@ Item {
                 body: formatted
                 isOnlyEmoji: false
                 isReply: d.isReply
-                formatted: room.formatJoinRuleEvent(d.eventId)
+                formatted: d.relatedEventCacheBuster, room.formatJoinRuleEvent(d.eventId)
             }
 
         }
@@ -325,7 +337,7 @@ Item {
                 body: formatted
                 isOnlyEmoji: false
                 isReply: d.isReply
-                formatted: room.formatHistoryVisibilityEvent(d.eventId)
+                formatted: d.relatedEventCacheBuster, room.formatHistoryVisibilityEvent(d.eventId)
             }
 
         }
@@ -337,7 +349,7 @@ Item {
                 body: formatted
                 isOnlyEmoji: false
                 isReply: d.isReply
-                formatted: room.formatGuestAccessEvent(d.eventId)
+                formatted: d.relatedEventCacheBuster, room.formatGuestAccessEvent(d.eventId)
             }
 
         }
@@ -349,7 +361,7 @@ Item {
                 body: formatted
                 isOnlyEmoji: false
                 isReply: d.isReply
-                formatted: room.formatMemberEvent(d.eventId)
+                formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId)
             }
 
         }
diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index 3face74d..aa7ee530 100644
--- a/resources/qml/delegates/PlayableMediaMessage.qml
+++ b/resources/qml/delegates/PlayableMediaMessage.qml
@@ -52,7 +52,7 @@ ColumnLayout {
 
         id: mediaCachedObserver
         target: room
-        onMediaCached: {
+        function onMediaCached(mxcUrl, cacheUrl) {
             if (mxcUrl == url) {
                 mediaCached = true
                 media.source = "file://" + cacheUrl
diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml
index 3a188d78..8bbce10e 100644
--- a/resources/qml/delegates/Reply.qml
+++ b/resources/qml/delegates/Reply.qml
@@ -5,7 +5,7 @@
 import QtQuick 2.12
 import QtQuick.Controls 2.3
 import QtQuick.Layouts 1.2
-import QtQuick.Window 2.2
+import QtQuick.Window 2.13
 import im.nheko 1.0
 
 Item {
@@ -30,6 +30,8 @@ Item {
     property string roomTopic
     property string roomName
     property string callType
+    property int encryptionError
+    property int relatedEventCacheBuster
 
     width: parent.width
     height: replyContainer.height
@@ -95,6 +97,8 @@ Item {
             roomTopic: r.roomTopic
             roomName: r.roomName
             callType: r.callType
+            relatedEventCacheBuster: r.relatedEventCacheBuster
+            encryptionError: r.encryptionError
             enabled: false
             width: parent.width
             isReply: true
diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml
index e2c66c5a..8e0271d6 100644
--- a/resources/qml/device-verification/DeviceVerification.qml
+++ b/resources/qml/device-verification/DeviceVerification.qml
@@ -4,7 +4,7 @@
 
 import QtQuick 2.10
 import QtQuick.Controls 2.3
-import QtQuick.Window 2.10
+import QtQuick.Window 2.13
 import im.nheko 1.0
 
 ApplicationWindow {
@@ -14,13 +14,12 @@ ApplicationWindow {
 
     onClosing: TimelineManager.removeVerificationFlow(flow)
     title: stack.currentItem.title
-    flags: Qt.Dialog
     modality: Qt.NonModal
     palette: Nheko.colors
     height: stack.implicitHeight
     width: stack.implicitWidth
-    x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
-    y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
+    flags: Qt.Dialog | Qt.WindowCloseButtonHint
+    Component.onCompleted: Nheko.reparent(dialog)
 
     StackView {
         id: stack
diff --git a/resources/qml/dialogs/ImagePackEditorDialog.qml b/resources/qml/dialogs/ImagePackEditorDialog.qml
new file mode 100644
index 00000000..b839c9e3
--- /dev/null
+++ b/resources/qml/dialogs/ImagePackEditorDialog.qml
@@ -0,0 +1,301 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import ".."
+import "../components"
+import Qt.labs.platform 1.1
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import im.nheko 1.0
+
+ApplicationWindow {
+    //Component.onCompleted: Nheko.reparent(win)
+
+    id: win
+
+    property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
+    property SingleImagePackModel imagePack
+    property int currentImageIndex: -1
+    readonly property int stickerDim: 128
+    readonly property int stickerDimPad: 128 + Nheko.paddingSmall
+
+    title: qsTr("Editing image pack")
+    height: 600
+    width: 600
+    palette: Nheko.colors
+    color: Nheko.colors.base
+    modality: Qt.WindowModal
+    flags: Qt.Dialog | Qt.WindowCloseButtonHint
+
+    AdaptiveLayout {
+        id: adaptiveView
+
+        anchors.fill: parent
+        singlePageMode: false
+        pageIndex: 0
+
+        AdaptiveLayoutElement {
+            id: packlistC
+
+            visible: Settings.groupView
+            minimumWidth: 200
+            collapsedWidth: 200
+            preferredWidth: 300
+            maximumWidth: 300
+            clip: true
+
+            ListView {
+                //required property bool isEmote
+                //required property bool isSticker
+
+                model: imagePack
+
+                ScrollHelper {
+                    flickable: parent
+                    anchors.fill: parent
+                    enabled: !Settings.mobileMode
+                }
+
+                header: AvatarListTile {
+                    title: imagePack.packname
+                    avatarUrl: imagePack.avatarUrl
+                    subtitle: imagePack.statekey
+                    index: -1
+                    selectedIndex: currentImageIndex
+
+                    TapHandler {
+                        onSingleTapped: currentImageIndex = -1
+                    }
+
+                    Rectangle {
+                        anchors.left: parent.left
+                        anchors.verticalCenter: parent.verticalCenter
+                        height: parent.height - Nheko.paddingSmall * 2
+                        width: 3
+                        color: Nheko.colors.highlight
+                    }
+
+                }
+
+                footer: Button {
+                    palette: Nheko.colors
+                    onClicked: addFilesDialog.open()
+                    width: ListView.view.width
+                    text: qsTr("Add images")
+
+                    FileDialog {
+                        id: addFilesDialog
+
+                        folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
+                        fileMode: FileDialog.OpenFiles
+                        nameFilters: [qsTr("Stickers (*.png *.webp)")]
+                        onAccepted: imagePack.addStickers(files)
+                    }
+
+                }
+
+                delegate: AvatarListTile {
+                    id: packItem
+
+                    property color background: Nheko.colors.window
+                    property color importantText: Nheko.colors.text
+                    property color unimportantText: Nheko.colors.buttonText
+                    property color bubbleBackground: Nheko.colors.highlight
+                    property color bubbleText: Nheko.colors.highlightedText
+                    required property string shortCode
+                    required property string url
+                    required property string body
+
+                    title: shortCode
+                    subtitle: body
+                    avatarUrl: url
+                    selectedIndex: currentImageIndex
+                    crop: false
+
+                    TapHandler {
+                        onSingleTapped: currentImageIndex = index
+                    }
+
+                }
+
+            }
+
+        }
+
+        AdaptiveLayoutElement {
+            id: packinfoC
+
+            Rectangle {
+                color: Nheko.colors.window
+
+                GridLayout {
+                    anchors.fill: parent
+                    anchors.margins: Nheko.paddingMedium
+                    visible: currentImageIndex == -1
+                    enabled: visible
+                    columns: 2
+                    rowSpacing: Nheko.paddingLarge
+
+                    Avatar {
+                        Layout.columnSpan: 2
+                        url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/")
+                        displayName: imagePack.packname
+                        height: 130
+                        width: 130
+                        crop: false
+                        Layout.alignment: Qt.AlignHCenter
+                    }
+
+                    MatrixText {
+                        visible: imagePack.roomid
+                        text: qsTr("State key")
+                    }
+
+                    MatrixTextField {
+                        visible: imagePack.roomid
+                        Layout.fillWidth: true
+                        text: imagePack.statekey
+                        onTextEdited: imagePack.statekey = text
+                    }
+
+                    MatrixText {
+                        text: qsTr("Packname")
+                    }
+
+                    MatrixTextField {
+                        Layout.fillWidth: true
+                        text: imagePack.packname
+                        onTextEdited: imagePack.packname = text
+                    }
+
+                    MatrixText {
+                        text: qsTr("Attrbution")
+                    }
+
+                    MatrixTextField {
+                        Layout.fillWidth: true
+                        text: imagePack.attribution
+                        onTextEdited: imagePack.attribution = text
+                    }
+
+                    MatrixText {
+                        text: qsTr("Use as Emoji")
+                    }
+
+                    ToggleButton {
+                        checked: imagePack.isEmotePack
+                        onClicked: imagePack.isEmotePack = checked
+                        Layout.alignment: Qt.AlignRight
+                    }
+
+                    MatrixText {
+                        text: qsTr("Use as Sticker")
+                    }
+
+                    ToggleButton {
+                        checked: imagePack.isStickerPack
+                        onClicked: imagePack.isStickerPack = checked
+                        Layout.alignment: Qt.AlignRight
+                    }
+
+                    Item {
+                        Layout.columnSpan: 2
+                        Layout.fillHeight: true
+                    }
+
+                }
+
+                GridLayout {
+                    anchors.fill: parent
+                    anchors.margins: Nheko.paddingMedium
+                    visible: currentImageIndex >= 0
+                    enabled: visible
+                    columns: 2
+                    rowSpacing: Nheko.paddingLarge
+
+                    Avatar {
+                        Layout.columnSpan: 2
+                        url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/")
+                        displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
+                        height: 130
+                        width: 130
+                        crop: false
+                        Layout.alignment: Qt.AlignHCenter
+                    }
+
+                    MatrixText {
+                        text: qsTr("Shortcode")
+                    }
+
+                    MatrixTextField {
+                        Layout.fillWidth: true
+                        text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode)
+                        onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.ShortCode)
+                    }
+
+                    MatrixText {
+                        text: qsTr("Body")
+                    }
+
+                    MatrixTextField {
+                        Layout.fillWidth: true
+                        text: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Body)
+                        onTextEdited: imagePack.setData(imagePack.index(currentImageIndex, 0), text, SingleImagePackModel.Body)
+                    }
+
+                    MatrixText {
+                        text: qsTr("Use as Emoji")
+                    }
+
+                    ToggleButton {
+                        checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsEmote)
+                        onClicked: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote)
+                        Layout.alignment: Qt.AlignRight
+                    }
+
+                    MatrixText {
+                        text: qsTr("Use as Sticker")
+                    }
+
+                    ToggleButton {
+                        checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsSticker)
+                        onClicked: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker)
+                        Layout.alignment: Qt.AlignRight
+                    }
+
+                    Item {
+                        Layout.columnSpan: 2
+                        Layout.fillHeight: true
+                    }
+
+                }
+
+            }
+
+        }
+
+    }
+
+    footer: DialogButtonBox {
+        id: buttons
+
+        Button {
+            text: qsTr("Cancel")
+            DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
+            onClicked: win.close()
+        }
+
+        Button {
+            text: qsTr("Save")
+            DialogButtonBox.buttonRole: DialogButtonBox.ApplyRole
+            onClicked: {
+                imagePack.save();
+                win.close();
+            }
+        }
+
+    }
+
+}
diff --git a/resources/qml/dialogs/ImagePackSettingsDialog.qml b/resources/qml/dialogs/ImagePackSettingsDialog.qml
new file mode 100644
index 00000000..5181619c
--- /dev/null
+++ b/resources/qml/dialogs/ImagePackSettingsDialog.qml
@@ -0,0 +1,260 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import ".."
+import "../components"
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import im.nheko 1.0
+
+ApplicationWindow {
+    id: win
+
+    property ImagePackListModel packlist
+    property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
+    property SingleImagePackModel currentPack: packlist.packAt(currentPackIndex)
+    property int currentPackIndex: 0
+    readonly property int stickerDim: 128
+    readonly property int stickerDimPad: 128 + Nheko.paddingSmall
+
+    title: qsTr("Image pack settings")
+    height: 600
+    width: 800
+    palette: Nheko.colors
+    color: Nheko.colors.base
+    modality: Qt.NonModal
+    flags: Qt.Dialog | Qt.WindowCloseButtonHint
+    Component.onCompleted: Nheko.reparent(win)
+
+    Component {
+        id: packEditor
+
+        ImagePackEditorDialog {
+        }
+
+    }
+
+    AdaptiveLayout {
+        id: adaptiveView
+
+        anchors.fill: parent
+        singlePageMode: false
+        pageIndex: 0
+
+        AdaptiveLayoutElement {
+            id: packlistC
+
+            visible: Settings.groupView
+            minimumWidth: 200
+            collapsedWidth: 200
+            preferredWidth: 300
+            maximumWidth: 300
+
+            ListView {
+                model: packlist
+                clip: true
+
+                ScrollHelper {
+                    flickable: parent
+                    anchors.fill: parent
+                    enabled: !Settings.mobileMode
+                }
+
+                footer: ColumnLayout {
+                    Button {
+                        palette: Nheko.colors
+                        onClicked: {
+                            var dialog = packEditor.createObject(timelineRoot, {
+                                "imagePack": packlist.newPack(false)
+                            });
+                            dialog.show();
+                        }
+                        width: packlist.width
+                        visible: !packlist.containsAccountPack
+                        text: qsTr("Create account pack")
+                    }
+
+                    Button {
+                        palette: Nheko.colors
+                        onClicked: {
+                            var dialog = packEditor.createObject(timelineRoot, {
+                                "imagePack": packlist.newPack(true)
+                            });
+                            dialog.show();
+                        }
+                        width: packlist.width
+                        text: qsTr("New room pack")
+                    }
+
+                }
+
+                delegate: AvatarListTile {
+                    id: packItem
+
+                    property color background: Nheko.colors.window
+                    property color importantText: Nheko.colors.text
+                    property color unimportantText: Nheko.colors.buttonText
+                    property color bubbleBackground: Nheko.colors.highlight
+                    property color bubbleText: Nheko.colors.highlightedText
+                    required property string displayName
+                    required property bool fromAccountData
+                    required property bool fromCurrentRoom
+
+                    title: displayName
+                    subtitle: {
+                        if (fromAccountData)
+                            return qsTr("Private pack");
+                        else if (fromCurrentRoom)
+                            return qsTr("Pack from this room");
+                        else
+                            return qsTr("Globally enabled pack");
+                    }
+                    selectedIndex: currentPackIndex
+
+                    TapHandler {
+                        onSingleTapped: currentPackIndex = index
+                    }
+
+                }
+
+            }
+
+        }
+
+        AdaptiveLayoutElement {
+            id: packinfoC
+
+            Rectangle {
+                color: Nheko.colors.window
+
+                ColumnLayout {
+                    id: packinfo
+
+                    property string packName: currentPack ? currentPack.packname : ""
+                    property string attribution: currentPack ? currentPack.attribution : ""
+                    property string avatarUrl: currentPack ? currentPack.avatarUrl : ""
+
+                    anchors.fill: parent
+                    anchors.margins: Nheko.paddingLarge
+                    spacing: Nheko.paddingLarge
+
+                    Avatar {
+                        url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/")
+                        displayName: packinfo.packName
+                        height: 100
+                        width: 100
+                        Layout.alignment: Qt.AlignHCenter
+                        enabled: false
+                    }
+
+                    MatrixText {
+                        text: packinfo.packName
+                        font.pixelSize: Math.ceil(fontMetrics.pixelSize * 1.1)
+                        horizontalAlignment: TextEdit.AlignHCenter
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
+                    }
+
+                    MatrixText {
+                        text: packinfo.attribution
+                        wrapMode: TextEdit.Wrap
+                        horizontalAlignment: TextEdit.AlignHCenter
+                        Layout.alignment: Qt.AlignHCenter
+                        Layout.preferredWidth: packinfoC.width - Nheko.paddingLarge * 2
+                    }
+
+                    GridLayout {
+                        Layout.alignment: Qt.AlignHCenter
+                        visible: currentPack && currentPack.roomid != ""
+                        columns: 2
+                        rowSpacing: Nheko.paddingMedium
+
+                        MatrixText {
+                            text: qsTr("Enable globally")
+                        }
+
+                        ToggleButton {
+                            ToolTip.text: qsTr("Enables this pack to be used in all rooms")
+                            checked: currentPack ? currentPack.isGloballyEnabled : false
+                            onClicked: currentPack.isGloballyEnabled = !currentPack.isGloballyEnabled
+                            Layout.alignment: Qt.AlignRight
+                        }
+
+                    }
+
+                    Button {
+                        Layout.alignment: Qt.AlignHCenter
+                        text: qsTr("Edit")
+                        enabled: currentPack.canEdit
+                        onClicked: {
+                            var dialog = packEditor.createObject(timelineRoot, {
+                                "imagePack": currentPack
+                            });
+                            dialog.show();
+                        }
+                    }
+
+                    GridView {
+                        Layout.fillHeight: true
+                        Layout.fillWidth: true
+                        model: currentPack
+                        cellWidth: stickerDimPad
+                        cellHeight: stickerDimPad
+                        boundsBehavior: Flickable.StopAtBounds
+                        clip: true
+                        currentIndex: -1 // prevent sorting from stealing focus
+                        cacheBuffer: 500
+
+                        ScrollHelper {
+                            flickable: parent
+                            anchors.fill: parent
+                            enabled: !Settings.mobileMode
+                        }
+
+                        // Individual emoji
+                        delegate: AbstractButton {
+                            width: stickerDim
+                            height: stickerDim
+                            hoverEnabled: true
+                            ToolTip.text: ":" + model.shortCode + ": - " + model.body
+                            ToolTip.visible: hovered
+
+                            contentItem: Image {
+                                height: stickerDim
+                                width: stickerDim
+                                source: model.url.replace("mxc://", "image://MxcImage/")
+                                fillMode: Image.PreserveAspectFit
+                            }
+
+                            background: Rectangle {
+                                anchors.fill: parent
+                                color: hovered ? Nheko.colors.highlight : 'transparent'
+                                radius: 5
+                            }
+
+                        }
+
+                    }
+
+                }
+
+            }
+
+        }
+
+    }
+
+    footer: DialogButtonBox {
+        id: buttons
+
+        Button {
+            text: qsTr("Close")
+            DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
+            onClicked: win.close()
+        }
+
+    }
+
+}
diff --git a/resources/qml/dialogs/InputDialog.qml b/resources/qml/dialogs/InputDialog.qml
index 134b78a3..e0f17851 100644
--- a/resources/qml/dialogs/InputDialog.qml
+++ b/resources/qml/dialogs/InputDialog.qml
@@ -16,6 +16,7 @@ ApplicationWindow {
 
     modality: Qt.NonModal
     flags: Qt.Dialog
+    Component.onCompleted: Nheko.reparent(inputDialog)
     width: 350
     height: fontMetrics.lineSpacing * 7
 
diff --git a/resources/qml/emoji/StickerPicker.qml b/resources/qml/emoji/StickerPicker.qml
index 813c0b12..3731a948 100644
--- a/resources/qml/emoji/StickerPicker.qml
+++ b/resources/qml/emoji/StickerPicker.qml
@@ -122,7 +122,6 @@ Menu {
                 id: gridView
 
                 model: roomid ? TimelineManager.completerFor("stickers", roomid) : null
-
                 Layout.preferredHeight: cellHeight * 3.5
                 Layout.preferredWidth: stickersPerRow * stickerDimPad + 20
                 Layout.leftMargin: 4
diff --git a/resources/qml/voip/CallInvite.qml b/resources/qml/voip/CallInvite.qml
index 1b57976d..253fa25c 100644
--- a/resources/qml/voip/CallInvite.qml
+++ b/resources/qml/voip/CallInvite.qml
@@ -23,12 +23,13 @@ Popup {
     }
 
     Connections {
-        target: CallManager
-        onNewInviteState: {
+        function onNewInviteState() {
             if (!CallManager.haveCallInvite)
                 close();
 
         }
+
+        target: CallManager
     }
 
     ColumnLayout {
diff --git a/resources/qml/voip/PlaceCall.qml b/resources/qml/voip/PlaceCall.qml
index 5f564853..97932cc9 100644
--- a/resources/qml/voip/PlaceCall.qml
+++ b/resources/qml/voip/PlaceCall.qml
@@ -88,7 +88,7 @@ Popup {
                 onClicked: {
                     if (buttonLayout.validateMic()) {
                         Settings.microphone = micCombo.currentText;
-                        CallManager.sendInvite(room.roomId(), CallType.VOICE);
+                        CallManager.sendInvite(room.roomId, CallType.VOICE);
                         close();
                     }
                 }
@@ -102,7 +102,7 @@ Popup {
                     if (buttonLayout.validateMic()) {
                         Settings.microphone = micCombo.currentText;
                         Settings.camera = cameraCombo.currentText;
-                        CallManager.sendInvite(room.roomId(), CallType.VIDEO);
+                        CallManager.sendInvite(room.roomId, CallType.VIDEO);
                         close();
                     }
                 }
diff --git a/resources/qml/voip/ScreenShare.qml b/resources/qml/voip/ScreenShare.qml
index a10057b2..8cd43b1c 100644
--- a/resources/qml/voip/ScreenShare.qml
+++ b/resources/qml/voip/ScreenShare.qml
@@ -136,7 +136,7 @@ Popup {
                         Settings.screenSharePiP = pipCheckBox.checked;
                         Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked;
                         Settings.screenShareHideCursor = hideCursorCheckBox.checked;
-                        CallManager.sendInvite(room.roomId(), CallType.SCREEN, windowCombo.currentIndex);
+                        CallManager.sendInvite(room.roomId, CallType.SCREEN, windowCombo.currentIndex);
                         close();
                     }
                 }
diff --git a/resources/res.qrc b/resources/res.qrc
index d2024eda..e1761cc0 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -10,7 +10,6 @@
         <file>icons/ui/do-not-disturb-rounded-sign@2x.png</file>
         <file>icons/ui/round-remove-button.png</file>
         <file>icons/ui/round-remove-button@2x.png</file>
-
         <file>icons/ui/double-tick-indicator.png</file>
         <file>icons/ui/double-tick-indicator@2x.png</file>
         <file>icons/ui/lock.png</file>
@@ -56,22 +55,17 @@
         <file>icons/ui/pause-symbol@2x.png</file>
         <file>icons/ui/remove-symbol.png</file>
         <file>icons/ui/remove-symbol@2x.png</file>
-
         <file>icons/ui/world.png</file>
         <file>icons/ui/world@2x.png</file>
-
         <file>icons/ui/tag.png</file>
         <file>icons/ui/tag@2x.png</file>
         <file>icons/ui/star.png</file>
         <file>icons/ui/star@2x.png</file>
         <file>icons/ui/lowprio.png</file>
         <file>icons/ui/lowprio@2x.png</file>
-
         <file>icons/ui/edit.png</file>
         <file>icons/ui/edit@2x.png</file>
-    
         <file>icons/ui/mail-reply.png</file>
-
         <file>icons/ui/place-call.png</file>
         <file>icons/ui/end-call.png</file>
         <file>icons/ui/microphone-mute.png</file>
@@ -79,7 +73,6 @@
         <file>icons/ui/screen-share.png</file>
         <file>icons/ui/toggle-camera-view.png</file>
         <file>icons/ui/video-call.png</file>
-
         <file>icons/emoji-categories/people.png</file>
         <file>icons/emoji-categories/people@2x.png</file>
         <file>icons/emoji-categories/nature.png</file>
@@ -100,16 +93,12 @@
     <qresource prefix="/logos">
         <file>nheko.png</file>
         <file>nheko.svg</file>
-
         <file>splash.png</file>
         <file>splash@2x.png</file>
-
         <file>register.png</file>
         <file>register@2x.png</file>
-
         <file>login.png</file>
         <file>login@2x.png</file>
-
         <file>nheko-512.png</file>
         <file>nheko-256.png</file>
         <file>nheko-128.png</file>
@@ -124,7 +113,6 @@
     </qresource>
     <qresource prefix="/">
         <file>qtquickcontrols2.conf</file>
-
         <file>qml/Root.qml</file>
         <file>qml/ChatPage.qml</file>
         <file>qml/CommunitiesList.qml</file>
@@ -156,14 +144,15 @@
         <file>qml/emoji/StickerPicker.qml</file>
         <file>qml/UserProfile.qml</file>
         <file>qml/delegates/MessageDelegate.qml</file>
-        <file>qml/delegates/TextMessage.qml</file>
-        <file>qml/delegates/NoticeMessage.qml</file>
-        <file>qml/delegates/ImageMessage.qml</file>
-        <file>qml/delegates/PlayableMediaMessage.qml</file>
+        <file>qml/delegates/Encrypted.qml</file>
         <file>qml/delegates/FileMessage.qml</file>
+        <file>qml/delegates/ImageMessage.qml</file>
+        <file>qml/delegates/NoticeMessage.qml</file>
         <file>qml/delegates/Pill.qml</file>
         <file>qml/delegates/Placeholder.qml</file>
+        <file>qml/delegates/PlayableMediaMessage.qml</file>
         <file>qml/delegates/Reply.qml</file>
+        <file>qml/delegates/TextMessage.qml</file>
         <file>qml/device-verification/Waiting.qml</file>
         <file>qml/device-verification/DeviceVerification.qml</file>
         <file>qml/device-verification/DigitVerification.qml</file>
@@ -172,6 +161,8 @@
         <file>qml/device-verification/Failed.qml</file>
         <file>qml/device-verification/Success.qml</file>
         <file>qml/dialogs/InputDialog.qml</file>
+        <file>qml/dialogs/ImagePackSettingsDialog.qml</file>
+        <file>qml/dialogs/ImagePackEditorDialog.qml</file>
         <file>qml/ui/Ripple.qml</file>
         <file>qml/ui/Spinner.qml</file>
         <file>qml/ui/animations/BlinkAnimation.qml</file>
@@ -185,7 +176,12 @@
         <file>qml/voip/VideoCall.qml</file>
         <file>qml/components/AdaptiveLayout.qml</file>
         <file>qml/components/AdaptiveLayoutElement.qml</file>
+        <file>qml/components/AvatarListTile.qml</file>
         <file>qml/components/FlatButton.qml</file>
+        <file>qml/RoomMembers.qml</file>
+        <file>qml/InviteDialog.qml</file>
+        <file>qml/ReadReceipts.qml</file>
+        <file>qml/RawMessageDialog.qml</file>
     </qresource>
     <qresource prefix="/media">
         <file>media/ring.ogg</file>
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 0bcf9fbf..ee991dc2 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -125,7 +125,7 @@ template<class T>
 bool
 containsStateUpdates(const T &e)
 {
-        return std::visit([](const auto &ev) { return Cache::isStateEvent(ev); }, e);
+        return std::visit([](const auto &ev) { return Cache::isStateEvent_<decltype(ev)>; }, e);
 }
 
 bool
@@ -158,7 +158,7 @@ Cache::isHiddenEvent(lmdb::txn &txn,
                 index.session_id = encryptedEvent->content.session_id;
                 index.sender_key = encryptedEvent->content.sender_key;
 
-                auto result = olm::decryptEvent(index, *encryptedEvent);
+                auto result = olm::decryptEvent(index, *encryptedEvent, true);
                 if (!result.error)
                         e = result.event.value();
         }
@@ -288,6 +288,9 @@ Cache::setup()
         outboundMegolmSessionDb_ = lmdb::dbi::open(txn, OUTBOUND_MEGOLM_SESSIONS_DB, MDB_CREATE);
         megolmSessionDataDb_     = lmdb::dbi::open(txn, MEGOLM_SESSIONS_DATA_DB, MDB_CREATE);
 
+        // What rooms are encrypted
+        encryptedRooms_ = lmdb::dbi::open(txn, ENCRYPTED_ROOMS_DB, MDB_CREATE);
+
         txn.commit();
 
         databaseReady_ = true;
@@ -298,8 +301,7 @@ Cache::setEncryptedRoom(lmdb::txn &txn, const std::string &room_id)
 {
         nhlog::db()->info("mark room {} as encrypted", room_id);
 
-        auto db = lmdb::dbi::open(txn, ENCRYPTED_ROOMS_DB, MDB_CREATE);
-        db.put(txn, room_id, "0");
+        encryptedRooms_.put(txn, room_id, "0");
 }
 
 bool
@@ -308,8 +310,7 @@ Cache::isRoomEncrypted(const std::string &room_id)
         std::string_view unused;
 
         auto txn = ro_txn(env_);
-        auto db  = lmdb::dbi::open(txn, ENCRYPTED_ROOMS_DB, MDB_CREATE);
-        auto res = db.get(txn, room_id, unused);
+        auto res = encryptedRooms_.get(txn, room_id, unused);
 
         return res;
 }
@@ -715,32 +716,29 @@ Cache::restoreOlmAccount()
 }
 
 void
-Cache::storeSecret(const std::string &name, const std::string &secret)
+Cache::storeSecret(const std::string name, const std::string secret)
 {
         auto settings = UserSettings::instance();
-        QKeychain::WritePasswordJob job(QCoreApplication::applicationName());
-        job.setAutoDelete(false);
-        job.setInsecureFallback(true);
-        job.setKey("matrix." +
-                   QString(QCryptographicHash::hash(settings->profile().toUtf8(),
-                                                    QCryptographicHash::Sha256)) +
-                   "." + name.c_str());
-        job.setTextData(QString::fromStdString(secret));
-        QEventLoop loop;
-        job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
-        job.start();
-        loop.exec();
-
-        if (job.error()) {
-                nhlog::db()->warn(
-                  "Storing secret '{}' failed: {}", name, job.errorString().toStdString());
-        } else {
-                emit secretChanged(name);
-        }
+        auto job      = new QKeychain::WritePasswordJob(QCoreApplication::applicationName());
+        job->setInsecureFallback(true);
+        job->setKey("matrix." +
+                    QString(QCryptographicHash::hash(settings->profile().toUtf8(),
+                                                     QCryptographicHash::Sha256)) +
+                    "." + name.c_str());
+        job->setTextData(QString::fromStdString(secret));
+        QObject::connect(job, &QKeychain::Job::finished, job, [name, this](QKeychain::Job *job) {
+                if (job->error()) {
+                        nhlog::db()->warn(
+                          "Storing secret '{}' failed: {}", name, job->errorString().toStdString());
+                } else {
+                        emit secretChanged(name);
+                }
+        });
+        job->start();
 }
 
 void
-Cache::deleteSecret(const std::string &name)
+Cache::deleteSecret(const std::string name)
 {
         auto settings = UserSettings::instance();
         QKeychain::DeletePasswordJob job(QCoreApplication::applicationName());
@@ -750,6 +748,8 @@ Cache::deleteSecret(const std::string &name)
                    QString(QCryptographicHash::hash(settings->profile().toUtf8(),
                                                     QCryptographicHash::Sha256)) +
                    "." + name.c_str());
+        // FIXME(Nico): Nested event loops are dangerous. Some other slots may resume in the mean
+        // time!
         QEventLoop loop;
         job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
         job.start();
@@ -759,7 +759,7 @@ Cache::deleteSecret(const std::string &name)
 }
 
 std::optional<std::string>
-Cache::secret(const std::string &name)
+Cache::secret(const std::string name)
 {
         auto settings = UserSettings::instance();
         QKeychain::ReadPasswordJob job(QCoreApplication::applicationName());
@@ -769,6 +769,8 @@ Cache::secret(const std::string &name)
                    QString(QCryptographicHash::hash(settings->profile().toUtf8(),
                                                     QCryptographicHash::Sha256)) +
                    "." + name.c_str());
+        // FIXME(Nico): Nested event loops are dangerous. Some other slots may resume in the mean
+        // time!
         QEventLoop loop;
         job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
         job.start();
@@ -3383,26 +3385,30 @@ Cache::getChildRoomIds(const std::string &room_id)
 }
 
 std::vector<ImagePackInfo>
-Cache::getImagePacks(const std::string &room_id, bool stickers)
+Cache::getImagePacks(const std::string &room_id, std::optional<bool> stickers)
 {
         auto txn = ro_txn(env_);
         std::vector<ImagePackInfo> infos;
 
-        auto addPack = [&infos, stickers](const mtx::events::msc2545::ImagePack &pack) {
-                if (!pack.pack || (stickers ? pack.pack->is_sticker() : pack.pack->is_emoji())) {
+        auto addPack = [&infos, stickers](const mtx::events::msc2545::ImagePack &pack,
+                                          const std::string &source_room,
+                                          const std::string &state_key) {
+                if (!pack.pack || !stickers.has_value() ||
+                    (stickers.value() ? pack.pack->is_sticker() : pack.pack->is_emoji())) {
                         ImagePackInfo info;
-                        if (pack.pack)
-                                info.packname = pack.pack->display_name;
+                        info.source_room = source_room;
+                        info.state_key   = state_key;
+                        info.pack.pack   = pack.pack;
 
                         for (const auto &img : pack.images) {
-                                if (img.second.overrides_usage() &&
+                                if (stickers.has_value() && img.second.overrides_usage() &&
                                     (stickers ? !img.second.is_sticker() : !img.second.is_emoji()))
                                         continue;
 
-                                info.images.insert(img);
+                                info.pack.images.insert(img);
                         }
 
-                        if (!info.images.empty())
+                        if (!info.pack.images.empty())
                                 infos.push_back(std::move(info));
                 }
         };
@@ -3414,7 +3420,7 @@ Cache::getImagePacks(const std::string &room_id, bool stickers)
                   std::get_if<mtx::events::EphemeralEvent<mtx::events::msc2545::ImagePack>>(
                     &*accountpack);
                 if (tmp)
-                        addPack(tmp->content);
+                        addPack(tmp->content, "", "");
         }
 
         // packs from rooms, that were enabled globally
@@ -3433,7 +3439,7 @@ Cache::getImagePacks(const std::string &room_id, bool stickers)
                                         if (auto pack =
                                               getStateEvent<mtx::events::msc2545::ImagePack>(
                                                 txn, room_id2, state_id))
-                                                addPack(pack->content);
+                                                addPack(pack->content, room_id2, state_id);
                                 }
                         }
                 }
@@ -3441,17 +3447,24 @@ Cache::getImagePacks(const std::string &room_id, bool stickers)
 
         // packs from current room
         if (auto pack = getStateEvent<mtx::events::msc2545::ImagePack>(txn, room_id)) {
-                addPack(pack->content);
+                addPack(pack->content, room_id, "");
         }
         for (const auto &pack :
              getStateEventsWithType<mtx::events::msc2545::ImagePack>(txn, room_id)) {
-                addPack(pack.content);
+                addPack(pack.content, room_id, pack.state_key);
         }
 
         return infos;
 }
 
 std::optional<mtx::events::collections::RoomAccountDataEvents>
+Cache::getAccountData(mtx::events::EventType type, const std::string &room_id)
+{
+        auto txn = ro_txn(env_);
+        return getAccountData(txn, type, room_id);
+}
+
+std::optional<mtx::events::collections::RoomAccountDataEvents>
 Cache::getAccountData(lmdb::txn &txn, mtx::events::EventType type, const std::string &room_id)
 {
         try {
@@ -3529,7 +3542,7 @@ Cache::roomMembers(const std::string &room_id)
 }
 
 std::map<std::string, std::optional<UserKeyCache>>
-Cache::getMembersWithKeys(const std::string &room_id)
+Cache::getMembersWithKeys(const std::string &room_id, bool verified_only)
 {
         std::string_view keys;
 
@@ -3546,10 +3559,51 @@ Cache::getMembersWithKeys(const std::string &room_id)
                         auto res = keysDb.get(txn, user_id, keys);
 
                         if (res) {
-                                members[std::string(user_id)] =
-                                  json::parse(keys).get<UserKeyCache>();
+                                auto k = json::parse(keys).get<UserKeyCache>();
+                                if (verified_only) {
+                                        auto verif = verificationStatus(std::string(user_id));
+                                        if (verif.user_verified == crypto::Trust::Verified ||
+                                            !verif.verified_devices.empty()) {
+                                                auto keyCopy = k;
+                                                keyCopy.device_keys.clear();
+
+                                                std::copy_if(
+                                                  k.device_keys.begin(),
+                                                  k.device_keys.end(),
+                                                  std::inserter(keyCopy.device_keys,
+                                                                keyCopy.device_keys.end()),
+                                                  [&verif](const auto &key) {
+                                                          auto curve25519 = key.second.keys.find(
+                                                            "curve25519:" + key.first);
+                                                          if (curve25519 == key.second.keys.end())
+                                                                  return false;
+                                                          if (auto t =
+                                                                verif.verified_device_keys.find(
+                                                                  curve25519->second);
+                                                              t ==
+                                                                verif.verified_device_keys.end() ||
+                                                              t->second != crypto::Trust::Verified)
+                                                                  return false;
+
+                                                          return key.first ==
+                                                                   key.second.device_id &&
+                                                                 std::find(
+                                                                   verif.verified_devices.begin(),
+                                                                   verif.verified_devices.end(),
+                                                                   key.first) !=
+                                                                   verif.verified_devices.end();
+                                                  });
+
+                                                if (!keyCopy.device_keys.empty())
+                                                        members[std::string(user_id)] =
+                                                          std::move(keyCopy);
+                                        }
+                                } else {
+                                        members[std::string(user_id)] = std::move(k);
+                                }
                         } else {
-                                members[std::string(user_id)] = {};
+                                if (!verified_only)
+                                        members[std::string(user_id)] = {};
                         }
                 }
                 cursor.close();
@@ -4240,6 +4294,8 @@ to_json(nlohmann::json &obj, const GroupSessionData &msg)
         obj["forwarding_curve25519_key_chain"] = msg.forwarding_curve25519_key_chain;
 
         obj["currently"] = msg.currently;
+
+        obj["indices"] = msg.indices;
 }
 
 void
@@ -4253,6 +4309,8 @@ from_json(const nlohmann::json &obj, GroupSessionData &msg)
           obj.value("forwarding_curve25519_key_chain", std::vector<std::string>{});
 
         msg.currently = obj.value("currently", SharedWithUsers{});
+
+        msg.indices = obj.value("indices", std::map<uint32_t, std::string>());
 }
 
 void
diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h
index 409c9d67..69d64885 100644
--- a/src/CacheCryptoStructs.h
+++ b/src/CacheCryptoStructs.h
@@ -50,6 +50,9 @@ struct GroupSessionData
         std::string sender_claimed_ed25519_key;
         std::vector<std::string> forwarding_curve25519_key_chain;
 
+        //! map from index to event_id to check for replay attacks
+        std::map<uint32_t, std::string> indices;
+
         // who has access to this session.
         // Rotate, when a user leaves the room and share, when a user gets added.
         SharedWithUsers currently;
diff --git a/src/CacheStructs.h b/src/CacheStructs.h
index f274d70f..4a5c5c76 100644
--- a/src/CacheStructs.h
+++ b/src/CacheStructs.h
@@ -113,6 +113,7 @@ struct RoomSearchResult
 
 struct ImagePackInfo
 {
-        std::string packname;
-        std::map<std::string, mtx::events::msc2545::PackImage> images;
+        mtx::events::msc2545::ImagePack pack;
+        std::string source_room;
+        std::string state_key;
 };
diff --git a/src/Cache_p.h b/src/Cache_p.h
index 13fbc371..30c365a6 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -48,7 +48,8 @@ public:
         // user cache stores user keys
         std::optional<UserKeyCache> userKeys(const std::string &user_id);
         std::map<std::string, std::optional<UserKeyCache>> getMembersWithKeys(
-          const std::string &room_id);
+          const std::string &room_id,
+          bool verified_only);
         void updateUserKeys(const std::string &sync_token,
                             const mtx::responses::QueryKeys &keyQuery);
         void markUserKeysOutOfDate(lmdb::txn &txn,
@@ -97,6 +98,12 @@ public:
                 return getStateEvent<T>(txn, room_id, state_key);
         }
 
+        //! retrieve a specific event from account data
+        //! pass empty room_id for global account data
+        std::optional<mtx::events::collections::RoomAccountDataEvents> getAccountData(
+          mtx::events::EventType type,
+          const std::string &room_id = "");
+
         //! Retrieve member info from a room.
         std::vector<RoomMember> getMembers(const std::string &room_id,
                                            std::size_t startIndex = 0,
@@ -225,7 +232,8 @@ public:
         std::vector<std::string> getParentRoomIds(const std::string &room_id);
         std::vector<std::string> getChildRoomIds(const std::string &room_id);
 
-        std::vector<ImagePackInfo> getImagePacks(const std::string &room_id, bool stickers);
+        std::vector<ImagePackInfo> getImagePacks(const std::string &room_id,
+                                                 std::optional<bool> stickers);
 
         //! Mark a room that uses e2e encryption.
         void setEncryptedRoom(lmdb::txn &txn, const std::string &room_id);
@@ -278,20 +286,14 @@ public:
         void saveOlmAccount(const std::string &pickled);
         std::string restoreOlmAccount();
 
-        void storeSecret(const std::string &name, const std::string &secret);
-        void deleteSecret(const std::string &name);
-        std::optional<std::string> secret(const std::string &name);
+        void storeSecret(const std::string name, const std::string secret);
+        void deleteSecret(const std::string name);
+        std::optional<std::string> secret(const std::string name);
 
         template<class T>
-        static constexpr bool isStateEvent(const mtx::events::StateEvent<T> &)
-        {
-                return true;
-        }
-        template<class T>
-        static constexpr bool isStateEvent(const mtx::events::Event<T> &)
-        {
-                return false;
-        }
+        constexpr static bool isStateEvent_ =
+          std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
+                         mtx::events::StateEvent<decltype(std::declval<T>().content)>>;
 
         static int compare_state_key(const MDB_val *a, const MDB_val *b)
         {
@@ -408,11 +410,27 @@ private:
                 }
 
                 std::visit(
-                  [&txn, &statesdb, &stateskeydb, &eventsDb](auto e) {
-                          if constexpr (isStateEvent(e)) {
+                  [&txn, &statesdb, &stateskeydb, &eventsDb, &membersdb](const auto &e) {
+                          if constexpr (isStateEvent_<decltype(e)>) {
                                   eventsDb.put(txn, e.event_id, json(e).dump());
 
-                                  if (e.type != EventType::Unsupported) {
+                                  if (std::is_same_v<
+                                        std::remove_cv_t<std::remove_reference_t<decltype(e)>>,
+                                        StateEvent<mtx::events::msg::Redacted>>) {
+                                          if (e.type == EventType::RoomMember)
+                                                  membersdb.del(txn, e.state_key, "");
+                                          else if (e.state_key.empty())
+                                                  statesdb.del(txn, to_string(e.type));
+                                          else
+                                                  stateskeydb.del(
+                                                    txn,
+                                                    to_string(e.type),
+                                                    json::object({
+                                                                   {"key", e.state_key},
+                                                                   {"id", e.event_id},
+                                                                 })
+                                                      .dump());
+                                  } else if (e.type != EventType::Unsupported) {
                                           if (e.state_key.empty())
                                                   statesdb.put(
                                                     txn, to_string(e.type), json(e).dump());
@@ -682,6 +700,8 @@ private:
         lmdb::dbi outboundMegolmSessionDb_;
         lmdb::dbi megolmSessionDataDb_;
 
+        lmdb::dbi encryptedRooms_;
+
         QString localUserId_;
         QString cacheDirectory_;
 
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 10a91557..42e3bc7b 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -31,7 +31,6 @@
 
 #include "notifications/Manager.h"
 
-#include "dialogs/ReadReceipts.h"
 #include "timeline/TimelineViewManager.h"
 
 #include "blurhash.hpp"
@@ -116,29 +115,31 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
 
         connect(this, &ChatPage::loggedOut, this, &ChatPage::logout);
 
-        connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList users) {
-                const auto room_id = currentRoom().toStdString();
-
-                for (int ii = 0; ii < users.size(); ++ii) {
-                        QTimer::singleShot(ii * 500, this, [this, room_id, ii, users]() {
-                                const auto user = users.at(ii);
-
-                                http::client()->invite_user(
-                                  room_id,
-                                  user.toStdString(),
-                                  [this, user](const mtx::responses::RoomInvite &,
-                                               mtx::http::RequestErr err) {
-                                          if (err) {
-                                                  emit showNotification(
-                                                    tr("Failed to invite user: %1").arg(user));
-                                                  return;
-                                          }
-
-                                          emit showNotification(tr("Invited user: %1").arg(user));
-                                  });
-                        });
-                }
-        });
+        connect(
+          view_manager_,
+          &TimelineViewManager::inviteUsers,
+          this,
+          [this](QString roomId, QStringList users) {
+                  for (int ii = 0; ii < users.size(); ++ii) {
+                          QTimer::singleShot(ii * 500, this, [this, roomId, ii, users]() {
+                                  const auto user = users.at(ii);
+
+                                  http::client()->invite_user(
+                                    roomId.toStdString(),
+                                    user.toStdString(),
+                                    [this, user](const mtx::responses::RoomInvite &,
+                                                 mtx::http::RequestErr err) {
+                                            if (err) {
+                                                    emit showNotification(
+                                                      tr("Failed to invite user: %1").arg(user));
+                                                    return;
+                                            }
+
+                                            emit showNotification(tr("Invited user: %1").arg(user));
+                                    });
+                          });
+                  }
+          });
 
         connect(this, &ChatPage::leftRoom, this, &ChatPage::removeRoom);
         connect(this, &ChatPage::newRoom, this, &ChatPage::changeRoom, Qt::QueuedConnection);
@@ -927,31 +928,33 @@ ChatPage::currentPresence() const
 void
 ChatPage::ensureOneTimeKeyCount(const std::map<std::string, uint16_t> &counts)
 {
-        for (const auto &entry : counts) {
-                if (entry.second < MAX_ONETIME_KEYS) {
-                        const int nkeys = MAX_ONETIME_KEYS - entry.second;
+        uint16_t count = 0;
+        if (auto c = counts.find(mtx::crypto::SIGNED_CURVE25519); c != counts.end())
+                count = c->second;
 
-                        nhlog::crypto()->info("uploading {} {} keys", nkeys, entry.first);
-                        olm::client()->generate_one_time_keys(nkeys);
+        if (count < MAX_ONETIME_KEYS) {
+                const int nkeys = MAX_ONETIME_KEYS - count;
 
-                        http::client()->upload_keys(
-                          olm::client()->create_upload_keys_request(),
-                          [](const mtx::responses::UploadKeys &, mtx::http::RequestErr err) {
-                                  if (err) {
-                                          nhlog::crypto()->warn(
-                                            "failed to update one-time keys: {} {} {}",
-                                            err->matrix_error.error,
-                                            static_cast<int>(err->status_code),
-                                            static_cast<int>(err->error_code));
+                nhlog::crypto()->info(
+                  "uploading {} {} keys", nkeys, mtx::crypto::SIGNED_CURVE25519);
+                olm::client()->generate_one_time_keys(nkeys);
 
-                                          if (err->status_code < 400 || err->status_code >= 500)
-                                                  return;
-                                  }
+                http::client()->upload_keys(
+                  olm::client()->create_upload_keys_request(),
+                  [](const mtx::responses::UploadKeys &, mtx::http::RequestErr err) {
+                          if (err) {
+                                  nhlog::crypto()->warn("failed to update one-time keys: {} {} {}",
+                                                        err->matrix_error.error,
+                                                        static_cast<int>(err->status_code),
+                                                        static_cast<int>(err->error_code));
 
-                                  // mark as published anyway, otherwise we may end up in a loop.
-                                  olm::mark_keys_as_published();
-                          });
-                }
+                                  if (err->status_code < 400 || err->status_code >= 500)
+                                          return;
+                          }
+
+                          // mark as published anyway, otherwise we may end up in a loop.
+                          olm::mark_keys_as_published();
+                  });
         }
 }
 
@@ -1024,8 +1027,15 @@ ChatPage::decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescriptio
 
         auto decryptionKey = mtx::crypto::key_from_recoverykey(text.toStdString(), keyDesc);
 
-        if (!decryptionKey)
-                decryptionKey = mtx::crypto::key_from_passphrase(text.toStdString(), keyDesc);
+        if (!decryptionKey && keyDesc.passphrase) {
+                try {
+                        decryptionKey =
+                          mtx::crypto::key_from_passphrase(text.toStdString(), keyDesc);
+                } catch (std::exception &e) {
+                        nhlog::crypto()->error("Failed to derive secret key from passphrase: {}",
+                                               e.what());
+                }
+        }
 
         if (!decryptionKey) {
                 QMessageBox::information(
diff --git a/src/ImagePackModel.cpp b/src/CombinedImagePackModel.cpp
index 9b0dca8d..341a34ec 100644
--- a/src/ImagePackModel.cpp
+++ b/src/CombinedImagePackModel.cpp
@@ -2,21 +2,24 @@
 //
 // SPDX-License-Identifier: GPL-3.0-or-later
 
-#include "ImagePackModel.h"
+#include "CombinedImagePackModel.h"
 
 #include "Cache_p.h"
 #include "CompletionModelRoles.h"
 
-ImagePackModel::ImagePackModel(const std::string &roomId, bool stickers, QObject *parent)
+CombinedImagePackModel::CombinedImagePackModel(const std::string &roomId,
+                                               bool stickers,
+                                               QObject *parent)
   : QAbstractListModel(parent)
   , room_id(roomId)
 {
         auto packs = cache::client()->getImagePacks(room_id, stickers);
 
         for (const auto &pack : packs) {
-                QString packname = QString::fromStdString(pack.packname);
+                QString packname =
+                  pack.pack.pack ? QString::fromStdString(pack.pack.pack->display_name) : "";
 
-                for (const auto &img : pack.images) {
+                for (const auto &img : pack.pack.images) {
                         ImageDesc i{};
                         i.shortcode = QString::fromStdString(img.first);
                         i.packname  = packname;
@@ -27,13 +30,13 @@ ImagePackModel::ImagePackModel(const std::string &roomId, bool stickers, QObject
 }
 
 int
-ImagePackModel::rowCount(const QModelIndex &) const
+CombinedImagePackModel::rowCount(const QModelIndex &) const
 {
         return (int)images.size();
 }
 
 QHash<int, QByteArray>
-ImagePackModel::roleNames() const
+CombinedImagePackModel::roleNames() const
 {
         return {
           {CompletionModel::CompletionRole, "completionRole"},
@@ -48,7 +51,7 @@ ImagePackModel::roleNames() const
 }
 
 QVariant
-ImagePackModel::data(const QModelIndex &index, int role) const
+CombinedImagePackModel::data(const QModelIndex &index, int role) const
 {
         if (hasIndex(index.row(), index.column(), index.parent())) {
                 switch (role) {
diff --git a/src/ImagePackModel.h b/src/CombinedImagePackModel.h
index 937014ec..f0f69799 100644
--- a/src/ImagePackModel.h
+++ b/src/CombinedImagePackModel.h
@@ -8,7 +8,7 @@
 
 #include <mtx/events/mscs/image_packs.hpp>
 
-class ImagePackModel : public QAbstractListModel
+class CombinedImagePackModel : public QAbstractListModel
 {
         Q_OBJECT
 public:
@@ -21,7 +21,7 @@ public:
                 OriginalRow,
         };
 
-        ImagePackModel(const std::string &roomId, bool stickers, QObject *parent = nullptr);
+        CombinedImagePackModel(const std::string &roomId, bool stickers, QObject *parent = nullptr);
         QHash<int, QByteArray> roleNames() const override;
         int rowCount(const QModelIndex &parent = QModelIndex()) const override;
         QVariant data(const QModelIndex &index, int role) const override;
diff --git a/src/ImagePackListModel.cpp b/src/ImagePackListModel.cpp
new file mode 100644
index 00000000..6392de22
--- /dev/null
+++ b/src/ImagePackListModel.cpp
@@ -0,0 +1,94 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "ImagePackListModel.h"
+
+#include <QQmlEngine>
+
+#include "Cache_p.h"
+#include "SingleImagePackModel.h"
+
+ImagePackListModel::ImagePackListModel(const std::string &roomId, QObject *parent)
+  : QAbstractListModel(parent)
+  , room_id(roomId)
+{
+        auto packs_ = cache::client()->getImagePacks(room_id, std::nullopt);
+
+        for (const auto &pack : packs_) {
+                packs.push_back(
+                  QSharedPointer<SingleImagePackModel>(new SingleImagePackModel(pack)));
+        }
+}
+
+int
+ImagePackListModel::rowCount(const QModelIndex &) const
+{
+        return (int)packs.size();
+}
+
+QHash<int, QByteArray>
+ImagePackListModel::roleNames() const
+{
+        return {
+          {Roles::DisplayName, "displayName"},
+          {Roles::AvatarUrl, "avatarUrl"},
+          {Roles::FromAccountData, "fromAccountData"},
+          {Roles::FromCurrentRoom, "fromCurrentRoom"},
+          {Roles::StateKey, "statekey"},
+          {Roles::RoomId, "roomid"},
+        };
+}
+
+QVariant
+ImagePackListModel::data(const QModelIndex &index, int role) const
+{
+        if (hasIndex(index.row(), index.column(), index.parent())) {
+                const auto &pack = packs.at(index.row());
+                switch (role) {
+                case Roles::DisplayName:
+                        return pack->packname();
+                case Roles::AvatarUrl:
+                        return pack->avatarUrl();
+                case Roles::FromAccountData:
+                        return pack->roomid().isEmpty();
+                case Roles::FromCurrentRoom:
+                        return pack->roomid().toStdString() == this->room_id;
+                case Roles::StateKey:
+                        return pack->statekey();
+                case Roles::RoomId:
+                        return pack->roomid();
+                default:
+                        return {};
+                }
+        }
+        return {};
+}
+
+SingleImagePackModel *
+ImagePackListModel::packAt(int row)
+{
+        if (row < 0 || static_cast<size_t>(row) >= packs.size())
+                return {};
+        auto e = packs.at(row).get();
+        QQmlEngine::setObjectOwnership(e, QQmlEngine::CppOwnership);
+        return e;
+}
+
+SingleImagePackModel *
+ImagePackListModel::newPack(bool inRoom)
+{
+        ImagePackInfo info{};
+        if (inRoom)
+                info.source_room = room_id;
+        return new SingleImagePackModel(info);
+}
+
+bool
+ImagePackListModel::containsAccountPack() const
+{
+        for (const auto &p : packs)
+                if (p->roomid().isEmpty())
+                        return true;
+        return false;
+}
diff --git a/src/ImagePackListModel.h b/src/ImagePackListModel.h
new file mode 100644
index 00000000..2aa5abb2
--- /dev/null
+++ b/src/ImagePackListModel.h
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QAbstractListModel>
+#include <QQmlEngine>
+#include <QSharedPointer>
+
+class SingleImagePackModel;
+class ImagePackListModel : public QAbstractListModel
+{
+        Q_OBJECT
+        Q_PROPERTY(bool containsAccountPack READ containsAccountPack CONSTANT)
+public:
+        enum Roles
+        {
+                DisplayName = Qt::UserRole,
+                AvatarUrl,
+                FromAccountData,
+                FromCurrentRoom,
+                StateKey,
+                RoomId,
+        };
+
+        ImagePackListModel(const std::string &roomId, QObject *parent = nullptr);
+        QHash<int, QByteArray> roleNames() const override;
+        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+        QVariant data(const QModelIndex &index, int role) const override;
+
+        Q_INVOKABLE SingleImagePackModel *packAt(int row);
+        Q_INVOKABLE SingleImagePackModel *newPack(bool inRoom);
+
+        bool containsAccountPack() const;
+
+private:
+        std::string room_id;
+
+        std::vector<QSharedPointer<SingleImagePackModel>> packs;
+};
diff --git a/src/InviteeItem.cpp b/src/InviteeItem.cpp
deleted file mode 100644
index 27f02560..00000000
--- a/src/InviteeItem.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QHBoxLayout>
-#include <QLabel>
-#include <QPushButton>
-
-#include "InviteeItem.h"
-
-constexpr int SidePadding = 10;
-
-InviteeItem::InviteeItem(mtx::identifiers::User user, QWidget *parent)
-  : QWidget{parent}
-  , user_{QString::fromStdString(user.to_string())}
-{
-        auto topLayout_ = new QHBoxLayout(this);
-        topLayout_->setSpacing(0);
-        topLayout_->setContentsMargins(SidePadding, 0, 3 * SidePadding, 0);
-
-        name_          = new QLabel(user_, this);
-        removeUserBtn_ = new QPushButton(tr("Remove"), this);
-
-        topLayout_->addWidget(name_);
-        topLayout_->addWidget(removeUserBtn_, 0, Qt::AlignRight);
-
-        connect(removeUserBtn_, &QPushButton::clicked, this, &InviteeItem::removeItem);
-}
diff --git a/src/InviteeItem.h b/src/InviteeItem.h
deleted file mode 100644
index 014541ea..00000000
--- a/src/InviteeItem.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QWidget>
-
-#include <mtx/identifiers.hpp>
-
-class QPushButton;
-class QLabel;
-
-class InviteeItem : public QWidget
-{
-        Q_OBJECT
-
-public:
-        InviteeItem(mtx::identifiers::User user, QWidget *parent = nullptr);
-
-        QString userID() { return user_; }
-
-signals:
-        void removeItem();
-
-private:
-        QString user_;
-
-        QLabel *name_;
-        QPushButton *removeUserBtn_;
-};
diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp
new file mode 100644
index 00000000..27b2116f
--- /dev/null
+++ b/src/InviteesModel.cpp
@@ -0,0 +1,84 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "InviteesModel.h"
+
+#include "Cache.h"
+#include "Logging.h"
+#include "MatrixClient.h"
+#include "mtx/responses/profile.hpp"
+
+InviteesModel::InviteesModel(QObject *parent)
+  : QAbstractListModel{parent}
+{}
+
+void
+InviteesModel::addUser(QString mxid)
+{
+        beginInsertRows(QModelIndex(), invitees_.count(), invitees_.count());
+
+        auto invitee        = new Invitee{mxid, this};
+        auto indexOfInvitee = invitees_.count();
+        connect(invitee, &Invitee::userInfoLoaded, this, [this, indexOfInvitee]() {
+                emit dataChanged(index(indexOfInvitee), index(indexOfInvitee));
+        });
+
+        invitees_.push_back(invitee);
+
+        endInsertRows();
+        emit countChanged();
+}
+
+QHash<int, QByteArray>
+InviteesModel::roleNames() const
+{
+        return {{Mxid, "mxid"}, {DisplayName, "displayName"}, {AvatarUrl, "avatarUrl"}};
+}
+
+QVariant
+InviteesModel::data(const QModelIndex &index, int role) const
+{
+        if (!index.isValid() || index.row() >= (int)invitees_.size() || index.row() < 0)
+                return {};
+
+        switch (role) {
+        case Mxid:
+                return invitees_[index.row()]->mxid_;
+        case DisplayName:
+                return invitees_[index.row()]->displayName_;
+        case AvatarUrl:
+                return invitees_[index.row()]->avatarUrl_;
+        default:
+                return {};
+        }
+}
+
+QStringList
+InviteesModel::mxids()
+{
+        QStringList mxidList;
+        for (int i = 0; i < invitees_.length(); ++i)
+                mxidList.push_back(invitees_[i]->mxid_);
+        return mxidList;
+}
+
+Invitee::Invitee(const QString &mxid, QObject *parent)
+  : QObject{parent}
+  , mxid_{mxid}
+{
+        http::client()->get_profile(
+          mxid_.toStdString(),
+          [this](const mtx::responses::Profile &res, mtx::http::RequestErr err) {
+                  if (err) {
+                          nhlog::net()->warn("failed to retrieve profile info");
+                          emit userInfoLoaded();
+                          return;
+                  }
+
+                  displayName_ = QString::fromStdString(res.display_name);
+                  avatarUrl_   = QString::fromStdString(res.avatar_url);
+
+                  emit userInfoLoaded();
+          });
+}
diff --git a/src/InviteesModel.h b/src/InviteesModel.h
new file mode 100644
index 00000000..a4e19ebb
--- /dev/null
+++ b/src/InviteesModel.h
@@ -0,0 +1,63 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef INVITEESMODEL_H
+#define INVITEESMODEL_H
+
+#include <QAbstractListModel>
+#include <QVector>
+
+class Invitee : public QObject
+{
+        Q_OBJECT
+
+public:
+        Invitee(const QString &mxid, QObject *parent = nullptr);
+
+signals:
+        void userInfoLoaded();
+
+private:
+        const QString mxid_;
+        QString displayName_;
+        QString avatarUrl_;
+
+        friend class InviteesModel;
+};
+
+class InviteesModel : public QAbstractListModel
+{
+        Q_OBJECT
+
+        Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
+
+public:
+        enum Roles
+        {
+                Mxid,
+                DisplayName,
+                AvatarUrl,
+        };
+
+        InviteesModel(QObject *parent = nullptr);
+
+        Q_INVOKABLE void addUser(QString mxid);
+
+        QHash<int, QByteArray> roleNames() const override;
+        int rowCount(const QModelIndex & = QModelIndex()) const override
+        {
+                return (int)invitees_.size();
+        }
+        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+        QStringList mxids();
+
+signals:
+        void accept();
+        void countChanged();
+
+private:
+        QVector<Invitee *> invitees_;
+};
+
+#endif // INVITEESMODEL_H
diff --git a/src/Logging.cpp b/src/Logging.cpp
index 642e8957..67bcaf7a 100644
--- a/src/Logging.cpp
+++ b/src/Logging.cpp
@@ -30,19 +30,11 @@ qmlMessageHandler(QtMsgType type, const QMessageLogContext &context, const QStri
         const char *function = context.function ? context.function : "";
 
         if (
-          // Surpress binding wrning for now, as we can't set restore mode to keep compat with
-          // qt 5.10
-          msg.contains(QStringLiteral(
-            "QML Binding: Not restoring previous value because restoreMode has not been set.")) ||
           // The default style has the point size set. If you use pixel size anywhere, you get
           // that warning, which is useless, since sometimes you need the pixel size to match the
           // text to the size of the outer element for example. This is done in the avatar and
           // without that you get one warning for every Avatar displayed, which is stupid!
-          msg.endsWith(QStringLiteral("Both point size and pixel size set. Using pixel size.")) ||
-          // The new syntax breaks rebinding on Qt < 5.15. Until we can drop that, we still need it.
-          msg.endsWith(QStringLiteral(
-            "QML Connections: Implicitly defined onFoo properties in Connections are "
-            "deprecated. Use this syntax instead: function onFoo(<arguments>) { ... }")))
+          msg.endsWith(QStringLiteral("Both point size and pixel size set. Using pixel size.")))
                 return;
 
         switch (type) {
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index ed337ca4..8bc90f29 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -21,6 +21,7 @@
 #include "LoginPage.h"
 #include "MainWindow.h"
 #include "MatrixClient.h"
+#include "MemberList.h"
 #include "RegisterPage.h"
 #include "TrayIcon.h"
 #include "UserSettingsPage.h"
@@ -32,12 +33,9 @@
 #include "ui/SnackBar.h"
 
 #include "dialogs/CreateRoom.h"
-#include "dialogs/InviteUsers.h"
 #include "dialogs/JoinRoom.h"
 #include "dialogs/LeaveRoom.h"
 #include "dialogs/Logout.h"
-#include "dialogs/MemberList.h"
-#include "dialogs/ReadReceipts.h"
 
 MainWindow *MainWindow::instance_ = nullptr;
 
@@ -311,14 +309,6 @@ MainWindow::hasActiveUser()
 }
 
 void
-MainWindow::openMemberListDialog(const QString &room_id)
-{
-        auto dialog = new dialogs::MemberList(room_id, this);
-
-        showDialog(dialog);
-}
-
-void
 MainWindow::openLeaveRoomDialog(const QString &room_id)
 {
         auto dialog = new dialogs::LeaveRoom(this);
@@ -342,18 +332,6 @@ MainWindow::showOverlayProgressBar()
 }
 
 void
-MainWindow::openInviteUsersDialog(std::function<void(const QStringList &invitees)> callback)
-{
-        auto dialog = new dialogs::InviteUsers(this);
-        connect(dialog, &dialogs::InviteUsers::sendInvites, this, [callback](QStringList invitees) {
-                if (!invitees.isEmpty())
-                        callback(invitees);
-        });
-
-        showDialog(dialog);
-}
-
-void
 MainWindow::openJoinRoomDialog(std::function<void(const QString &room_id)> callback)
 {
         auto dialog = new dialogs::JoinRoom(this);
@@ -419,27 +397,6 @@ MainWindow::openLogoutDialog()
         showDialog(dialog);
 }
 
-void
-MainWindow::openReadReceiptsDialog(const QString &event_id)
-{
-        auto dialog = new dialogs::ReadReceipts(this);
-
-        const auto room_id = chat_page_->currentRoom();
-
-        try {
-                dialog->addUsers(cache::readReceipts(event_id, room_id));
-        } catch (const lmdb::error &) {
-                nhlog::db()->warn("failed to retrieve read receipts for {} {}",
-                                  event_id.toStdString(),
-                                  chat_page_->currentRoom().toStdString());
-                dialog->deleteLater();
-
-                return;
-        }
-
-        showDialog(dialog);
-}
-
 bool
 MainWindow::hasActiveDialogs() const
 {
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 3571f079..d423af9f 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -65,8 +65,6 @@ public:
           std::function<void(const mtx::requests::CreateRoom &request)> callback);
         void openJoinRoomDialog(std::function<void(const QString &room_id)> callback);
         void openLogoutDialog();
-        void openMemberListDialog(const QString &room_id);
-        void openReadReceiptsDialog(const QString &event_id);
 
         void hideOverlay();
         void showSolidOverlayModal(QWidget *content,
diff --git a/src/MemberList.cpp b/src/MemberList.cpp
new file mode 100644
index 00000000..196647fe
--- /dev/null
+++ b/src/MemberList.cpp
@@ -0,0 +1,100 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "MemberList.h"
+
+#include "Cache.h"
+#include "ChatPage.h"
+#include "Config.h"
+#include "Logging.h"
+#include "Utils.h"
+#include "timeline/TimelineViewManager.h"
+
+MemberList::MemberList(const QString &room_id, QObject *parent)
+  : QAbstractListModel{parent}
+  , room_id_{room_id}
+{
+        try {
+                info_ = cache::singleRoomInfo(room_id_.toStdString());
+        } catch (const lmdb::error &) {
+                nhlog::db()->warn("failed to retrieve room info from cache: {}",
+                                  room_id_.toStdString());
+        }
+
+        try {
+                auto members = cache::getMembers(room_id_.toStdString());
+                addUsers(members);
+                numUsersLoaded_ = members.size();
+        } catch (const lmdb::error &e) {
+                nhlog::db()->critical("Failed to retrieve members from cache: {}", e.what());
+        }
+}
+
+void
+MemberList::addUsers(const std::vector<RoomMember> &members)
+{
+        beginInsertRows(
+          QModelIndex{}, m_memberList.count(), m_memberList.count() + members.size() - 1);
+
+        for (const auto &member : members)
+                m_memberList.push_back(
+                  {member,
+                   ChatPage::instance()->timelineManager()->rooms()->currentRoom()->avatarUrl(
+                     member.user_id)});
+
+        endInsertRows();
+}
+
+QHash<int, QByteArray>
+MemberList::roleNames() const
+{
+        return {
+          {Mxid, "mxid"},
+          {DisplayName, "displayName"},
+          {AvatarUrl, "avatarUrl"},
+        };
+}
+
+QVariant
+MemberList::data(const QModelIndex &index, int role) const
+{
+        if (!index.isValid() || index.row() >= (int)m_memberList.size() || index.row() < 0)
+                return {};
+
+        switch (role) {
+        case Mxid:
+                return m_memberList[index.row()].first.user_id;
+        case DisplayName:
+                return m_memberList[index.row()].first.display_name;
+        case AvatarUrl:
+                return m_memberList[index.row()].second;
+        default:
+                return {};
+        }
+}
+
+bool
+MemberList::canFetchMore(const QModelIndex &) const
+{
+        const size_t numMembers = rowCount();
+        if (numMembers > 1 && numMembers < info_.member_count)
+                return true;
+        else
+                return false;
+}
+
+void
+MemberList::fetchMore(const QModelIndex &)
+{
+        loadingMoreMembers_ = true;
+        emit loadingMoreMembersChanged();
+
+        auto members = cache::getMembers(room_id_.toStdString(), rowCount());
+        addUsers(members);
+        numUsersLoaded_ += members.size();
+        emit numUsersLoadedChanged();
+
+        loadingMoreMembers_ = false;
+        emit loadingMoreMembersChanged();
+}
diff --git a/src/MemberList.h b/src/MemberList.h
new file mode 100644
index 00000000..e6522694
--- /dev/null
+++ b/src/MemberList.h
@@ -0,0 +1,67 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QAbstractListModel>
+
+#include "CacheStructs.h"
+
+class MemberList : public QAbstractListModel
+{
+        Q_OBJECT
+
+        Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged)
+        Q_PROPERTY(int memberCount READ memberCount NOTIFY memberCountChanged)
+        Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged)
+        Q_PROPERTY(QString roomId READ roomId NOTIFY roomIdChanged)
+        Q_PROPERTY(int numUsersLoaded READ numUsersLoaded NOTIFY numUsersLoadedChanged)
+        Q_PROPERTY(bool loadingMoreMembers READ loadingMoreMembers NOTIFY loadingMoreMembersChanged)
+
+public:
+        enum Roles
+        {
+                Mxid,
+                DisplayName,
+                AvatarUrl,
+        };
+        MemberList(const QString &room_id, QObject *parent = nullptr);
+
+        QHash<int, QByteArray> roleNames() const override;
+        int rowCount(const QModelIndex &parent = QModelIndex()) const override
+        {
+                Q_UNUSED(parent)
+                return static_cast<int>(m_memberList.size());
+        }
+        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+        QString roomName() const { return QString::fromStdString(info_.name); }
+        int memberCount() const { return info_.member_count; }
+        QString avatarUrl() const { return QString::fromStdString(info_.avatar_url); }
+        QString roomId() const { return room_id_; }
+        int numUsersLoaded() const { return numUsersLoaded_; }
+        bool loadingMoreMembers() const { return loadingMoreMembers_; }
+
+signals:
+        void roomNameChanged();
+        void memberCountChanged();
+        void avatarUrlChanged();
+        void roomIdChanged();
+        void numUsersLoadedChanged();
+        void loadingMoreMembersChanged();
+
+public slots:
+        void addUsers(const std::vector<RoomMember> &users);
+
+protected:
+        bool canFetchMore(const QModelIndex &) const override;
+        void fetchMore(const QModelIndex &) override;
+
+private:
+        QVector<QPair<RoomMember, QString>> m_memberList;
+        QString room_id_;
+        RoomInfo info_;
+        int numUsersLoaded_{0};
+        bool loadingMoreMembers_{false};
+};
diff --git a/src/MxcImageProvider.cpp b/src/MxcImageProvider.cpp
index ab6540a4..b8648269 100644
--- a/src/MxcImageProvider.cpp
+++ b/src/MxcImageProvider.cpp
@@ -22,7 +22,14 @@ QHash<QString, mtx::crypto::EncryptedFile> infos;
 QQuickImageResponse *
 MxcImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
 {
-        MxcImageResponse *response = new MxcImageResponse(id, requestedSize);
+        auto id_  = id;
+        bool crop = true;
+        if (id.endsWith("?scale")) {
+                crop = false;
+                id_.remove("?scale");
+        }
+
+        MxcImageResponse *response = new MxcImageResponse(id_, crop, requestedSize);
         pool.start(response);
         return response;
 }
@@ -36,20 +43,24 @@ void
 MxcImageResponse::run()
 {
         MxcImageProvider::download(
-          m_id, m_requestedSize, [this](QString, QSize, QImage image, QString) {
+          m_id,
+          m_requestedSize,
+          [this](QString, QSize, QImage image, QString) {
                   if (image.isNull()) {
                           m_error = "Failed to download image.";
                   } else {
                           m_image = image;
                   }
                   emit finished();
-          });
+          },
+          m_crop);
 }
 
 void
 MxcImageProvider::download(const QString &id,
                            const QSize &requestedSize,
-                           std::function<void(QString, QSize, QImage, QString)> then)
+                           std::function<void(QString, QSize, QImage, QString)> then,
+                           bool crop)
 {
         std::optional<mtx::crypto::EncryptedFile> encryptionInfo;
         auto temp = infos.find("mxc://" + id);
@@ -58,11 +69,12 @@ MxcImageProvider::download(const QString &id,
 
         if (requestedSize.isValid() && !encryptionInfo) {
                 QString fileName =
-                  QString("%1_%2x%3_crop")
+                  QString("%1_%2x%3_%4")
                     .arg(QString::fromUtf8(id.toUtf8().toBase64(QByteArray::Base64UrlEncoding |
                                                                 QByteArray::OmitTrailingEquals)))
                     .arg(requestedSize.width())
-                    .arg(requestedSize.height());
+                    .arg(requestedSize.height())
+                    .arg(crop ? "crop" : "scale");
                 QFileInfo fileInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
                                      "/media_cache",
                                    fileName);
@@ -85,7 +97,7 @@ MxcImageProvider::download(const QString &id,
                 opts.mxc_url = "mxc://" + id.toStdString();
                 opts.width   = requestedSize.width() > 0 ? requestedSize.width() : -1;
                 opts.height  = requestedSize.height() > 0 ? requestedSize.height() : -1;
-                opts.method  = "crop";
+                opts.method  = crop ? "crop" : "scale";
                 http::client()->get_thumbnail(
                   opts,
                   [fileInfo, requestedSize, then, id](const std::string &res,
@@ -196,7 +208,6 @@ MxcImageProvider::download(const QString &id,
                                   image.setText("original filename",
                                                 QString::fromStdString(originalFilename));
                                   image.setText("mxc url", "mxc://" + id);
-                                  image.save(fileInfo.absoluteFilePath());
                                   then(id, requestedSize, image, fileInfo.absoluteFilePath());
                           });
                 } catch (std::exception &e) {
diff --git a/src/MxcImageProvider.h b/src/MxcImageProvider.h
index 7b960836..61d82852 100644
--- a/src/MxcImageProvider.h
+++ b/src/MxcImageProvider.h
@@ -19,9 +19,10 @@ class MxcImageResponse
   , public QRunnable
 {
 public:
-        MxcImageResponse(const QString &id, const QSize &requestedSize)
+        MxcImageResponse(const QString &id, bool crop, const QSize &requestedSize)
           : m_id(id)
           , m_requestedSize(requestedSize)
+          , m_crop(crop)
         {
                 setAutoDelete(false);
         }
@@ -37,6 +38,7 @@ public:
         QString m_id, m_error;
         QSize m_requestedSize;
         QImage m_image;
+        bool m_crop;
 };
 
 class MxcImageProvider
@@ -51,7 +53,8 @@ public slots:
         static void addEncryptionInfo(mtx::crypto::EncryptedFile info);
         static void download(const QString &id,
                              const QSize &requestedSize,
-                             std::function<void(QString, QSize, QImage, QString)> then);
+                             std::function<void(QString, QSize, QImage, QString)> then,
+                             bool crop = true);
 
 private:
         QThreadPool pool;
diff --git a/src/Olm.cpp b/src/Olm.cpp
index 18e2ddcf..e4ab0aa1 100644
--- a/src/Olm.cpp
+++ b/src/Olm.cpp
@@ -212,14 +212,21 @@ handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKey
         nhlog::crypto()->info("sender    : {}", msg.sender);
         nhlog::crypto()->info("sender_key: {}", msg.sender_key);
 
+        if (msg.sender_key == olm::client()->identity_keys().ed25519) {
+                nhlog::crypto()->warn("Ignoring olm message from ourselves!");
+                return;
+        }
+
         const auto my_key = olm::client()->identity_keys().curve25519;
 
+        bool failed_decryption = false;
+
         for (const auto &cipher : msg.ciphertext) {
                 // We skip messages not meant for the current device.
                 if (cipher.first != my_key) {
                         nhlog::crypto()->debug(
                           "Skipping message for {} since we are {}.", cipher.first, my_key);
-                        return;
+                        continue;
                 }
 
                 const auto type = cipher.second.type;
@@ -234,6 +241,7 @@ handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKey
                                   msg.sender, msg.sender_key, cipher.second);
                         } else {
                                 nhlog::crypto()->error("Undecryptable olm message!");
+                                failed_decryption = true;
                                 continue;
                         }
                 }
@@ -278,11 +286,17 @@ handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKey
 
                         bool from_their_device = false;
                         for (auto [device_id, key] : otherUserDeviceKeys.device_keys) {
-                                if (key.keys.at("curve25519:" + device_id) == msg.sender_key) {
-                                        if (key.keys.at("ed25519:" + device_id) == sender_ed25519) {
-                                                from_their_device = true;
-                                                break;
-                                        }
+                                auto c_key = key.keys.find("curve25519:" + device_id);
+                                auto e_key = key.keys.find("ed25519:" + device_id);
+
+                                if (c_key == key.keys.end() || e_key == key.keys.end()) {
+                                        nhlog::crypto()->warn(
+                                          "Skipping device {} as we have no keys for it.",
+                                          device_id);
+                                } else if (c_key->second == msg.sender_key &&
+                                           e_key->second == sender_ed25519) {
+                                        from_their_device = true;
+                                        break;
                                 }
                         }
                         if (!from_their_device) {
@@ -423,22 +437,28 @@ handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKey
                         }
 
                         return;
+                } else {
+                        failed_decryption = true;
                 }
         }
 
-        try {
-                std::map<std::string, std::vector<std::string>> targets;
-                for (auto [device_id, key] : otherUserDeviceKeys.device_keys) {
-                        if (key.keys.at("curve25519:" + device_id) == msg.sender_key)
-                                targets[msg.sender].push_back(device_id);
-                }
+        if (failed_decryption) {
+                try {
+                        std::map<std::string, std::vector<std::string>> targets;
+                        for (auto [device_id, key] : otherUserDeviceKeys.device_keys) {
+                                if (key.keys.at("curve25519:" + device_id) == msg.sender_key)
+                                        targets[msg.sender].push_back(device_id);
+                        }
 
-                send_encrypted_to_device_messages(
-                  targets, mtx::events::DeviceEvent<mtx::events::msg::Dummy>{}, true);
-                nhlog::crypto()->info(
-                  "Recovering from broken olm channel with {}:{}", msg.sender, msg.sender_key);
-        } catch (std::exception &e) {
-                nhlog::crypto()->error("Failed to recover from broken olm sessions: {}", e.what());
+                        send_encrypted_to_device_messages(
+                          targets, mtx::events::DeviceEvent<mtx::events::msg::Dummy>{}, true);
+                        nhlog::crypto()->info("Recovering from broken olm channel with {}:{}",
+                                              msg.sender,
+                                              msg.sender_key);
+                } catch (std::exception &e) {
+                        nhlog::crypto()->error("Failed to recover from broken olm sessions: {}",
+                                               e.what());
+                }
         }
 }
 
@@ -504,7 +524,8 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
 
         auto own_user_id = http::client()->user_id().to_string();
 
-        auto members = cache::client()->getMembersWithKeys(room_id);
+        auto members = cache::client()->getMembersWithKeys(
+          room_id, UserSettings::instance()->onlyShareKeysWithVerifiedUsers());
 
         std::map<std::string, std::vector<std::string>> sendSessionTo;
         mtx::crypto::OutboundGroupSessionPtr session = nullptr;
@@ -955,13 +976,12 @@ handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyR
                 }
         }
 
-        if (!verifiedDevice && !shouldSeeKeys &&
-            !utils::respondsToKeyRequests(req.content.room_id)) {
+        if (!verifiedDevice && !shouldSeeKeys) {
                 nhlog::crypto()->debug("ignoring key request for room {}", req.content.room_id);
                 return;
         }
 
-        if (verifiedDevice || utils::respondsToKeyRequests(req.content.room_id)) {
+        if (verifiedDevice) {
                 // share the minimum index we have
                 minimumIndex = -1;
         }
@@ -1008,7 +1028,8 @@ send_megolm_key_to_device(const std::string &user_id,
 
 DecryptionResult
 decryptEvent(const MegolmSessionIndex &index,
-             const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &event)
+             const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &event,
+             bool dont_write_db)
 {
         try {
                 if (!cache::client()->inboundMegolmSessionExists(index)) {
@@ -1023,10 +1044,26 @@ decryptEvent(const MegolmSessionIndex &index,
         std::string msg_str;
         try {
                 auto session = cache::client()->getInboundMegolmSession(index);
+                auto sessionData =
+                  cache::client()->getMegolmSessionData(index).value_or(GroupSessionData{});
 
                 auto res =
                   olm::client()->decrypt_group_message(session.get(), event.content.ciphertext);
                 msg_str = std::string((char *)res.data.data(), res.data.size());
+
+                if (!event.event_id.empty() && event.event_id[0] == '$') {
+                        auto oldIdx = sessionData.indices.find(res.message_index);
+                        if (oldIdx != sessionData.indices.end()) {
+                                if (oldIdx->second != event.event_id)
+                                        return {DecryptionErrorCode::ReplayAttack,
+                                                std::nullopt,
+                                                std::nullopt};
+                        } else if (!dont_write_db) {
+                                sessionData.indices[res.message_index] = event.event_id;
+                                cache::client()->saveInboundMegolmSession(
+                                  index, std::move(session), sessionData);
+                        }
+                }
         } catch (const lmdb::error &e) {
                 return {DecryptionErrorCode::DbError, e.what(), std::nullopt};
         } catch (const mtx::crypto::olm_exception &e) {
@@ -1035,24 +1072,24 @@ decryptEvent(const MegolmSessionIndex &index,
                 return {DecryptionErrorCode::DecryptionFailed, e.what(), std::nullopt};
         }
 
-        // Add missing fields for the event.
-        json body                = json::parse(msg_str);
-        body["event_id"]         = event.event_id;
-        body["sender"]           = event.sender;
-        body["origin_server_ts"] = event.origin_server_ts;
-        body["unsigned"]         = event.unsigned_data;
+        try {
+                // Add missing fields for the event.
+                json body                = json::parse(msg_str);
+                body["event_id"]         = event.event_id;
+                body["sender"]           = event.sender;
+                body["origin_server_ts"] = event.origin_server_ts;
+                body["unsigned"]         = event.unsigned_data;
 
-        // relations are unencrypted in content...
-        mtx::common::add_relations(body["content"], event.content.relations);
+                // relations are unencrypted in content...
+                mtx::common::add_relations(body["content"], event.content.relations);
 
-        mtx::events::collections::TimelineEvent te;
-        try {
+                mtx::events::collections::TimelineEvent te;
                 mtx::events::collections::from_json(body, te);
+
+                return {DecryptionErrorCode::NoError, std::nullopt, std::move(te.data)};
         } catch (std::exception &e) {
                 return {DecryptionErrorCode::ParsingFailed, e.what(), std::nullopt};
         }
-
-        return {std::nullopt, std::nullopt, std::move(te.data)};
 }
 
 crypto::Trust
@@ -1081,6 +1118,8 @@ send_encrypted_to_device_messages(const std::map<std::string, std::vector<std::s
           messages;
         std::map<std::string, std::map<std::string, DevicePublicKeys>> pks;
 
+        auto our_curve = olm::client()->identity_keys().curve25519;
+
         for (const auto &[user, devices] : targets) {
                 auto deviceKeys = cache::client()->userKeys(user);
 
@@ -1114,12 +1153,32 @@ send_encrypted_to_device_messages(const std::map<std::string, std::vector<std::s
                                 continue;
                         }
 
-                        auto session =
-                          cache::getLatestOlmSession(d.keys.at("curve25519:" + device));
+                        auto device_curve = d.keys.at("curve25519:" + device);
+                        if (device_curve == our_curve) {
+                                nhlog::crypto()->warn("Skipping our own device, since sending "
+                                                      "ourselves olm messages makes no sense.");
+                                continue;
+                        }
+
+                        auto session = cache::getLatestOlmSession(device_curve);
                         if (!session || force_new_session) {
-                                claims.one_time_keys[user][device] = mtx::crypto::SIGNED_CURVE25519;
-                                pks[user][device].ed25519          = d.keys.at("ed25519:" + device);
-                                pks[user][device].curve25519 = d.keys.at("curve25519:" + device);
+                                static QMap<QPair<std::string, std::string>, qint64> rateLimit;
+                                auto currentTime = QDateTime::currentSecsSinceEpoch();
+                                if (rateLimit.value(QPair(user, device)) + 60 * 60 * 10 <
+                                    currentTime) {
+                                        claims.one_time_keys[user][device] =
+                                          mtx::crypto::SIGNED_CURVE25519;
+                                        pks[user][device].ed25519 = d.keys.at("ed25519:" + device);
+                                        pks[user][device].curve25519 =
+                                          d.keys.at("curve25519:" + device);
+
+                                        rateLimit.insert(QPair(user, device), currentTime);
+                                } else {
+                                        nhlog::crypto()->warn("Not creating new session with {}:{} "
+                                                              "because of rate limit",
+                                                              user,
+                                                              device);
+                                }
                                 continue;
                         }
 
@@ -1129,7 +1188,7 @@ send_encrypted_to_device_messages(const std::map<std::string, std::vector<std::s
                                                            ev_json,
                                                            UserId(user),
                                                            d.keys.at("ed25519:" + device),
-                                                           d.keys.at("curve25519:" + device))
+                                                           device_curve)
                             .get<mtx::events::msg::OlmEncrypted>();
 
                         try {
@@ -1187,22 +1246,40 @@ send_encrypted_to_device_messages(const std::map<std::string, std::vector<std::s
                                                 continue;
                                         }
 
-                                        // TODO: Verify signatures
                                         auto otk = rd.second.begin()->at("key");
 
-                                        auto id_key = pks.at(user_id).at(device_id).curve25519;
+                                        auto sign_key = pks.at(user_id).at(device_id).ed25519;
+                                        auto id_key   = pks.at(user_id).at(device_id).curve25519;
+
+                                        // Verify signature
+                                        {
+                                                auto signedKey = *rd.second.begin();
+                                                std::string signature =
+                                                  signedKey["signatures"][user_id].value(
+                                                    "ed25519:" + device_id, "");
+
+                                                if (signature.empty() ||
+                                                    !mtx::crypto::ed25519_verify_signature(
+                                                      sign_key, signedKey, signature)) {
+                                                        nhlog::net()->warn(
+                                                          "Skipping device {} as its one time key "
+                                                          "has an invalid signature.",
+                                                          device_id);
+                                                        continue;
+                                                }
+                                        }
+
                                         auto session =
                                           olm::client()->create_outbound_session(id_key, otk);
 
                                         messages[mtx::identifiers::parse<mtx::identifiers::User>(
                                           user_id)][device_id] =
                                           olm::client()
-                                            ->create_olm_encrypted_content(
-                                              session.get(),
-                                              ev_json,
-                                              UserId(user_id),
-                                              pks.at(user_id).at(device_id).ed25519,
-                                              id_key)
+                                            ->create_olm_encrypted_content(session.get(),
+                                                                           ev_json,
+                                                                           UserId(user_id),
+                                                                           sign_key,
+                                                                           id_key)
                                             .get<mtx::events::msg::OlmEncrypted>();
 
                                         try {
@@ -1248,8 +1325,8 @@ send_encrypted_to_device_messages(const std::map<std::string, std::vector<std::s
                 req.device_keys = keysToQuery;
                 http::client()->query_keys(
                   req,
-                  [ev_json, BindPks](const mtx::responses::QueryKeys &res,
-                                     mtx::http::RequestErr err) {
+                  [ev_json, BindPks, our_curve](const mtx::responses::QueryKeys &res,
+                                                mtx::http::RequestErr err) {
                           if (err) {
                                   nhlog::net()->warn("failed to query device keys: {} {}",
                                                      err->matrix_error.error,
@@ -1291,6 +1368,13 @@ send_encrypted_to_device_messages(const std::map<std::string, std::vector<std::s
                                           pks.ed25519    = device_keys.at(edKey);
                                           pks.curve25519 = device_keys.at(curveKey);
 
+                                          if (pks.curve25519 == our_curve) {
+                                                  nhlog::crypto()->warn(
+                                                    "Skipping our own device, since sending "
+                                                    "ourselves olm messages makes no sense.");
+                                                  continue;
+                                          }
+
                                           try {
                                                   if (!mtx::crypto::verify_identity_signature(
                                                         dev.second, device_id, user_id)) {
@@ -1360,9 +1444,12 @@ request_cross_signing_keys()
                   body,
                   [request_id = secretRequest.request_id, secretName](mtx::http::RequestErr err) {
                           if (err) {
-                                  request_id_to_secret_name.erase(request_id);
                                   nhlog::net()->error("Failed to send request for secrect '{}'",
                                                       secretName);
+                                  // Cancel request on UI thread
+                                  QTimer::singleShot(1, cache::client(), [request_id]() {
+                                          request_id_to_secret_name.erase(request_id);
+                                  });
                                   return;
                           }
                   });
diff --git a/src/Olm.h b/src/Olm.h
index a18cbbfb..ab86ca00 100644
--- a/src/Olm.h
+++ b/src/Olm.h
@@ -14,9 +14,11 @@
 constexpr auto OLM_ALGO = "m.olm.v1.curve25519-aes-sha2";
 
 namespace olm {
+Q_NAMESPACE
 
-enum class DecryptionErrorCode
+enum DecryptionErrorCode
 {
+        NoError,
         MissingSession, // Session was not found, retrieve from backup or request from other devices
                         // and try again
         MissingSessionIndex, // Session was found, but it does not reach back enough to this index,
@@ -25,14 +27,13 @@ enum class DecryptionErrorCode
         DecryptionFailed,    // libolm error
         ParsingFailed,       // Failed to parse the actual event
         ReplayAttack,        // Megolm index reused
-        UnknownFingerprint,  // Unknown device Fingerprint
 };
+Q_ENUM_NS(DecryptionErrorCode)
 
 struct DecryptionResult
 {
-        std::optional<DecryptionErrorCode> error;
+        DecryptionErrorCode error;
         std::optional<std::string> error_message;
-
         std::optional<mtx::events::collections::TimelineEvents> event;
 };
 
@@ -80,9 +81,11 @@ encrypt_group_message(const std::string &room_id,
                       const std::string &device_id,
                       nlohmann::json body);
 
+//! Decrypt an event. Use dont_write_db to prevent db writes when already in a write transaction.
 DecryptionResult
 decryptEvent(const MegolmSessionIndex &index,
-             const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &event);
+             const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &event,
+             bool dont_write_db = false);
 crypto::Trust
 calculate_trust(const std::string &user_id, const std::string &curve25519);
 
diff --git a/src/ReadReceiptsModel.cpp b/src/ReadReceiptsModel.cpp
new file mode 100644
index 00000000..25262c59
--- /dev/null
+++ b/src/ReadReceiptsModel.cpp
@@ -0,0 +1,131 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "ReadReceiptsModel.h"
+
+#include <QLocale>
+
+#include "Cache.h"
+#include "Cache_p.h"
+#include "Logging.h"
+#include "Utils.h"
+
+ReadReceiptsModel::ReadReceiptsModel(QString event_id, QString room_id, QObject *parent)
+  : QAbstractListModel{parent}
+  , event_id_{event_id}
+  , room_id_{room_id}
+{
+        try {
+                addUsers(cache::readReceipts(event_id_, room_id_));
+        } catch (const lmdb::error &) {
+                nhlog::db()->warn("failed to retrieve read receipts for {} {}",
+                                  event_id_.toStdString(),
+                                  room_id_.toStdString());
+
+                return;
+        }
+
+        connect(cache::client(), &Cache::newReadReceipts, this, &ReadReceiptsModel::update);
+}
+
+void
+ReadReceiptsModel::update()
+{
+        try {
+                addUsers(cache::readReceipts(event_id_, room_id_));
+        } catch (const lmdb::error &) {
+                nhlog::db()->warn("failed to retrieve read receipts for {} {}",
+                                  event_id_.toStdString(),
+                                  room_id_.toStdString());
+
+                return;
+        }
+}
+
+QHash<int, QByteArray>
+ReadReceiptsModel::roleNames() const
+{
+        // Note: RawTimestamp is purposely not included here
+        return {
+          {Mxid, "mxid"},
+          {DisplayName, "displayName"},
+          {AvatarUrl, "avatarUrl"},
+          {Timestamp, "timestamp"},
+        };
+}
+
+QVariant
+ReadReceiptsModel::data(const QModelIndex &index, int role) const
+{
+        if (!index.isValid() || index.row() >= (int)readReceipts_.size() || index.row() < 0)
+                return {};
+
+        switch (role) {
+        case Mxid:
+                return readReceipts_[index.row()].first;
+        case DisplayName:
+                return cache::displayName(room_id_, readReceipts_[index.row()].first);
+        case AvatarUrl:
+                return cache::avatarUrl(room_id_, readReceipts_[index.row()].first);
+        case Timestamp:
+                return dateFormat(readReceipts_[index.row()].second);
+        case RawTimestamp:
+                return readReceipts_[index.row()].second;
+        default:
+                return {};
+        }
+}
+
+void
+ReadReceiptsModel::addUsers(
+  const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users)
+{
+        auto newReceipts = users.size() - readReceipts_.size();
+
+        if (newReceipts > 0) {
+                beginInsertRows(
+                  QModelIndex{}, readReceipts_.size(), readReceipts_.size() + newReceipts - 1);
+
+                for (const auto &user : users) {
+                        QPair<QString, QDateTime> item = {
+                          QString::fromStdString(user.second),
+                          QDateTime::fromMSecsSinceEpoch(user.first)};
+                        if (!readReceipts_.contains(item))
+                                readReceipts_.push_back(item);
+                }
+
+                endInsertRows();
+        }
+}
+
+QString
+ReadReceiptsModel::dateFormat(const QDateTime &then) const
+{
+        auto now  = QDateTime::currentDateTime();
+        auto days = then.daysTo(now);
+
+        if (days == 0)
+                return QLocale::system().toString(then.time(), QLocale::ShortFormat);
+        else if (days < 2)
+                return tr("Yesterday, %1")
+                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
+        else if (days < 7)
+                //: %1 is the name of the current day, %2 is the time the read receipt was read. The
+                //: result may look like this: Monday, 7:15
+                return QString("%1, %2")
+                  .arg(then.toString("dddd"))
+                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
+
+        return QLocale::system().toString(then.time(), QLocale::ShortFormat);
+}
+
+ReadReceiptsProxy::ReadReceiptsProxy(QString event_id, QString room_id, QObject *parent)
+  : QSortFilterProxyModel{parent}
+  , model_{event_id, room_id, this}
+{
+        setSourceModel(&model_);
+        setSortRole(ReadReceiptsModel::RawTimestamp);
+        sort(0, Qt::DescendingOrder);
+        setDynamicSortFilter(true);
+}
diff --git a/src/ReadReceiptsModel.h b/src/ReadReceiptsModel.h
new file mode 100644
index 00000000..3b45716c
--- /dev/null
+++ b/src/ReadReceiptsModel.h
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef READRECEIPTSMODEL_H
+#define READRECEIPTSMODEL_H
+
+#include <QAbstractListModel>
+#include <QDateTime>
+#include <QObject>
+#include <QSortFilterProxyModel>
+#include <QString>
+
+class ReadReceiptsModel : public QAbstractListModel
+{
+        Q_OBJECT
+
+public:
+        enum Roles
+        {
+                Mxid,
+                DisplayName,
+                AvatarUrl,
+                Timestamp,
+                RawTimestamp,
+        };
+
+        explicit ReadReceiptsModel(QString event_id, QString room_id, QObject *parent = nullptr);
+
+        QString eventId() const { return event_id_; }
+        QString roomId() const { return room_id_; }
+
+        QHash<int, QByteArray> roleNames() const override;
+        int rowCount(const QModelIndex &parent) const override
+        {
+                Q_UNUSED(parent)
+                return readReceipts_.size();
+        }
+        QVariant data(const QModelIndex &index, int role) const override;
+
+public slots:
+        void addUsers(const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users);
+        void update();
+
+private:
+        QString dateFormat(const QDateTime &then) const;
+
+        QString event_id_;
+        QString room_id_;
+        QVector<QPair<QString, QDateTime>> readReceipts_;
+};
+
+class ReadReceiptsProxy : public QSortFilterProxyModel
+{
+        Q_OBJECT
+
+        Q_PROPERTY(QString eventId READ eventId CONSTANT)
+        Q_PROPERTY(QString roomId READ roomId CONSTANT)
+
+public:
+        explicit ReadReceiptsProxy(QString event_id, QString room_id, QObject *parent = nullptr);
+
+        QString eventId() const { return event_id_; }
+        QString roomId() const { return room_id_; }
+
+private:
+        QString event_id_;
+        QString room_id_;
+
+        ReadReceiptsModel model_;
+};
+
+#endif // READRECEIPTSMODEL_H
diff --git a/src/RegisterPage.cpp b/src/RegisterPage.cpp
index 1588d07d..bae24df0 100644
--- a/src/RegisterPage.cpp
+++ b/src/RegisterPage.cpp
@@ -12,6 +12,7 @@
 
 #include <mtx/responses/register.hpp>
 #include <mtx/responses/well-known.hpp>
+#include <mtxclient/http/client.hpp>
 
 #include "Config.h"
 #include "Logging.h"
@@ -93,6 +94,7 @@ RegisterPage::RegisterPage(QWidget *parent)
 
         server_input_ = new TextField();
         server_input_->setLabel(tr("Homeserver"));
+        server_input_->setRegexp(QRegularExpression(".+"));
         server_input_->setToolTip(
           tr("A server that allows registration. Since matrix is decentralized, you need to first "
              "find a server you can register on or host your own."));
@@ -145,178 +147,39 @@ RegisterPage::RegisterPage(QWidget *parent)
         top_layout_->addLayout(button_layout_);
         top_layout_->addWidget(error_label_, 0, Qt::AlignHCenter);
         top_layout_->addStretch(1);
-
-        connect(
-          this,
-          &RegisterPage::versionErrorCb,
-          this,
-          [this](const QString &msg) {
-                  error_server_label_->show();
-                  server_input_->setValid(false);
-                  showError(error_server_label_, msg);
-          },
-          Qt::QueuedConnection);
+        setLayout(top_layout_);
 
         connect(back_button_, SIGNAL(clicked()), this, SLOT(onBackButtonClicked()));
         connect(register_button_, SIGNAL(clicked()), this, SLOT(onRegisterButtonClicked()));
 
         connect(username_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
-        connect(username_input_, &TextField::editingFinished, this, &RegisterPage::checkFields);
+        connect(username_input_, &TextField::editingFinished, this, &RegisterPage::checkUsername);
         connect(password_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
-        connect(password_input_, &TextField::editingFinished, this, &RegisterPage::checkFields);
+        connect(password_input_, &TextField::editingFinished, this, &RegisterPage::checkPassword);
         connect(password_confirmation_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
-        connect(
-          password_confirmation_, &TextField::editingFinished, this, &RegisterPage::checkFields);
+        connect(password_confirmation_,
+                &TextField::editingFinished,
+                this,
+                &RegisterPage::checkPasswordConfirmation);
         connect(server_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
-        connect(server_input_, &TextField::editingFinished, this, &RegisterPage::checkFields);
-        connect(this, &RegisterPage::registerErrorCb, this, [this](const QString &msg) {
-                showError(msg);
-        });
-        connect(
-          this,
-          &RegisterPage::registrationFlow,
-          this,
-          [this](const std::string &user,
-                 const std::string &pass,
-                 const mtx::user_interactive::Unauthorized &unauthorized) {
-                  auto completed_stages = unauthorized.completed;
-                  auto flows            = unauthorized.flows;
-                  auto session = unauthorized.session.empty() ? http::client()->generate_txn_id()
-                                                              : unauthorized.session;
-
-                  nhlog::ui()->info("Completed stages: {}", completed_stages.size());
-
-                  if (!completed_stages.empty())
-                          flows.erase(std::remove_if(
-                                        flows.begin(),
-                                        flows.end(),
-                                        [completed_stages](auto flow) {
-                                                if (completed_stages.size() > flow.stages.size())
-                                                        return true;
-                                                for (size_t f = 0; f < completed_stages.size(); f++)
-                                                        if (completed_stages[f] != flow.stages[f])
-                                                                return true;
-                                                return false;
-                                        }),
-                                      flows.end());
-
-                  if (flows.empty()) {
-                          nhlog::net()->error("No available registration flows!");
-                          emit registerErrorCb(tr("No supported registration flows!"));
-                          return;
-                  }
-
-                  auto current_stage = flows.front().stages.at(completed_stages.size());
-
-                  if (current_stage == mtx::user_interactive::auth_types::recaptcha) {
-                          auto captchaDialog =
-                            new dialogs::ReCaptcha(QString::fromStdString(session), this);
-
-                          connect(captchaDialog,
-                                  &dialogs::ReCaptcha::confirmation,
-                                  this,
-                                  [this, user, pass, session, captchaDialog]() {
-                                          captchaDialog->close();
-                                          captchaDialog->deleteLater();
-
-                                          emit registerAuth(
-                                            user,
-                                            pass,
-                                            mtx::user_interactive::Auth{
-                                              session, mtx::user_interactive::auth::Fallback{}});
-                                  });
-                          connect(captchaDialog,
-                                  &dialogs::ReCaptcha::cancel,
-                                  this,
-                                  &RegisterPage::errorOccurred);
-
-                          QTimer::singleShot(
-                            1000, this, [captchaDialog]() { captchaDialog->show(); });
-                  } else if (current_stage == mtx::user_interactive::auth_types::dummy) {
-                          emit registerAuth(user,
-                                            pass,
-                                            mtx::user_interactive::Auth{
-                                              session, mtx::user_interactive::auth::Dummy{}});
-                  } else {
-                          // use fallback
-                          auto dialog =
-                            new dialogs::FallbackAuth(QString::fromStdString(current_stage),
-                                                      QString::fromStdString(session),
-                                                      this);
-
-                          connect(dialog,
-                                  &dialogs::FallbackAuth::confirmation,
-                                  this,
-                                  [this, user, pass, session, dialog]() {
-                                          dialog->close();
-                                          dialog->deleteLater();
-
-                                          emit registerAuth(
-                                            user,
-                                            pass,
-                                            mtx::user_interactive::Auth{
-                                              session, mtx::user_interactive::auth::Fallback{}});
-                                  });
-                          connect(dialog,
-                                  &dialogs::FallbackAuth::cancel,
-                                  this,
-                                  &RegisterPage::errorOccurred);
-
-                          dialog->show();
-                  }
-          });
+        connect(server_input_, &TextField::editingFinished, this, &RegisterPage::checkServer);
 
         connect(
           this,
-          &RegisterPage::registerAuth,
+          &RegisterPage::serverError,
           this,
-          [this](const std::string &user,
-                 const std::string &pass,
-                 const mtx::user_interactive::Auth &auth) {
-                  http::client()->registration(
-                    user,
-                    pass,
-                    auth,
-                    [this, user, pass](const mtx::responses::Register &res,
-                                       mtx::http::RequestErr err) {
-                            if (!err) {
-                                    http::client()->set_user(res.user_id);
-                                    http::client()->set_access_token(res.access_token);
-                                    http::client()->set_device_id(res.device_id);
-
-                                    emit registerOk();
-                                    return;
-                            }
-
-                            // The server requires registration flows.
-                            if (err->status_code == 401) {
-                                    if (err->matrix_error.unauthorized.flows.empty()) {
-                                            nhlog::net()->warn(
-                                              "failed to retrieve registration flows: ({}) "
-                                              "{}",
-                                              static_cast<int>(err->status_code),
-                                              err->matrix_error.error);
-                                            emit registerErrorCb(
-                                              QString::fromStdString(err->matrix_error.error));
-                                            return;
-                                    }
-
-                                    emit registrationFlow(
-                                      user, pass, err->matrix_error.unauthorized);
-                                    return;
-                            }
-
-                            nhlog::net()->warn("failed to register: status_code ({}), "
-                                               "matrix_error: ({}), parser error ({})",
-                                               static_cast<int>(err->status_code),
-                                               err->matrix_error.error,
-                                               err->parse_error);
-
-                            emit registerErrorCb(QString::fromStdString(err->matrix_error.error));
-                    });
-          });
+          [this](const QString &msg) {
+                  server_input_->setValid(false);
+                  showError(error_server_label_, msg);
+          },
+          Qt::QueuedConnection);
 
-        setLayout(top_layout_);
+        connect(this, &RegisterPage::wellKnownLookup, this, &RegisterPage::doWellKnownLookup);
+        connect(this, &RegisterPage::versionsCheck, this, &RegisterPage::doVersionsCheck);
+        connect(this, &RegisterPage::registration, this, &RegisterPage::doRegistration);
+        connect(this, &RegisterPage::UIA, this, &RegisterPage::doUIA);
+        connect(
+          this, &RegisterPage::registrationWithAuth, this, &RegisterPage::doRegistrationWithAuth);
 }
 
 void
@@ -345,192 +208,299 @@ RegisterPage::showError(QLabel *label, const QString &msg)
         int height = rect.height();
         label->setFixedHeight((int)qCeil(width / 200.0) * height);
         label->setText(msg);
+        label->show();
 }
 
 bool
 RegisterPage::checkOneField(QLabel *label, const TextField *t_field, const QString &msg)
 {
         if (t_field->isValid()) {
-                label->setText("");
                 label->hide();
                 return true;
         } else {
-                label->show();
                 showError(label, msg);
                 return false;
         }
 }
 
 bool
-RegisterPage::checkFields()
+RegisterPage::checkUsername()
 {
-        error_label_->setText("");
-        error_username_label_->setText("");
-        error_password_label_->setText("");
-        error_password_confirmation_label_->setText("");
-        error_server_label_->setText("");
+        return checkOneField(error_username_label_,
+                             username_input_,
+                             tr("The username must not be empty, and must contain only the "
+                                "characters a-z, 0-9, ., _, =, -, and /."));
+}
 
-        error_username_label_->hide();
-        error_password_label_->hide();
-        error_password_confirmation_label_->hide();
-        error_server_label_->hide();
+bool
+RegisterPage::checkPassword()
+{
+        return checkOneField(
+          error_password_label_, password_input_, tr("Password is not long enough (min 8 chars)"));
+}
 
-        password_confirmation_->setValid(true);
-        server_input_->setValid(true);
-
-        bool all_fields_good = true;
-        if (username_input_->isModified() &&
-            !checkOneField(error_username_label_,
-                           username_input_,
-                           tr("The username must not be empty, and must contain only the "
-                              "characters a-z, 0-9, ., _, =, -, and /."))) {
-                all_fields_good = false;
-        } else if (password_input_->isModified() &&
-                   !checkOneField(error_password_label_,
-                                  password_input_,
-                                  tr("Password is not long enough (min 8 chars)"))) {
-                all_fields_good = false;
-        } else if (password_confirmation_->isModified() &&
-                   password_input_->text() != password_confirmation_->text()) {
-                error_password_confirmation_label_->show();
+bool
+RegisterPage::checkPasswordConfirmation()
+{
+        if (password_input_->text() == password_confirmation_->text()) {
+                error_password_confirmation_label_->hide();
+                password_confirmation_->setValid(true);
+                return true;
+        } else {
                 showError(error_password_confirmation_label_, tr("Passwords don't match"));
                 password_confirmation_->setValid(false);
-                all_fields_good = false;
-        } else if (server_input_->isModified() &&
-                   (!server_input_->hasAcceptableInput() || server_input_->text().isEmpty())) {
-                error_server_label_->show();
-                showError(error_server_label_, tr("Invalid server name"));
-                server_input_->setValid(false);
-                all_fields_good = false;
-        }
-        if (!username_input_->isModified() || !password_input_->isModified() ||
-            !password_confirmation_->isModified() || !server_input_->isModified()) {
-                all_fields_good = false;
+                return false;
         }
-        return all_fields_good;
+}
+
+bool
+RegisterPage::checkServer()
+{
+        // This doesn't check that the server is reachable,
+        // just that the input is not obviously wrong.
+        return checkOneField(error_server_label_, server_input_, tr("Invalid server name"));
 }
 
 void
 RegisterPage::onRegisterButtonClicked()
 {
-        if (!checkFields()) {
-                showError(error_label_,
-                          tr("One or more fields have invalid inputs. Please correct those issues "
-                             "and try again."));
-                return;
-        } else {
-                auto username = username_input_->text().toStdString();
-                auto password = password_input_->text().toStdString();
-                auto server   = server_input_->text().toStdString();
+        if (checkUsername() && checkPassword() && checkPasswordConfirmation() && checkServer()) {
+                auto server = server_input_->text().toStdString();
 
                 http::client()->set_server(server);
                 http::client()->verify_certificates(
                   !UserSettings::instance()->disableCertificateValidation());
 
-                http::client()->well_known(
-                  [this, username, password](const mtx::responses::WellKnown &res,
-                                             mtx::http::RequestErr err) {
-                          if (err) {
-                                  if (err->status_code == 404) {
-                                          nhlog::net()->info("Autodiscovery: No .well-known.");
-                                          checkVersionAndRegister(username, password);
-                                          return;
-                                  }
-
-                                  if (!err->parse_error.empty()) {
-                                          emit versionErrorCb(tr(
-                                            "Autodiscovery failed. Received malformed response."));
-                                          nhlog::net()->error(
-                                            "Autodiscovery failed. Received malformed response.");
-                                          return;
-                                  }
-
-                                  emit versionErrorCb(tr("Autodiscovery failed. Unknown error when "
-                                                         "requesting .well-known."));
-                                  nhlog::net()->error("Autodiscovery failed. Unknown error when "
-                                                      "requesting .well-known. {} {}",
-                                                      err->status_code,
-                                                      err->error_code);
+                // This starts a chain of `emit`s which ends up doing the
+                // registration. Signals are used rather than normal function
+                // calls so that the dialogs used in UIA work correctly.
+                //
+                // The sequence of events looks something like this:
+                //
+                // dowellKnownLookup
+                //   v
+                // doVersionsCheck
+                //   v
+                // doRegistration
+                //   v
+                // doUIA <-----------------+
+                //   v					   | More auth required
+                // doRegistrationWithAuth -+
+                //                         | Success
+                // 						   v
+                //                     registering
+
+                emit wellKnownLookup();
+
+                emit registering();
+        }
+}
+
+void
+RegisterPage::doWellKnownLookup()
+{
+        http::client()->well_known(
+          [this](const mtx::responses::WellKnown &res, mtx::http::RequestErr err) {
+                  if (err) {
+                          if (err->status_code == 404) {
+                                  nhlog::net()->info("Autodiscovery: No .well-known.");
+                                  // Check that the homeserver can be reached
+                                  emit versionsCheck();
                                   return;
                           }
 
-                          nhlog::net()->info("Autodiscovery: Discovered '" +
-                                             res.homeserver.base_url + "'");
-                          http::client()->set_server(res.homeserver.base_url);
-                          checkVersionAndRegister(username, password);
-                  });
+                          if (!err->parse_error.empty()) {
+                                  emit serverError(
+                                    tr("Autodiscovery failed. Received malformed response."));
+                                  nhlog::net()->error(
+                                    "Autodiscovery failed. Received malformed response.");
+                                  return;
+                          }
 
-                emit registering();
-        }
+                          emit serverError(tr("Autodiscovery failed. Unknown error when "
+                                              "requesting .well-known."));
+                          nhlog::net()->error("Autodiscovery failed. Unknown error when "
+                                              "requesting .well-known. {} {}",
+                                              err->status_code,
+                                              err->error_code);
+                          return;
+                  }
+
+                  nhlog::net()->info("Autodiscovery: Discovered '" + res.homeserver.base_url + "'");
+                  http::client()->set_server(res.homeserver.base_url);
+                  // Check that the homeserver can be reached
+                  emit versionsCheck();
+          });
 }
 
 void
-RegisterPage::checkVersionAndRegister(const std::string &username, const std::string &password)
+RegisterPage::doVersionsCheck()
 {
+        // Make a request to /_matrix/client/versions to check the address
+        // given is a Matrix homeserver.
         http::client()->versions(
-          [this, username, password](const mtx::responses::Versions &, mtx::http::RequestErr err) {
+          [this](const mtx::responses::Versions &, mtx::http::RequestErr err) {
                   if (err) {
                           if (err->status_code == 404) {
-                                  emit versionErrorCb(tr("The required endpoints were not found. "
-                                                         "Possibly not a Matrix server."));
+                                  emit serverError(
+                                    tr("The required endpoints were not found. Possibly "
+                                       "not a Matrix server."));
                                   return;
                           }
 
                           if (!err->parse_error.empty()) {
-                                  emit versionErrorCb(tr("Received malformed response. Make sure "
-                                                         "the homeserver domain is valid."));
+                                  emit serverError(
+                                    tr("Received malformed response. Make sure the homeserver "
+                                       "domain is valid."));
                                   return;
                           }
 
-                          emit versionErrorCb(tr(
-                            "An unknown error occured. Make sure the homeserver domain is valid."));
+                          emit serverError(tr("An unknown error occured. Make sure the "
+                                              "homeserver domain is valid."));
                           return;
                   }
 
-                  http::client()->registration(
-                    username,
-                    password,
-                    [this, username, password](const mtx::responses::Register &res,
-                                               mtx::http::RequestErr err) {
-                            if (!err) {
-                                    http::client()->set_user(res.user_id);
-                                    http::client()->set_access_token(res.access_token);
-
-                                    emit registerOk();
-                                    return;
-                            }
-
-                            // The server requires registration flows.
-                            if (err->status_code == 401) {
-                                    if (err->matrix_error.unauthorized.flows.empty()) {
-                                            nhlog::net()->warn(
-                                              "failed to retrieve registration flows1: ({}) "
-                                              "{}",
-                                              static_cast<int>(err->status_code),
-                                              err->matrix_error.error);
-                                            emit errorOccurred();
-                                            emit registerErrorCb(
-                                              QString::fromStdString(err->matrix_error.error));
-                                            return;
-                                    }
-
-                                    emit registrationFlow(
-                                      username, password, err->matrix_error.unauthorized);
-                                    return;
-                            }
-
-                            nhlog::net()->error(
-                              "failed to register: status_code ({}), matrix_error({})",
-                              static_cast<int>(err->status_code),
-                              err->matrix_error.error);
-
-                            emit registerErrorCb(QString::fromStdString(err->matrix_error.error));
-                            emit errorOccurred();
-                    });
+                  // Attempt registration without an `auth` dict
+                  emit registration();
           });
 }
 
 void
+RegisterPage::doRegistration()
+{
+        // These inputs should still be alright, but check just in case
+        if (checkUsername() && checkPassword() && checkPasswordConfirmation()) {
+                auto username = username_input_->text().toStdString();
+                auto password = password_input_->text().toStdString();
+                http::client()->registration(username, password, registrationCb());
+        }
+}
+
+void
+RegisterPage::doRegistrationWithAuth(const mtx::user_interactive::Auth &auth)
+{
+        // These inputs should still be alright, but check just in case
+        if (checkUsername() && checkPassword() && checkPasswordConfirmation()) {
+                auto username = username_input_->text().toStdString();
+                auto password = password_input_->text().toStdString();
+                http::client()->registration(username, password, auth, registrationCb());
+        }
+}
+
+mtx::http::Callback<mtx::responses::Register>
+RegisterPage::registrationCb()
+{
+        // Return a function to be used as the callback when an attempt at
+        // registration is made.
+        return [this](const mtx::responses::Register &res, mtx::http::RequestErr err) {
+                if (!err) {
+                        http::client()->set_user(res.user_id);
+                        http::client()->set_access_token(res.access_token);
+                        emit registerOk();
+                        return;
+                }
+
+                // The server requires registration flows.
+                if (err->status_code == 401) {
+                        if (err->matrix_error.unauthorized.flows.empty()) {
+                                nhlog::net()->warn("failed to retrieve registration flows: "
+                                                   "status_code({}), matrix_error({}) ",
+                                                   static_cast<int>(err->status_code),
+                                                   err->matrix_error.error);
+                                showError(QString::fromStdString(err->matrix_error.error));
+                                return;
+                        }
+
+                        // Attempt to complete a UIA stage
+                        emit UIA(err->matrix_error.unauthorized);
+                        return;
+                }
+
+                nhlog::net()->error("failed to register: status_code ({}), matrix_error({})",
+                                    static_cast<int>(err->status_code),
+                                    err->matrix_error.error);
+
+                showError(QString::fromStdString(err->matrix_error.error));
+        };
+}
+
+void
+RegisterPage::doUIA(const mtx::user_interactive::Unauthorized &unauthorized)
+{
+        auto completed_stages = unauthorized.completed;
+        auto flows            = unauthorized.flows;
+        auto session =
+          unauthorized.session.empty() ? http::client()->generate_txn_id() : unauthorized.session;
+
+        nhlog::ui()->info("Completed stages: {}", completed_stages.size());
+
+        if (!completed_stages.empty()) {
+                // Get rid of all flows which don't start with the sequence of
+                // stages that have already been completed.
+                flows.erase(
+                  std::remove_if(flows.begin(),
+                                 flows.end(),
+                                 [completed_stages](auto flow) {
+                                         if (completed_stages.size() > flow.stages.size())
+                                                 return true;
+                                         for (size_t f = 0; f < completed_stages.size(); f++)
+                                                 if (completed_stages[f] != flow.stages[f])
+                                                         return true;
+                                         return false;
+                                 }),
+                  flows.end());
+        }
+
+        if (flows.empty()) {
+                nhlog::ui()->error("No available registration flows!");
+                showError(tr("No supported registration flows!"));
+                return;
+        }
+
+        auto current_stage = flows.front().stages.at(completed_stages.size());
+
+        if (current_stage == mtx::user_interactive::auth_types::recaptcha) {
+                auto captchaDialog = new dialogs::ReCaptcha(QString::fromStdString(session), this);
+
+                connect(captchaDialog,
+                        &dialogs::ReCaptcha::confirmation,
+                        this,
+                        [this, session, captchaDialog]() {
+                                captchaDialog->close();
+                                captchaDialog->deleteLater();
+                                doRegistrationWithAuth(mtx::user_interactive::Auth{
+                                  session, mtx::user_interactive::auth::Fallback{}});
+                        });
+
+                connect(
+                  captchaDialog, &dialogs::ReCaptcha::cancel, this, &RegisterPage::errorOccurred);
+
+                QTimer::singleShot(1000, this, [captchaDialog]() { captchaDialog->show(); });
+
+        } else if (current_stage == mtx::user_interactive::auth_types::dummy) {
+                doRegistrationWithAuth(
+                  mtx::user_interactive::Auth{session, mtx::user_interactive::auth::Dummy{}});
+
+        } else {
+                // use fallback
+                auto dialog = new dialogs::FallbackAuth(
+                  QString::fromStdString(current_stage), QString::fromStdString(session), this);
+
+                connect(
+                  dialog, &dialogs::FallbackAuth::confirmation, this, [this, session, dialog]() {
+                          dialog->close();
+                          dialog->deleteLater();
+                          emit registrationWithAuth(mtx::user_interactive::Auth{
+                            session, mtx::user_interactive::auth::Fallback{}});
+                  });
+
+                connect(dialog, &dialogs::FallbackAuth::cancel, this, &RegisterPage::errorOccurred);
+
+                dialog->show();
+        }
+}
+
+void
 RegisterPage::paintEvent(QPaintEvent *)
 {
         QStyleOption opt;
diff --git a/src/RegisterPage.h b/src/RegisterPage.h
index 0e4a45d0..42ea00cb 100644
--- a/src/RegisterPage.h
+++ b/src/RegisterPage.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #include <mtx/user_interactive.hpp>
+#include <mtxclient/http/client.hpp>
 
 class FlatButton;
 class RaisedButton;
@@ -33,17 +34,16 @@ signals:
         void errorOccurred();
 
         //! Used to trigger the corresponding slot outside of the main thread.
-        void versionErrorCb(const QString &err);
+        void serverError(const QString &err);
+
+        void wellKnownLookup();
+        void versionsCheck();
+        void registration();
+        void UIA(const mtx::user_interactive::Unauthorized &unauthorized);
+        void registrationWithAuth(const mtx::user_interactive::Auth &auth);
 
         void registering();
         void registerOk();
-        void registerErrorCb(const QString &msg);
-        void registrationFlow(const std::string &user,
-                              const std::string &pass,
-                              const mtx::user_interactive::Unauthorized &unauthorized);
-        void registerAuth(const std::string &user,
-                          const std::string &pass,
-                          const mtx::user_interactive::Auth &auth);
 
 private slots:
         void onBackButtonClicked();
@@ -51,12 +51,22 @@ private slots:
 
         // function for showing different errors
         void showError(const QString &msg);
+        void showError(QLabel *label, const QString &msg);
 
-private:
         bool checkOneField(QLabel *label, const TextField *t_field, const QString &msg);
-        bool checkFields();
-        void showError(QLabel *label, const QString &msg);
-        void checkVersionAndRegister(const std::string &username, const std::string &password);
+        bool checkUsername();
+        bool checkPassword();
+        bool checkPasswordConfirmation();
+        bool checkServer();
+
+        void doWellKnownLookup();
+        void doVersionsCheck();
+        void doRegistration();
+        void doUIA(const mtx::user_interactive::Unauthorized &unauthorized);
+        void doRegistrationWithAuth(const mtx::user_interactive::Auth &auth);
+        mtx::http::Callback<mtx::responses::Register> registrationCb();
+
+private:
         QVBoxLayout *top_layout_;
 
         QHBoxLayout *back_layout_;
@@ -69,6 +79,7 @@ private:
         QLabel *error_password_label_;
         QLabel *error_password_confirmation_label_;
         QLabel *error_server_label_;
+        QLabel *error_registration_token_label_;
 
         FlatButton *back_button_;
         RaisedButton *register_button_;
@@ -81,4 +92,5 @@ private:
         TextField *password_input_;
         TextField *password_confirmation_;
         TextField *server_input_;
+        TextField *registration_token_input_;
 };
diff --git a/src/SingleImagePackModel.cpp b/src/SingleImagePackModel.cpp
new file mode 100644
index 00000000..7bf55617
--- /dev/null
+++ b/src/SingleImagePackModel.cpp
@@ -0,0 +1,350 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "SingleImagePackModel.h"
+
+#include <QFile>
+#include <QMimeDatabase>
+
+#include "Cache_p.h"
+#include "ChatPage.h"
+#include "Logging.h"
+#include "MatrixClient.h"
+#include "Utils.h"
+#include "timeline/Permissions.h"
+#include "timeline/TimelineModel.h"
+
+Q_DECLARE_METATYPE(mtx::common::ImageInfo)
+
+SingleImagePackModel::SingleImagePackModel(ImagePackInfo pack_, QObject *parent)
+  : QAbstractListModel(parent)
+  , roomid_(std::move(pack_.source_room))
+  , statekey_(std::move(pack_.state_key))
+  , old_statekey_(statekey_)
+  , pack(std::move(pack_.pack))
+{
+        [[maybe_unused]] static auto imageInfoType = qRegisterMetaType<mtx::common::ImageInfo>();
+
+        if (!pack.pack)
+                pack.pack = mtx::events::msc2545::ImagePack::PackDescription{};
+
+        for (const auto &e : pack.images)
+                shortcodes.push_back(e.first);
+
+        connect(this, &SingleImagePackModel::addImage, this, &SingleImagePackModel::addImageCb);
+}
+
+int
+SingleImagePackModel::rowCount(const QModelIndex &) const
+{
+        return (int)shortcodes.size();
+}
+
+QHash<int, QByteArray>
+SingleImagePackModel::roleNames() const
+{
+        return {
+          {Roles::Url, "url"},
+          {Roles::ShortCode, "shortCode"},
+          {Roles::Body, "body"},
+          {Roles::IsEmote, "isEmote"},
+          {Roles::IsSticker, "isSticker"},
+        };
+}
+
+QVariant
+SingleImagePackModel::data(const QModelIndex &index, int role) const
+{
+        if (hasIndex(index.row(), index.column(), index.parent())) {
+                const auto &img = pack.images.at(shortcodes.at(index.row()));
+                switch (role) {
+                case Url:
+                        return QString::fromStdString(img.url);
+                case ShortCode:
+                        return QString::fromStdString(shortcodes.at(index.row()));
+                case Body:
+                        return QString::fromStdString(img.body);
+                case IsEmote:
+                        return img.overrides_usage() ? img.is_emoji() : pack.pack->is_emoji();
+                case IsSticker:
+                        return img.overrides_usage() ? img.is_sticker() : pack.pack->is_sticker();
+                default:
+                        return {};
+                }
+        }
+        return {};
+}
+
+bool
+SingleImagePackModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+        using mtx::events::msc2545::PackUsage;
+
+        if (hasIndex(index.row(), index.column(), index.parent())) {
+                auto &img = pack.images.at(shortcodes.at(index.row()));
+                switch (role) {
+                case ShortCode: {
+                        auto newCode = value.toString().toStdString();
+
+                        // otherwise we delete this by accident
+                        if (pack.images.count(newCode))
+                                return false;
+
+                        auto tmp     = img;
+                        auto oldCode = shortcodes.at(index.row());
+                        pack.images.erase(oldCode);
+                        shortcodes[index.row()] = newCode;
+                        pack.images.insert({newCode, tmp});
+
+                        emit dataChanged(
+                          this->index(index.row()), this->index(index.row()), {Roles::ShortCode});
+                        return true;
+                }
+                case Body:
+                        img.body = value.toString().toStdString();
+                        emit dataChanged(
+                          this->index(index.row()), this->index(index.row()), {Roles::Body});
+                        return true;
+                case IsEmote: {
+                        bool isEmote = value.toBool();
+                        bool isSticker =
+                          img.overrides_usage() ? img.is_sticker() : pack.pack->is_sticker();
+
+                        img.usage.set(PackUsage::Emoji, isEmote);
+                        img.usage.set(PackUsage::Sticker, isSticker);
+
+                        if (img.usage == pack.pack->usage)
+                                img.usage.reset();
+
+                        emit dataChanged(
+                          this->index(index.row()), this->index(index.row()), {Roles::IsEmote});
+
+                        return true;
+                }
+                case IsSticker: {
+                        bool isEmote =
+                          img.overrides_usage() ? img.is_emoji() : pack.pack->is_emoji();
+                        bool isSticker = value.toBool();
+
+                        img.usage.set(PackUsage::Emoji, isEmote);
+                        img.usage.set(PackUsage::Sticker, isSticker);
+
+                        if (img.usage == pack.pack->usage)
+                                img.usage.reset();
+
+                        emit dataChanged(
+                          this->index(index.row()), this->index(index.row()), {Roles::IsSticker});
+
+                        return true;
+                }
+                }
+        }
+        return false;
+}
+
+bool
+SingleImagePackModel::isGloballyEnabled() const
+{
+        if (auto roomPacks =
+              cache::client()->getAccountData(mtx::events::EventType::ImagePackRooms)) {
+                if (auto tmp = std::get_if<
+                      mtx::events::EphemeralEvent<mtx::events::msc2545::ImagePackRooms>>(
+                      &*roomPacks)) {
+                        if (tmp->content.rooms.count(roomid_) &&
+                            tmp->content.rooms.at(roomid_).count(statekey_))
+                                return true;
+                }
+        }
+        return false;
+}
+void
+SingleImagePackModel::setGloballyEnabled(bool enabled)
+{
+        mtx::events::msc2545::ImagePackRooms content{};
+        if (auto roomPacks =
+              cache::client()->getAccountData(mtx::events::EventType::ImagePackRooms)) {
+                if (auto tmp = std::get_if<
+                      mtx::events::EphemeralEvent<mtx::events::msc2545::ImagePackRooms>>(
+                      &*roomPacks)) {
+                        content = tmp->content;
+                }
+        }
+
+        if (enabled)
+                content.rooms[roomid_][statekey_] = {};
+        else
+                content.rooms[roomid_].erase(statekey_);
+
+        http::client()->put_account_data(content, [](mtx::http::RequestErr) {
+                // emit this->globallyEnabledChanged();
+        });
+}
+
+bool
+SingleImagePackModel::canEdit() const
+{
+        if (roomid_.empty())
+                return true;
+        else
+                return Permissions(QString::fromStdString(roomid_))
+                  .canChange(qml_mtx_events::ImagePackInRoom);
+}
+
+void
+SingleImagePackModel::setPackname(QString val)
+{
+        auto val_ = val.toStdString();
+        if (val_ != this->pack.pack->display_name) {
+                this->pack.pack->display_name = val_;
+                emit packnameChanged();
+        }
+}
+
+void
+SingleImagePackModel::setAttribution(QString val)
+{
+        auto val_ = val.toStdString();
+        if (val_ != this->pack.pack->attribution) {
+                this->pack.pack->attribution = val_;
+                emit attributionChanged();
+        }
+}
+
+void
+SingleImagePackModel::setAvatarUrl(QString val)
+{
+        auto val_ = val.toStdString();
+        if (val_ != this->pack.pack->avatar_url) {
+                this->pack.pack->avatar_url = val_;
+                emit avatarUrlChanged();
+        }
+}
+
+void
+SingleImagePackModel::setStatekey(QString val)
+{
+        auto val_ = val.toStdString();
+        if (val_ != statekey_) {
+                statekey_ = val_;
+                emit statekeyChanged();
+        }
+}
+
+void
+SingleImagePackModel::setIsStickerPack(bool val)
+{
+        using mtx::events::msc2545::PackUsage;
+        if (val != pack.pack->is_sticker()) {
+                pack.pack->usage.set(PackUsage::Sticker, val);
+                emit isStickerPackChanged();
+        }
+}
+
+void
+SingleImagePackModel::setIsEmotePack(bool val)
+{
+        using mtx::events::msc2545::PackUsage;
+        if (val != pack.pack->is_emoji()) {
+                pack.pack->usage.set(PackUsage::Emoji, val);
+                emit isEmotePackChanged();
+        }
+}
+
+void
+SingleImagePackModel::save()
+{
+        if (roomid_.empty()) {
+                http::client()->put_account_data(pack, [](mtx::http::RequestErr e) {
+                        if (e)
+                                ChatPage::instance()->showNotification(
+                                  tr("Failed to update image pack: {}")
+                                    .arg(QString::fromStdString(e->matrix_error.error)));
+                });
+        } else {
+                if (old_statekey_ != statekey_) {
+                        http::client()->send_state_event(
+                          roomid_,
+                          to_string(mtx::events::EventType::ImagePackInRoom),
+                          old_statekey_,
+                          nlohmann::json::object(),
+                          [](const mtx::responses::EventId &, mtx::http::RequestErr e) {
+                                  if (e)
+                                          ChatPage::instance()->showNotification(
+                                            tr("Failed to delete old image pack: {}")
+                                              .arg(QString::fromStdString(e->matrix_error.error)));
+                          });
+                }
+
+                http::client()->send_state_event(
+                  roomid_,
+                  statekey_,
+                  pack,
+                  [this](const mtx::responses::EventId &, mtx::http::RequestErr e) {
+                          if (e)
+                                  ChatPage::instance()->showNotification(
+                                    tr("Failed to update image pack: {}")
+                                      .arg(QString::fromStdString(e->matrix_error.error)));
+
+                          nhlog::net()->info("Uploaded image pack: {}", statekey_);
+                  });
+        }
+}
+
+void
+SingleImagePackModel::addStickers(QList<QUrl> files)
+{
+        for (const auto &f : files) {
+                auto file = QFile(f.toLocalFile());
+                if (!file.open(QFile::ReadOnly)) {
+                        ChatPage::instance()->showNotification(
+                          tr("Failed to open image: {}").arg(f.toLocalFile()));
+                        return;
+                }
+
+                auto bytes = file.readAll();
+                auto img   = utils::readImage(bytes);
+
+                mtx::common::ImageInfo info{};
+
+                auto sz = img.size() / 2;
+                if (sz.width() > 512 || sz.height() > 512) {
+                        sz.scale(512, 512, Qt::AspectRatioMode::KeepAspectRatio);
+                }
+
+                info.h    = sz.height();
+                info.w    = sz.width();
+                info.size = bytes.size();
+
+                auto filename = f.fileName().toStdString();
+                http::client()->upload(
+                  bytes.toStdString(),
+                  QMimeDatabase().mimeTypeForFile(f.toLocalFile()).name().toStdString(),
+                  filename,
+                  [this, filename, info](const mtx::responses::ContentURI &uri,
+                                         mtx::http::RequestErr e) {
+                          if (e) {
+                                  ChatPage::instance()->showNotification(
+                                    tr("Failed to upload image: {}")
+                                      .arg(QString::fromStdString(e->matrix_error.error)));
+                                  return;
+                          }
+
+                          emit addImage(uri.content_uri, filename, info);
+                  });
+        }
+}
+void
+SingleImagePackModel::addImageCb(std::string uri, std::string filename, mtx::common::ImageInfo info)
+{
+        mtx::events::msc2545::PackImage img{};
+        img.url  = uri;
+        img.info = info;
+        beginInsertRows(
+          QModelIndex(), static_cast<int>(shortcodes.size()), static_cast<int>(shortcodes.size()));
+
+        pack.images[filename] = img;
+        shortcodes.push_back(filename);
+
+        endInsertRows();
+}
diff --git a/src/SingleImagePackModel.h b/src/SingleImagePackModel.h
new file mode 100644
index 00000000..cd38b3b6
--- /dev/null
+++ b/src/SingleImagePackModel.h
@@ -0,0 +1,93 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QAbstractListModel>
+#include <QList>
+#include <QUrl>
+
+#include <mtx/events/mscs/image_packs.hpp>
+
+#include "CacheStructs.h"
+
+class SingleImagePackModel : public QAbstractListModel
+{
+        Q_OBJECT
+
+        Q_PROPERTY(QString roomid READ roomid CONSTANT)
+        Q_PROPERTY(QString statekey READ statekey WRITE setStatekey NOTIFY statekeyChanged)
+        Q_PROPERTY(
+          QString attribution READ attribution WRITE setAttribution NOTIFY attributionChanged)
+        Q_PROPERTY(QString packname READ packname WRITE setPackname NOTIFY packnameChanged)
+        Q_PROPERTY(QString avatarUrl READ avatarUrl WRITE setAvatarUrl NOTIFY avatarUrlChanged)
+        Q_PROPERTY(
+          bool isStickerPack READ isStickerPack WRITE setIsStickerPack NOTIFY isStickerPackChanged)
+        Q_PROPERTY(bool isEmotePack READ isEmotePack WRITE setIsEmotePack NOTIFY isEmotePackChanged)
+        Q_PROPERTY(bool isGloballyEnabled READ isGloballyEnabled WRITE setGloballyEnabled NOTIFY
+                     globallyEnabledChanged)
+        Q_PROPERTY(bool canEdit READ canEdit CONSTANT)
+
+public:
+        enum Roles
+        {
+                Url = Qt::UserRole,
+                ShortCode,
+                Body,
+                IsEmote,
+                IsSticker,
+        };
+        Q_ENUM(Roles);
+
+        SingleImagePackModel(ImagePackInfo pack_, QObject *parent = nullptr);
+        QHash<int, QByteArray> roleNames() const override;
+        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+        QVariant data(const QModelIndex &index, int role) const override;
+        bool setData(const QModelIndex &index,
+                     const QVariant &value,
+                     int role = Qt::EditRole) override;
+
+        QString roomid() const { return QString::fromStdString(roomid_); }
+        QString statekey() const { return QString::fromStdString(statekey_); }
+        QString packname() const { return QString::fromStdString(pack.pack->display_name); }
+        QString attribution() const { return QString::fromStdString(pack.pack->attribution); }
+        QString avatarUrl() const { return QString::fromStdString(pack.pack->avatar_url); }
+        bool isStickerPack() const { return pack.pack->is_sticker(); }
+        bool isEmotePack() const { return pack.pack->is_emoji(); }
+
+        bool isGloballyEnabled() const;
+        bool canEdit() const;
+        void setGloballyEnabled(bool enabled);
+
+        void setPackname(QString val);
+        void setAttribution(QString val);
+        void setAvatarUrl(QString val);
+        void setStatekey(QString val);
+        void setIsStickerPack(bool val);
+        void setIsEmotePack(bool val);
+
+        Q_INVOKABLE void save();
+        Q_INVOKABLE void addStickers(QList<QUrl> files);
+
+signals:
+        void globallyEnabledChanged();
+        void statekeyChanged();
+        void attributionChanged();
+        void packnameChanged();
+        void avatarUrlChanged();
+        void isEmotePackChanged();
+        void isStickerPackChanged();
+
+        void addImage(std::string uri, std::string filename, mtx::common::ImageInfo info);
+
+private slots:
+        void addImageCb(std::string uri, std::string filename, mtx::common::ImageInfo info);
+
+private:
+        std::string roomid_;
+        std::string statekey_, old_statekey_;
+
+        mtx::events::msc2545::ImagePack pack;
+        std::vector<std::string> shortcodes;
+};
diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index ffaebe61..ab6ac492 100644
--- a/src/UserSettingsPage.cpp
+++ b/src/UserSettingsPage.cpp
@@ -90,13 +90,11 @@ UserSettings::load(std::optional<QString> profile)
         decryptSidebar_       = settings.value("user/decrypt_sidebar", true).toBool();
         privacyScreen_        = settings.value("user/privacy_screen", false).toBool();
         privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt();
-        shareKeysWithTrustedUsers_ =
-          settings.value("user/automatically_share_keys_with_trusted_users", false).toBool();
-        mobileMode_        = settings.value("user/mobile_mode", false).toBool();
-        emojiFont_         = settings.value("user/emoji_font_family", "default").toString();
-        baseFontSize_      = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
-        auto tempPresence  = settings.value("user/presence", "").toString().toStdString();
-        auto presenceValue = QMetaEnum::fromType<Presence>().keyToValue(tempPresence.c_str());
+        mobileMode_           = settings.value("user/mobile_mode", false).toBool();
+        emojiFont_            = settings.value("user/emoji_font_family", "default").toString();
+        baseFontSize_         = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
+        auto tempPresence     = settings.value("user/presence", "").toString().toStdString();
+        auto presenceValue    = QMetaEnum::fromType<Presence>().keyToValue(tempPresence.c_str());
         if (presenceValue < 0)
                 presenceValue = 0;
         presence_               = static_cast<Presence>(presenceValue);
@@ -123,6 +121,12 @@ UserSettings::load(std::optional<QString> profile)
         userId_      = settings.value(prefix + "auth/user_id", "").toString();
         deviceId_    = settings.value(prefix + "auth/device_id", "").toString();
 
+        shareKeysWithTrustedUsers_ =
+          settings.value(prefix + "user/automatically_share_keys_with_trusted_users", false)
+            .toBool();
+        onlyShareKeysWithVerifiedUsers_ =
+          settings.value(prefix + "user/only_share_keys_with_verified_users", false).toBool();
+
         disableCertificateValidation_ =
           settings.value("disable_certificate_validation", false).toBool();
 
@@ -402,6 +406,17 @@ UserSettings::setUseStunServer(bool useStunServer)
 }
 
 void
+UserSettings::setOnlyShareKeysWithVerifiedUsers(bool shareKeys)
+{
+        if (shareKeys == onlyShareKeysWithVerifiedUsers_)
+                return;
+
+        onlyShareKeysWithVerifiedUsers_ = shareKeys;
+        emit onlyShareKeysWithVerifiedUsersChanged(shareKeys);
+        save();
+}
+
+void
 UserSettings::setShareKeysWithTrustedUsers(bool shareKeys)
 {
         if (shareKeys == shareKeysWithTrustedUsers_)
@@ -610,8 +625,6 @@ UserSettings::save()
         settings.setValue("decrypt_sidebar", decryptSidebar_);
         settings.setValue("privacy_screen", privacyScreen_);
         settings.setValue("privacy_screen_timeout", privacyScreenTimeout_);
-        settings.setValue("automatically_share_keys_with_trusted_users",
-                          shareKeysWithTrustedUsers_);
         settings.setValue("mobile_mode", mobileMode_);
         settings.setValue("font_size", baseFontSize_);
         settings.setValue("typing_notifications", typingNotifications_);
@@ -650,6 +663,11 @@ UserSettings::save()
         settings.setValue(prefix + "auth/user_id", userId_);
         settings.setValue(prefix + "auth/device_id", deviceId_);
 
+        settings.setValue(prefix + "user/automatically_share_keys_with_trusted_users",
+                          shareKeysWithTrustedUsers_);
+        settings.setValue(prefix + "user/only_share_keys_with_verified_users",
+                          onlyShareKeysWithVerifiedUsers_);
+
         settings.setValue("disable_certificate_validation", disableCertificateValidation_);
 
         settings.sync();
@@ -703,41 +721,43 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
         general_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
         general_->setFont(font);
 
-        trayToggle_                = new Toggle{this};
-        startInTrayToggle_         = new Toggle{this};
-        avatarCircles_             = new Toggle{this};
-        decryptSidebar_            = new Toggle(this);
-        privacyScreen_             = new Toggle{this};
-        shareKeysWithTrustedUsers_ = new Toggle(this);
-        groupViewToggle_           = new Toggle{this};
-        timelineButtonsToggle_     = new Toggle{this};
-        typingNotifications_       = new Toggle{this};
-        messageHoverHighlight_     = new Toggle{this};
-        enlargeEmojiOnlyMessages_  = new Toggle{this};
-        sortByImportance_          = new Toggle{this};
-        readReceipts_              = new Toggle{this};
-        markdown_                  = new Toggle{this};
-        desktopNotifications_      = new Toggle{this};
-        alertOnNotification_       = new Toggle{this};
-        useStunServer_             = new Toggle{this};
-        mobileMode_                = new Toggle{this};
-        scaleFactorCombo_          = new QComboBox{this};
-        fontSizeCombo_             = new QComboBox{this};
-        fontSelectionCombo_        = new QFontComboBox{this};
-        emojiFontSelectionCombo_   = new QComboBox{this};
-        ringtoneCombo_             = new QComboBox{this};
-        microphoneCombo_           = new QComboBox{this};
-        cameraCombo_               = new QComboBox{this};
-        cameraResolutionCombo_     = new QComboBox{this};
-        cameraFrameRateCombo_      = new QComboBox{this};
-        timelineMaxWidthSpin_      = new QSpinBox{this};
-        privacyScreenTimeout_      = new QSpinBox{this};
+        trayToggle_                     = new Toggle{this};
+        startInTrayToggle_              = new Toggle{this};
+        avatarCircles_                  = new Toggle{this};
+        decryptSidebar_                 = new Toggle(this);
+        privacyScreen_                  = new Toggle{this};
+        onlyShareKeysWithVerifiedUsers_ = new Toggle(this);
+        shareKeysWithTrustedUsers_      = new Toggle(this);
+        groupViewToggle_                = new Toggle{this};
+        timelineButtonsToggle_          = new Toggle{this};
+        typingNotifications_            = new Toggle{this};
+        messageHoverHighlight_          = new Toggle{this};
+        enlargeEmojiOnlyMessages_       = new Toggle{this};
+        sortByImportance_               = new Toggle{this};
+        readReceipts_                   = new Toggle{this};
+        markdown_                       = new Toggle{this};
+        desktopNotifications_           = new Toggle{this};
+        alertOnNotification_            = new Toggle{this};
+        useStunServer_                  = new Toggle{this};
+        mobileMode_                     = new Toggle{this};
+        scaleFactorCombo_               = new QComboBox{this};
+        fontSizeCombo_                  = new QComboBox{this};
+        fontSelectionCombo_             = new QFontComboBox{this};
+        emojiFontSelectionCombo_        = new QComboBox{this};
+        ringtoneCombo_                  = new QComboBox{this};
+        microphoneCombo_                = new QComboBox{this};
+        cameraCombo_                    = new QComboBox{this};
+        cameraResolutionCombo_          = new QComboBox{this};
+        cameraFrameRateCombo_           = new QComboBox{this};
+        timelineMaxWidthSpin_           = new QSpinBox{this};
+        privacyScreenTimeout_           = new QSpinBox{this};
 
         trayToggle_->setChecked(settings_->tray());
         startInTrayToggle_->setChecked(settings_->startInTray());
         avatarCircles_->setChecked(settings_->avatarCircles());
         decryptSidebar_->setChecked(settings_->decryptSidebar());
         privacyScreen_->setChecked(settings_->privacyScreen());
+        onlyShareKeysWithVerifiedUsers_->setChecked(settings_->onlyShareKeysWithVerifiedUsers());
         shareKeysWithTrustedUsers_->setChecked(settings_->shareKeysWithTrustedUsers());
         groupViewToggle_->setChecked(settings_->groupView());
         timelineButtonsToggle_->setChecked(settings_->buttonsInTimeline());
@@ -1008,10 +1028,14 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
         formLayout_->addRow(new HorizontalLine{this});
         boxWrap(tr("Device ID"), deviceIdValue_);
         boxWrap(tr("Device Fingerprint"), deviceFingerprintValue_);
-        boxWrap(
-          tr("Share keys with verified users and devices"),
-          shareKeysWithTrustedUsers_,
-          tr("Automatically replies to key requests from other users, if they are verified."));
+        boxWrap(tr("Send encrypted messages to verified users only"),
+                onlyShareKeysWithVerifiedUsers_,
+                tr("Requires a user to be verified to send encrypted messages to them. This "
+                   "improves safety but makes E2EE more tedious."));
+        boxWrap(tr("Share keys with verified users and devices"),
+                shareKeysWithTrustedUsers_,
+                tr("Automatically replies to key requests from other users, if they are verified, "
+                   "even if that device shouldn't have access to those keys otherwise."));
         formLayout_->addRow(new HorizontalLine{this});
         formLayout_->addRow(sessionKeysLabel, sessionKeysLayout);
         formLayout_->addRow(crossSigningKeysLabel, crossSigningKeysLayout);
@@ -1179,6 +1203,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
                 }
         });
 
+        connect(onlyShareKeysWithVerifiedUsers_, &Toggle::toggled, this, [this](bool enabled) {
+                settings_->setOnlyShareKeysWithVerifiedUsers(enabled);
+        });
+
         connect(shareKeysWithTrustedUsers_, &Toggle::toggled, this, [this](bool enabled) {
                 settings_->setShareKeysWithTrustedUsers(enabled);
         });
@@ -1271,6 +1299,7 @@ UserSettingsPage::showEvent(QShowEvent *)
         groupViewToggle_->setState(settings_->groupView());
         decryptSidebar_->setState(settings_->decryptSidebar());
         privacyScreen_->setState(settings_->privacyScreen());
+        onlyShareKeysWithVerifiedUsers_->setState(settings_->onlyShareKeysWithVerifiedUsers());
         shareKeysWithTrustedUsers_->setState(settings_->shareKeysWithTrustedUsers());
         avatarCircles_->setState(settings_->avatarCircles());
         typingNotifications_->setState(settings_->typingNotifications());
@@ -1399,7 +1428,7 @@ UserSettingsPage::exportSessionKeys()
                 QString suffix("-----END MEGOLM SESSION DATA-----");
                 QString newline("\n");
                 QTextStream out(&file);
-                out << prefix << newline << b64 << newline << suffix;
+                out << prefix << newline << b64 << newline << suffix << newline;
                 file.close();
         } catch (const std::exception &e) {
                 QMessageBox::warning(this, tr("Error"), e.what());
diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index acb08569..096aab81 100644
--- a/src/UserSettingsPage.h
+++ b/src/UserSettingsPage.h
@@ -88,6 +88,8 @@ class UserSettings : public QObject
                      setScreenShareHideCursor NOTIFY screenShareHideCursorChanged)
         Q_PROPERTY(
           bool useStunServer READ useStunServer WRITE setUseStunServer NOTIFY useStunServerChanged)
+        Q_PROPERTY(bool onlyShareKeysWithVerifiedUsers READ onlyShareKeysWithVerifiedUsers WRITE
+                     setOnlyShareKeysWithVerifiedUsers NOTIFY onlyShareKeysWithVerifiedUsersChanged)
         Q_PROPERTY(bool shareKeysWithTrustedUsers READ shareKeysWithTrustedUsers WRITE
                      setShareKeysWithTrustedUsers NOTIFY shareKeysWithTrustedUsersChanged)
         Q_PROPERTY(QString profile READ profile WRITE setProfile NOTIFY profileChanged)
@@ -152,6 +154,7 @@ public:
         void setScreenShareRemoteVideo(bool state);
         void setScreenShareHideCursor(bool state);
         void setUseStunServer(bool state);
+        void setOnlyShareKeysWithVerifiedUsers(bool state);
         void setShareKeysWithTrustedUsers(bool state);
         void setProfile(QString profile);
         void setUserId(QString userId);
@@ -208,6 +211,7 @@ public:
         bool screenShareHideCursor() const { return screenShareHideCursor_; }
         bool useStunServer() const { return useStunServer_; }
         bool shareKeysWithTrustedUsers() const { return shareKeysWithTrustedUsers_; }
+        bool onlyShareKeysWithVerifiedUsers() const { return onlyShareKeysWithVerifiedUsers_; }
         QString profile() const { return profile_; }
         QString userId() const { return userId_; }
         QString accessToken() const { return accessToken_; }
@@ -252,6 +256,7 @@ signals:
         void screenShareRemoteVideoChanged(bool state);
         void screenShareHideCursorChanged(bool state);
         void useStunServerChanged(bool state);
+        void onlyShareKeysWithVerifiedUsersChanged(bool state);
         void shareKeysWithTrustedUsersChanged(bool state);
         void profileChanged(QString profile);
         void userIdChanged(QString userId);
@@ -284,6 +289,7 @@ private:
         bool privacyScreen_;
         int privacyScreenTimeout_;
         bool shareKeysWithTrustedUsers_;
+        bool onlyShareKeysWithVerifiedUsers_;
         bool mobileMode_;
         int timelineMaxWidth_;
         int roomListWidth_;
@@ -372,6 +378,7 @@ private:
         Toggle *privacyScreen_;
         QSpinBox *privacyScreenTimeout_;
         Toggle *shareKeysWithTrustedUsers_;
+        Toggle *onlyShareKeysWithVerifiedUsers_;
         Toggle *mobileMode_;
         QLabel *deviceFingerprintValue_;
         QLabel *deviceIdValue_;
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 8d5ae4a9..41013e39 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -172,32 +172,6 @@ utils::scaleFactor()
         return settings.value("settings/scale_factor", -1).toFloat();
 }
 
-bool
-utils::respondsToKeyRequests(const std::string &roomId)
-{
-        return respondsToKeyRequests(QString::fromStdString(roomId));
-}
-
-bool
-utils::respondsToKeyRequests(const QString &roomId)
-{
-        if (roomId.isEmpty())
-                return false;
-
-        QSettings settings;
-        return settings.value("rooms/respond_to_key_requests/" + roomId, false).toBool();
-}
-
-void
-utils::setKeyRequestsPreference(QString roomId, bool value)
-{
-        if (roomId.isEmpty())
-                return;
-
-        QSettings settings;
-        settings.setValue("rooms/respond_to_key_requests/" + roomId, value);
-}
-
 QString
 utils::descriptiveTime(const QDateTime &then)
 {
@@ -556,7 +530,7 @@ utils::markdownToHtml(const QString &text, bool rainbowify)
                                 // Use colors as described here:
                                 // https://shark.comfsm.fm/~dleeling/cis/hsl_rainbow.html
                                 auto color =
-                                  QColor::fromHslF((charIdx - 1.0) / textLen * (5. / 6.), 1.0, 0.5);
+                                  QColor::fromHslF((charIdx - 1.0) / textLen * (5. / 6.), 0.9, 0.5);
                                 // format color for HTML
                                 auto colorString = color.name(QColor::NameFormat::HexRgb);
                                 // create HTML element for current char
diff --git a/src/Utils.h b/src/Utils.h
index 1d48e2c7..8f37a574 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -67,15 +67,6 @@ scaleFactor();
 void
 setScaleFactor(float factor);
 
-//! Whether or not we should respond to key requests for the given room.
-bool
-respondsToKeyRequests(const QString &roomId);
-bool
-respondsToKeyRequests(const std::string &roomId);
-
-void
-setKeyRequestsPreference(QString roomId, bool value);
-
 //! Human friendly timestamp representation.
 QString
 descriptiveTime(const QDateTime &then);
diff --git a/src/dialogs/InviteUsers.cpp b/src/dialogs/InviteUsers.cpp
deleted file mode 100644
index 9dd6085f..00000000
--- a/src/dialogs/InviteUsers.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QDebug>
-#include <QIcon>
-#include <QLabel>
-#include <QListWidget>
-#include <QListWidgetItem>
-#include <QPushButton>
-#include <QStyleOption>
-#include <QTimer>
-#include <QVBoxLayout>
-
-#include "dialogs/InviteUsers.h"
-
-#include "Config.h"
-#include "InviteeItem.h"
-#include "ui/TextField.h"
-
-#include <mtx/identifiers.hpp>
-
-using namespace dialogs;
-
-InviteUsers::InviteUsers(QWidget *parent)
-  : QFrame(parent)
-{
-        setAutoFillBackground(true);
-        setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
-        setWindowModality(Qt::WindowModal);
-        setAttribute(Qt::WA_DeleteOnClose, true);
-
-        setMinimumWidth(conf::window::minModalWidth);
-        setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
-
-        auto layout = new QVBoxLayout(this);
-        layout->setSpacing(conf::modals::WIDGET_SPACING);
-        layout->setMargin(conf::modals::WIDGET_MARGIN);
-
-        auto buttonLayout = new QHBoxLayout();
-        buttonLayout->setSpacing(0);
-        buttonLayout->setMargin(0);
-
-        confirmBtn_ = new QPushButton("Invite", this);
-        confirmBtn_->setDefault(true);
-        cancelBtn_ = new QPushButton(tr("Cancel"), this);
-
-        buttonLayout->addStretch(1);
-        buttonLayout->setSpacing(15);
-        buttonLayout->addWidget(cancelBtn_);
-        buttonLayout->addWidget(confirmBtn_);
-
-        inviteeInput_ = new TextField(this);
-        inviteeInput_->setLabel(tr("User ID to invite"));
-
-        inviteeList_ = new QListWidget;
-        inviteeList_->setFrameStyle(QFrame::NoFrame);
-        inviteeList_->setSelectionMode(QAbstractItemView::NoSelection);
-        inviteeList_->setAttribute(Qt::WA_MacShowFocusRect, 0);
-        inviteeList_->setSpacing(5);
-
-        errorLabel_ = new QLabel(this);
-        errorLabel_->setAlignment(Qt::AlignCenter);
-
-        layout->addWidget(inviteeInput_);
-        layout->addWidget(errorLabel_);
-        layout->addWidget(inviteeList_);
-        layout->addLayout(buttonLayout);
-
-        connect(inviteeInput_, &TextField::returnPressed, this, &InviteUsers::addUser);
-        connect(confirmBtn_, &QPushButton::clicked, [this]() {
-                if (!inviteeInput_->text().trimmed().isEmpty()) {
-                        addUser();
-                }
-
-                emit sendInvites(invitedUsers());
-
-                inviteeInput_->clear();
-                inviteeList_->clear();
-                errorLabel_->hide();
-
-                emit close();
-        });
-
-        connect(cancelBtn_, &QPushButton::clicked, [this]() {
-                inviteeInput_->clear();
-                inviteeList_->clear();
-                errorLabel_->hide();
-
-                emit close();
-        });
-}
-
-void
-InviteUsers::addUser()
-{
-        auto user_id = inviteeInput_->text();
-
-        try {
-                namespace ids = mtx::identifiers;
-                auto user     = ids::parse<ids::User>(user_id.toStdString());
-
-                auto item    = new QListWidgetItem(inviteeList_);
-                auto invitee = new InviteeItem(user, this);
-
-                item->setSizeHint(invitee->minimumSizeHint());
-                item->setFlags(Qt::NoItemFlags);
-                item->setTextAlignment(Qt::AlignCenter);
-
-                inviteeList_->setItemWidget(item, invitee);
-
-                connect(invitee, &InviteeItem::removeItem, this, [this, item]() {
-                        emit removeInvitee(item);
-                });
-
-                errorLabel_->hide();
-                inviteeInput_->clear();
-        } catch (std::exception &e) {
-                errorLabel_->setText(e.what());
-                errorLabel_->show();
-        }
-}
-
-void
-InviteUsers::removeInvitee(QListWidgetItem *item)
-{
-        int row     = inviteeList_->row(item);
-        auto widget = inviteeList_->takeItem(row);
-
-        inviteeList_->removeItemWidget(widget);
-}
-
-QStringList
-InviteUsers::invitedUsers() const
-{
-        QStringList users;
-
-        for (int ii = 0; ii < inviteeList_->count(); ++ii) {
-                auto item    = inviteeList_->item(ii);
-                auto widget  = inviteeList_->itemWidget(item);
-                auto invitee = qobject_cast<InviteeItem *>(widget);
-
-                if (invitee)
-                        users << invitee->userID();
-                else
-                        qDebug() << "Cast InviteeItem failed";
-        }
-
-        return users;
-}
-
-void
-InviteUsers::showEvent(QShowEvent *event)
-{
-        inviteeInput_->setFocus();
-
-        QFrame::showEvent(event);
-}
diff --git a/src/dialogs/InviteUsers.h b/src/dialogs/InviteUsers.h
deleted file mode 100644
index e40183c1..00000000
--- a/src/dialogs/InviteUsers.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QFrame>
-#include <QStringList>
-
-class QPushButton;
-class QLabel;
-class TextField;
-class QListWidget;
-class QListWidgetItem;
-
-namespace dialogs {
-
-class InviteUsers : public QFrame
-{
-        Q_OBJECT
-public:
-        explicit InviteUsers(QWidget *parent = nullptr);
-
-protected:
-        void showEvent(QShowEvent *event) override;
-
-signals:
-        void sendInvites(QStringList invitees);
-
-private slots:
-        void removeInvitee(QListWidgetItem *item);
-
-private:
-        void addUser();
-        QStringList invitedUsers() const;
-
-        QPushButton *confirmBtn_;
-        QPushButton *cancelBtn_;
-
-        TextField *inviteeInput_;
-        QLabel *errorLabel_;
-
-        QListWidget *inviteeList_;
-};
-} // dialogs
diff --git a/src/dialogs/MemberList.cpp b/src/dialogs/MemberList.cpp
deleted file mode 100644
index 21eb72b0..00000000
--- a/src/dialogs/MemberList.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QAbstractSlider>
-#include <QLabel>
-#include <QListWidgetItem>
-#include <QPainter>
-#include <QPushButton>
-#include <QScrollBar>
-#include <QShortcut>
-#include <QStyleOption>
-#include <QVBoxLayout>
-
-#include "dialogs/MemberList.h"
-
-#include "Cache.h"
-#include "ChatPage.h"
-#include "Config.h"
-#include "Logging.h"
-#include "Utils.h"
-#include "ui/Avatar.h"
-
-using namespace dialogs;
-
-MemberItem::MemberItem(const RoomMember &member, QWidget *parent)
-  : QWidget(parent)
-{
-        topLayout_ = new QHBoxLayout(this);
-        topLayout_->setMargin(0);
-
-        textLayout_ = new QVBoxLayout;
-        textLayout_->setMargin(0);
-        textLayout_->setSpacing(0);
-
-        avatar_ = new Avatar(this, 44);
-        avatar_->setLetter(utils::firstChar(member.display_name));
-
-        avatar_->setImage(ChatPage::instance()->currentRoom(), member.user_id);
-
-        QFont nameFont;
-        nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1);
-
-        userId_   = new QLabel(member.user_id, this);
-        userName_ = new QLabel(member.display_name, this);
-        userName_->setFont(nameFont);
-
-        textLayout_->addWidget(userName_);
-        textLayout_->addWidget(userId_);
-
-        topLayout_->addWidget(avatar_);
-        topLayout_->addLayout(textLayout_, 1);
-}
-
-void
-MemberItem::paintEvent(QPaintEvent *)
-{
-        QStyleOption opt;
-        opt.init(this);
-        QPainter p(this);
-        style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-}
-
-MemberList::MemberList(const QString &room_id, QWidget *parent)
-  : QFrame(parent)
-  , room_id_{room_id}
-{
-        setAutoFillBackground(true);
-        setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
-        setWindowModality(Qt::WindowModal);
-        setAttribute(Qt::WA_DeleteOnClose, true);
-
-        auto layout = new QVBoxLayout(this);
-        layout->setSpacing(conf::modals::WIDGET_SPACING);
-        layout->setMargin(conf::modals::WIDGET_MARGIN);
-
-        list_ = new QListWidget;
-        list_->setFrameStyle(QFrame::NoFrame);
-        list_->setSelectionMode(QAbstractItemView::NoSelection);
-        list_->setSpacing(5);
-
-        QFont largeFont;
-        largeFont.setPointSizeF(largeFont.pointSizeF() * 1.5);
-
-        setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
-        setMinimumHeight(list_->sizeHint().height() * 2);
-        setMinimumWidth(std::max(list_->sizeHint().width() + 4 * conf::modals::WIDGET_MARGIN,
-                                 QFontMetrics(largeFont).averageCharWidth() * 30 -
-                                   2 * conf::modals::WIDGET_MARGIN));
-
-        QFont font;
-        font.setPointSizeF(font.pointSizeF() * conf::modals::LABEL_MEDIUM_SIZE_RATIO);
-
-        topLabel_ = new QLabel(tr("Room members"), this);
-        topLabel_->setAlignment(Qt::AlignCenter);
-        topLabel_->setFont(font);
-
-        auto okBtn = new QPushButton(tr("OK"), this);
-
-        auto buttonLayout = new QHBoxLayout();
-        buttonLayout->setSpacing(15);
-        buttonLayout->addStretch(1);
-        buttonLayout->addWidget(okBtn);
-
-        layout->addWidget(topLabel_);
-        layout->addWidget(list_);
-        layout->addLayout(buttonLayout);
-
-        list_->clear();
-
-        connect(list_->verticalScrollBar(), &QAbstractSlider::valueChanged, this, [this](int pos) {
-                if (pos != list_->verticalScrollBar()->maximum())
-                        return;
-
-                const size_t numMembers = list_->count() - 1;
-
-                if (numMembers > 0)
-                        addUsers(cache::getMembers(room_id_.toStdString(), numMembers));
-        });
-
-        try {
-                addUsers(cache::getMembers(room_id_.toStdString()));
-        } catch (const lmdb::error &e) {
-                nhlog::db()->critical("Failed to retrieve members from cache: {}", e.what());
-        }
-
-        auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
-        connect(closeShortcut, &QShortcut::activated, this, &MemberList::close);
-        connect(okBtn, &QPushButton::clicked, this, &MemberList::close);
-}
-
-void
-MemberList::addUsers(const std::vector<RoomMember> &members)
-{
-        for (const auto &member : members) {
-                auto user = new MemberItem(member, this);
-                auto item = new QListWidgetItem;
-
-                item->setSizeHint(user->minimumSizeHint());
-                item->setFlags(Qt::NoItemFlags);
-                item->setTextAlignment(Qt::AlignCenter);
-
-                list_->insertItem(list_->count() - 1, item);
-                list_->setItemWidget(item, user);
-        }
-}
diff --git a/src/dialogs/MemberList.h b/src/dialogs/MemberList.h
deleted file mode 100644
index b822eec8..00000000
--- a/src/dialogs/MemberList.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QFrame>
-#include <QListWidget>
-
-class Avatar;
-class QPushButton;
-class QHBoxLayout;
-class QLabel;
-class QVBoxLayout;
-
-struct RoomMember;
-
-template<class T>
-class QSharedPointer;
-
-namespace dialogs {
-
-class MemberItem : public QWidget
-{
-        Q_OBJECT
-
-public:
-        MemberItem(const RoomMember &member, QWidget *parent);
-
-protected:
-        void paintEvent(QPaintEvent *) override;
-
-private:
-        QHBoxLayout *topLayout_;
-        QVBoxLayout *textLayout_;
-
-        Avatar *avatar_;
-
-        QLabel *userName_;
-        QLabel *userId_;
-};
-
-class MemberList : public QFrame
-{
-        Q_OBJECT
-public:
-        MemberList(const QString &room_id, QWidget *parent = nullptr);
-
-public slots:
-        void addUsers(const std::vector<RoomMember> &users);
-
-private:
-        QString room_id_;
-        QLabel *topLabel_;
-        QListWidget *list_;
-};
-} // dialogs
diff --git a/src/dialogs/RawMessage.h b/src/dialogs/RawMessage.h
deleted file mode 100644
index e95f675c..00000000
--- a/src/dialogs/RawMessage.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QFont>
-#include <QFontDatabase>
-#include <QTextBrowser>
-#include <QVBoxLayout>
-#include <QWidget>
-
-#include "nlohmann/json.hpp"
-
-#include "Logging.h"
-#include "MainWindow.h"
-#include "ui/FlatButton.h"
-
-namespace dialogs {
-
-class RawMessage : public QWidget
-{
-        Q_OBJECT
-public:
-        RawMessage(QString msg, QWidget *parent = nullptr)
-          : QWidget{parent}
-        {
-                QFont monospaceFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
-
-                auto layout = new QVBoxLayout{this};
-                auto viewer = new QTextBrowser{this};
-                viewer->setFont(monospaceFont);
-                viewer->setText(msg);
-
-                layout->setSpacing(0);
-                layout->setMargin(0);
-                layout->addWidget(viewer);
-
-                setAutoFillBackground(true);
-                setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
-                setAttribute(Qt::WA_DeleteOnClose, true);
-
-                QSize winsize;
-                QPoint center;
-
-                auto window = MainWindow::instance();
-                if (window) {
-                        winsize = window->frameGeometry().size();
-                        center  = window->frameGeometry().center();
-
-                        move(center.x() - (width() * 0.5), center.y() - (height() * 0.5));
-                } else {
-                        nhlog::ui()->warn("unable to retrieve MainWindow's size");
-                }
-
-                raise();
-                show();
-        }
-};
-} // namespace dialogs
diff --git a/src/dialogs/ReadReceipts.cpp b/src/dialogs/ReadReceipts.cpp
deleted file mode 100644
index fa7132fd..00000000
--- a/src/dialogs/ReadReceipts.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QDebug>
-#include <QIcon>
-#include <QLabel>
-#include <QListWidgetItem>
-#include <QPainter>
-#include <QPushButton>
-#include <QShortcut>
-#include <QStyleOption>
-#include <QTimer>
-#include <QVBoxLayout>
-
-#include "dialogs/ReadReceipts.h"
-
-#include "AvatarProvider.h"
-#include "Cache.h"
-#include "ChatPage.h"
-#include "Config.h"
-#include "Utils.h"
-#include "ui/Avatar.h"
-
-using namespace dialogs;
-
-ReceiptItem::ReceiptItem(QWidget *parent,
-                         const QString &user_id,
-                         uint64_t timestamp,
-                         const QString &room_id)
-  : QWidget(parent)
-{
-        topLayout_ = new QHBoxLayout(this);
-        topLayout_->setMargin(0);
-
-        textLayout_ = new QVBoxLayout;
-        textLayout_->setMargin(0);
-        textLayout_->setSpacing(conf::modals::TEXT_SPACING);
-
-        QFont nameFont;
-        nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1);
-
-        auto displayName = cache::displayName(room_id, user_id);
-
-        avatar_ = new Avatar(this, 44);
-        avatar_->setLetter(utils::firstChar(displayName));
-
-        // If it's a matrix id we use the second letter.
-        if (displayName.size() > 1 && displayName.at(0) == '@')
-                avatar_->setLetter(QChar(displayName.at(1)));
-
-        userName_ = new QLabel(displayName, this);
-        userName_->setFont(nameFont);
-
-        timestamp_ = new QLabel(dateFormat(QDateTime::fromMSecsSinceEpoch(timestamp)), this);
-
-        textLayout_->addWidget(userName_);
-        textLayout_->addWidget(timestamp_);
-
-        topLayout_->addWidget(avatar_);
-        topLayout_->addLayout(textLayout_, 1);
-
-        avatar_->setImage(ChatPage::instance()->currentRoom(), user_id);
-}
-
-void
-ReceiptItem::paintEvent(QPaintEvent *)
-{
-        QStyleOption opt;
-        opt.init(this);
-        QPainter p(this);
-        style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-}
-
-QString
-ReceiptItem::dateFormat(const QDateTime &then) const
-{
-        auto now  = QDateTime::currentDateTime();
-        auto days = then.daysTo(now);
-
-        if (days == 0)
-                return tr("Today %1")
-                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
-        else if (days < 2)
-                return tr("Yesterday %1")
-                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
-        else if (days < 7)
-                return QString("%1 %2")
-                  .arg(then.toString("dddd"))
-                  .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
-
-        return QLocale::system().toString(then.time(), QLocale::ShortFormat);
-}
-
-ReadReceipts::ReadReceipts(QWidget *parent)
-  : QFrame(parent)
-{
-        setAutoFillBackground(true);
-        setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
-        setWindowModality(Qt::WindowModal);
-        setAttribute(Qt::WA_DeleteOnClose, true);
-
-        auto layout = new QVBoxLayout(this);
-        layout->setSpacing(conf::modals::WIDGET_SPACING);
-        layout->setMargin(conf::modals::WIDGET_MARGIN);
-
-        userList_ = new QListWidget;
-        userList_->setFrameStyle(QFrame::NoFrame);
-        userList_->setSelectionMode(QAbstractItemView::NoSelection);
-        userList_->setSpacing(conf::modals::TEXT_SPACING);
-
-        QFont largeFont;
-        largeFont.setPointSizeF(largeFont.pointSizeF() * 1.5);
-
-        setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
-        setMinimumHeight(userList_->sizeHint().height() * 2);
-        setMinimumWidth(std::max(userList_->sizeHint().width() + 4 * conf::modals::WIDGET_MARGIN,
-                                 QFontMetrics(largeFont).averageCharWidth() * 30 -
-                                   2 * conf::modals::WIDGET_MARGIN));
-
-        QFont font;
-        font.setPointSizeF(font.pointSizeF() * conf::modals::LABEL_MEDIUM_SIZE_RATIO);
-
-        topLabel_ = new QLabel(tr("Read receipts"), this);
-        topLabel_->setAlignment(Qt::AlignCenter);
-        topLabel_->setFont(font);
-
-        auto okBtn = new QPushButton(tr("Close"), this);
-
-        auto buttonLayout = new QHBoxLayout();
-        buttonLayout->setSpacing(15);
-        buttonLayout->addStretch(1);
-        buttonLayout->addWidget(okBtn);
-
-        layout->addWidget(topLabel_);
-        layout->addWidget(userList_);
-        layout->addLayout(buttonLayout);
-
-        auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
-        connect(closeShortcut, &QShortcut::activated, this, &ReadReceipts::close);
-        connect(okBtn, &QPushButton::clicked, this, &ReadReceipts::close);
-}
-
-void
-ReadReceipts::addUsers(const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &receipts)
-{
-        // We want to remove any previous items that have been set.
-        userList_->clear();
-
-        for (const auto &receipt : receipts) {
-                auto user = new ReceiptItem(this,
-                                            QString::fromStdString(receipt.second),
-                                            receipt.first,
-                                            ChatPage::instance()->currentRoom());
-                auto item = new QListWidgetItem(userList_);
-
-                item->setSizeHint(user->minimumSizeHint());
-                item->setFlags(Qt::NoItemFlags);
-                item->setTextAlignment(Qt::AlignCenter);
-
-                userList_->setItemWidget(item, user);
-        }
-}
-
-void
-ReadReceipts::paintEvent(QPaintEvent *)
-{
-        QStyleOption opt;
-        opt.init(this);
-        QPainter p(this);
-        style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-}
-
-void
-ReadReceipts::hideEvent(QHideEvent *event)
-{
-        userList_->clear();
-        QFrame::hideEvent(event);
-}
diff --git a/src/dialogs/ReadReceipts.h b/src/dialogs/ReadReceipts.h
deleted file mode 100644
index 5c6c5d2b..00000000
--- a/src/dialogs/ReadReceipts.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QDateTime>
-#include <QFrame>
-
-class Avatar;
-class QLabel;
-class QListWidget;
-class QHBoxLayout;
-class QVBoxLayout;
-
-namespace dialogs {
-
-class ReceiptItem : public QWidget
-{
-        Q_OBJECT
-
-public:
-        ReceiptItem(QWidget *parent,
-                    const QString &user_id,
-                    uint64_t timestamp,
-                    const QString &room_id);
-
-protected:
-        void paintEvent(QPaintEvent *) override;
-
-private:
-        QString dateFormat(const QDateTime &then) const;
-
-        QHBoxLayout *topLayout_;
-        QVBoxLayout *textLayout_;
-
-        Avatar *avatar_;
-
-        QLabel *userName_;
-        QLabel *timestamp_;
-};
-
-class ReadReceipts : public QFrame
-{
-        Q_OBJECT
-public:
-        explicit ReadReceipts(QWidget *parent = nullptr);
-
-public slots:
-        void addUsers(const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users);
-
-protected:
-        void paintEvent(QPaintEvent *event) override;
-        void hideEvent(QHideEvent *event) override;
-
-private:
-        QLabel *topLabel_;
-
-        QListWidget *userList_;
-};
-} // dialogs
diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp
index 598b2bd0..2809de87 100644
--- a/src/notifications/ManagerLinux.cpp
+++ b/src/notifications/ManagerLinux.cpp
@@ -295,12 +295,9 @@ operator<<(QDBusArgument &arg, const QImage &image)
         int channels = i.isGrayscale() ? 1 : (i.hasAlphaChannel() ? 4 : 3);
         arg << i.depth() / channels;
         arg << channels;
-#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
-        arg << QByteArray(reinterpret_cast<const char *>(i.bits()), i.byteCount());
-#else
         arg << QByteArray(reinterpret_cast<const char *>(i.bits()), i.sizeInBytes());
-#endif
         arg.endStructure();
+
         return arg;
 }
 
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index 9a91ff79..742f8dbb 100644
--- a/src/timeline/EventStore.cpp
+++ b/src/timeline/EventStore.cpp
@@ -20,8 +20,7 @@
 
 Q_DECLARE_METATYPE(Reaction)
 
-QCache<EventStore::IdIndex, mtx::events::collections::TimelineEvents> EventStore::decryptedEvents_{
-  1000};
+QCache<EventStore::IdIndex, olm::DecryptionResult> EventStore::decryptedEvents_{1000};
 QCache<EventStore::IdIndex, mtx::events::collections::TimelineEvents> EventStore::events_by_id_{
   1000};
 QCache<EventStore::Index, mtx::events::collections::TimelineEvents> EventStore::events_{1000};
@@ -144,12 +143,16 @@ EventStore::EventStore(std::string room_id, QObject *)
                                                             mtx::events::msg::Encrypted>) {
                                                     auto event =
                                                       decryptEvent({room_id_, e.event_id}, e);
-                                                    if (auto dec =
-                                                          std::get_if<mtx::events::RoomEvent<
-                                                            mtx::events::msg::
-                                                              KeyVerificationRequest>>(event)) {
-                                                            emit updateFlowEventId(
-                                                              event_id.event_id.to_string());
+                                                    if (event->event) {
+                                                            if (auto dec = std::get_if<
+                                                                  mtx::events::RoomEvent<
+                                                                    mtx::events::msg::
+                                                                      KeyVerificationRequest>>(
+                                                                  &event->event.value())) {
+                                                                    emit updateFlowEventId(
+                                                                      event_id.event_id
+                                                                        .to_string());
+                                                            }
                                                     }
                                             }
                                     });
@@ -393,12 +396,12 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
                 if (auto encrypted =
                       std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
                         &event)) {
-                        mtx::events::collections::TimelineEvents *d_event =
-                          decryptEvent({room_id_, encrypted->event_id}, *encrypted);
-                        if (std::visit(
+                        auto d_event = decryptEvent({room_id_, encrypted->event_id}, *encrypted);
+                        if (d_event->event &&
+                            std::visit(
                               [](auto e) { return (e.sender != utils::localUser().toStdString()); },
-                              *d_event)) {
-                                handle_room_verification(*d_event);
+                              *d_event->event)) {
+                                handle_room_verification(*d_event->event);
                         }
                 }
         }
@@ -599,11 +602,15 @@ EventStore::get(int idx, bool decrypt)
                 events_.insert(index, event_ptr);
         }
 
-        if (decrypt)
+        if (decrypt) {
                 if (auto encrypted =
                       std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
-                        event_ptr))
-                        return decryptEvent({room_id_, encrypted->event_id}, *encrypted);
+                        event_ptr)) {
+                        auto decrypted = decryptEvent({room_id_, encrypted->event_id}, *encrypted);
+                        if (decrypted->event)
+                                return &*decrypted->event;
+                }
+        }
 
         return event_ptr;
 }
@@ -629,7 +636,7 @@ EventStore::indexToId(int idx) const
         return cache::client()->getTimelineEventId(room_id_, toInternalIdx(idx));
 }
 
-mtx::events::collections::TimelineEvents *
+olm::DecryptionResult *
 EventStore::decryptEvent(const IdIndex &idx,
                          const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e)
 {
@@ -641,57 +648,24 @@ EventStore::decryptEvent(const IdIndex &idx,
         index.session_id = e.content.session_id;
         index.sender_key = e.content.sender_key;
 
-        auto asCacheEntry = [&idx](mtx::events::collections::TimelineEvents &&event) {
-                auto event_ptr = new mtx::events::collections::TimelineEvents(std::move(event));
+        auto asCacheEntry = [&idx](olm::DecryptionResult &&event) {
+                auto event_ptr = new olm::DecryptionResult(std::move(event));
                 decryptedEvents_.insert(idx, event_ptr);
                 return event_ptr;
         };
 
         auto decryptionResult = olm::decryptEvent(index, e);
 
-        mtx::events::RoomEvent<mtx::events::msg::Notice> dummy;
-        dummy.origin_server_ts = e.origin_server_ts;
-        dummy.event_id         = e.event_id;
-        dummy.sender           = e.sender;
-
         if (decryptionResult.error) {
-                switch (*decryptionResult.error) {
+                switch (decryptionResult.error) {
                 case olm::DecryptionErrorCode::MissingSession:
                 case olm::DecryptionErrorCode::MissingSessionIndex: {
-                        if (decryptionResult.error == olm::DecryptionErrorCode::MissingSession)
-                                dummy.content.body =
-                                  tr("-- Encrypted Event (No keys found for decryption) --",
-                                     "Placeholder, when the message was not decrypted yet or can't "
-                                     "be "
-                                     "decrypted.")
-                                    .toStdString();
-                        else
-                                dummy.content.body =
-                                  tr("-- Encrypted Event (Key not valid for this index) --",
-                                     "Placeholder, when the message can't be decrypted with this "
-                                     "key since it is not valid for this index ")
-                                    .toStdString();
                         nhlog::crypto()->info("Could not find inbound megolm session ({}, {}, {})",
                                               index.room_id,
                                               index.session_id,
                                               e.sender);
-                        // we may not want to request keys during initial sync and such
-                        if (suppressKeyRequests)
-                                break;
-                        // TODO: Check if this actually works and look in key backup
-                        auto copy    = e;
-                        copy.room_id = room_id_;
-                        if (pending_key_requests.count(e.content.session_id)) {
-                                pending_key_requests.at(e.content.session_id)
-                                  .events.push_back(copy);
-                        } else {
-                                PendingKeyRequests request;
-                                request.request_id =
-                                  "key_request." + http::client()->generate_txn_id();
-                                request.events.push_back(copy);
-                                olm::send_key_request_for(copy, request.request_id);
-                                pending_key_requests[e.content.session_id] = request;
-                        }
+
+                        requestSession(e, false);
                         break;
                 }
                 case olm::DecryptionErrorCode::DbError:
@@ -701,12 +675,6 @@ EventStore::decryptEvent(const IdIndex &idx,
                           index.session_id,
                           index.sender_key,
                           decryptionResult.error_message.value_or(""));
-                        dummy.content.body =
-                          tr("-- Decryption Error (failed to retrieve megolm keys from db) --",
-                             "Placeholder, when the message can't be decrypted, because the DB "
-                             "access "
-                             "failed.")
-                            .toStdString();
                         break;
                 case olm::DecryptionErrorCode::DecryptionFailed:
                         nhlog::crypto()->critical(
@@ -715,22 +683,8 @@ EventStore::decryptEvent(const IdIndex &idx,
                           index.session_id,
                           index.sender_key,
                           decryptionResult.error_message.value_or(""));
-                        dummy.content.body =
-                          tr("-- Decryption Error (%1) --",
-                             "Placeholder, when the message can't be decrypted. In this case, the "
-                             "Olm "
-                             "decrytion returned an error, which is passed as %1.")
-                            .arg(
-                              QString::fromStdString(decryptionResult.error_message.value_or("")))
-                            .toStdString();
                         break;
                 case olm::DecryptionErrorCode::ParsingFailed:
-                        dummy.content.body =
-                          tr("-- Encrypted Event (Unknown event type) --",
-                             "Placeholder, when the message was decrypted, but we couldn't parse "
-                             "it, because "
-                             "Nheko/mtxclient don't support that event type yet.")
-                            .toStdString();
                         break;
                 case olm::DecryptionErrorCode::ReplayAttack:
                         nhlog::crypto()->critical(
@@ -738,85 +692,50 @@ EventStore::decryptEvent(const IdIndex &idx,
                           e.event_id,
                           room_id_,
                           index.sender_key);
-                        dummy.content.body =
-                          tr("-- Replay attack! This message index was reused! --").toStdString();
                         break;
-                case olm::DecryptionErrorCode::UnknownFingerprint:
-                        // TODO: don't fail, just show in UI.
-                        nhlog::crypto()->critical("Message by unverified fingerprint {}",
-                                                  index.sender_key);
-                        dummy.content.body =
-                          tr("-- Message by unverified device! --").toStdString();
+                case olm::DecryptionErrorCode::NoError:
+                        // unreachable
                         break;
                 }
-                return asCacheEntry(std::move(dummy));
-        }
-
-        std::string msg_str;
-        try {
-                auto session = cache::client()->getInboundMegolmSession(index);
-                auto res =
-                  olm::client()->decrypt_group_message(session.get(), e.content.ciphertext);
-                msg_str = std::string((char *)res.data.data(), res.data.size());
-        } catch (const lmdb::error &e) {
-                nhlog::db()->critical("failed to retrieve megolm session with index ({}, {}, {})",
-                                      index.room_id,
-                                      index.session_id,
-                                      index.sender_key,
-                                      e.what());
-                dummy.content.body =
-                  tr("-- Decryption Error (failed to retrieve megolm keys from db) --",
-                     "Placeholder, when the message can't be decrypted, because the DB "
-                     "access "
-                     "failed.")
-                    .toStdString();
-                return asCacheEntry(std::move(dummy));
-        } catch (const mtx::crypto::olm_exception &e) {
-                nhlog::crypto()->critical("failed to decrypt message with index ({}, {}, {}): {}",
-                                          index.room_id,
-                                          index.session_id,
-                                          index.sender_key,
-                                          e.what());
-                dummy.content.body =
-                  tr("-- Decryption Error (%1) --",
-                     "Placeholder, when the message can't be decrypted. In this case, the "
-                     "Olm "
-                     "decrytion returned an error, which is passed as %1.")
-                    .arg(e.what())
-                    .toStdString();
-                return asCacheEntry(std::move(dummy));
-        }
-
-        // Add missing fields for the event.
-        json body                = json::parse(msg_str);
-        body["event_id"]         = e.event_id;
-        body["sender"]           = e.sender;
-        body["origin_server_ts"] = e.origin_server_ts;
-        body["unsigned"]         = e.unsigned_data;
-
-        // relations are unencrypted in content...
-        mtx::common::add_relations(body["content"], e.content.relations);
-
-        json event_array = json::array();
-        event_array.push_back(body);
-
-        std::vector<mtx::events::collections::TimelineEvents> temp_events;
-        mtx::responses::utils::parse_timeline_events(event_array, temp_events);
-
-        if (temp_events.size() == 1) {
-                auto encInfo = mtx::accessors::file(temp_events[0]);
-
-                if (encInfo)
-                        emit newEncryptedImage(encInfo.value());
-
-                return asCacheEntry(std::move(temp_events[0]));
+                return asCacheEntry(std::move(decryptionResult));
         }
 
         auto encInfo = mtx::accessors::file(decryptionResult.event.value());
         if (encInfo)
                 emit newEncryptedImage(encInfo.value());
 
-        return asCacheEntry(std::move(decryptionResult.event.value()));
+        return asCacheEntry(std::move(decryptionResult));
+}
+
+void
+EventStore::requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &ev,
+                           bool manual)
+{
+        // we may not want to request keys during initial sync and such
+        if (suppressKeyRequests)
+                return;
+
+        // TODO: Look in key backup
+        auto copy    = ev;
+        copy.room_id = room_id_;
+        if (pending_key_requests.count(ev.content.session_id)) {
+                auto &r = pending_key_requests.at(ev.content.session_id);
+                r.events.push_back(copy);
+
+                // automatically request once every 10 min, manually every 1 min
+                qint64 delay = manual ? 60 : (60 * 10);
+                if (r.requested_at + delay < QDateTime::currentSecsSinceEpoch()) {
+                        r.requested_at = QDateTime::currentSecsSinceEpoch();
+                        olm::send_key_request_for(copy, r.request_id);
+                }
+        } else {
+                PendingKeyRequests request;
+                request.request_id   = "key_request." + http::client()->generate_txn_id();
+                request.requested_at = QDateTime::currentSecsSinceEpoch();
+                request.events.push_back(copy);
+                olm::send_key_request_for(copy, request.request_id);
+                pending_key_requests[ev.content.session_id] = request;
+        }
 }
 
 void
@@ -877,15 +796,56 @@ EventStore::get(std::string id, std::string_view related_to, bool decrypt, bool
                 events_by_id_.insert(index, event_ptr);
         }
 
-        if (decrypt)
+        if (decrypt) {
                 if (auto encrypted =
                       std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
-                        event_ptr))
-                        return decryptEvent(index, *encrypted);
+                        event_ptr)) {
+                        auto decrypted = decryptEvent(index, *encrypted);
+                        if (decrypted->event)
+                                return &*decrypted->event;
+                }
+        }
 
         return event_ptr;
 }
 
+olm::DecryptionErrorCode
+EventStore::decryptionError(std::string id)
+{
+        if (this->thread() != QThread::currentThread())
+                nhlog::db()->warn("{} called from a different thread!", __func__);
+
+        if (id.empty())
+                return olm::DecryptionErrorCode::NoError;
+
+        IdIndex index{room_id_, std::move(id)};
+        auto edits_ = edits(index.id);
+        if (!edits_.empty()) {
+                index.id = mtx::accessors::event_id(edits_.back());
+                auto event_ptr =
+                  new mtx::events::collections::TimelineEvents(std::move(edits_.back()));
+                events_by_id_.insert(index, event_ptr);
+        }
+
+        auto event_ptr = events_by_id_.object(index);
+        if (!event_ptr) {
+                auto event = cache::client()->getEvent(room_id_, index.id);
+                if (!event) {
+                        return olm::DecryptionErrorCode::NoError;
+                }
+                event_ptr = new mtx::events::collections::TimelineEvents(std::move(event->data));
+                events_by_id_.insert(index, event_ptr);
+        }
+
+        if (auto encrypted =
+              std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(event_ptr)) {
+                auto decrypted = decryptEvent(index, *encrypted);
+                return decrypted->error;
+        }
+
+        return olm::DecryptionErrorCode::NoError;
+}
+
 void
 EventStore::fetchMore()
 {
diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h
index 7c404102..59c1c7c0 100644
--- a/src/timeline/EventStore.h
+++ b/src/timeline/EventStore.h
@@ -15,6 +15,7 @@
 #include <mtx/responses/messages.hpp>
 #include <mtx/responses/sync.hpp>
 
+#include "Olm.h"
 #include "Reaction.h"
 
 class EventStore : public QObject
@@ -78,6 +79,9 @@ public:
         mtx::events::collections::TimelineEvents *get(int idx, bool decrypt = true);
 
         QVariantList reactions(const std::string &event_id);
+        olm::DecryptionErrorCode decryptionError(std::string id);
+        void requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &ev,
+                            bool manual);
 
         int size() const
         {
@@ -119,7 +123,7 @@ public slots:
 
 private:
         std::vector<mtx::events::collections::TimelineEvents> edits(const std::string &event_id);
-        mtx::events::collections::TimelineEvents *decryptEvent(
+        olm::DecryptionResult *decryptEvent(
           const IdIndex &idx,
           const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e);
         void handle_room_verification(mtx::events::collections::TimelineEvents event);
@@ -129,7 +133,7 @@ private:
         uint64_t first = std::numeric_limits<uint64_t>::max(),
                  last  = std::numeric_limits<uint64_t>::max();
 
-        static QCache<IdIndex, mtx::events::collections::TimelineEvents> decryptedEvents_;
+        static QCache<IdIndex, olm::DecryptionResult> decryptedEvents_;
         static QCache<Index, mtx::events::collections::TimelineEvents> events_;
         static QCache<IdIndex, mtx::events::collections::TimelineEvents> events_by_id_;
 
@@ -137,6 +141,7 @@ private:
         {
                 std::string request_id;
                 std::vector<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>> events;
+                qint64 requested_at;
         };
         std::map<std::string, PendingKeyRequests> pending_key_requests;
 
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index 56d0d1ce..f17081e5 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -19,9 +19,9 @@
 
 #include "Cache.h"
 #include "ChatPage.h"
+#include "CombinedImagePackModel.h"
 #include "CompletionProxyModel.h"
 #include "Config.h"
-#include "ImagePackModel.h"
 #include "Logging.h"
 #include "MainWindow.h"
 #include "MatrixClient.h"
@@ -503,7 +503,7 @@ InputBar::video(const QString &filename,
 }
 
 void
-InputBar::sticker(ImagePackModel *model, int row)
+InputBar::sticker(CombinedImagePackModel *model, int row)
 {
         if (!model || row < 0)
                 return;
diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index acedceb7..2e6fb5c0 100644
--- a/src/timeline/InputBar.h
+++ b/src/timeline/InputBar.h
@@ -12,7 +12,7 @@
 #include <mtx/responses/messages.hpp>
 
 class TimelineModel;
-class ImagePackModel;
+class CombinedImagePackModel;
 class QMimeData;
 class QDropEvent;
 class QStringList;
@@ -58,7 +58,7 @@ public slots:
                      MarkdownOverride useMarkdown = MarkdownOverride::NOT_SPECIFIED,
                      bool rainbowify              = false);
         void reaction(const QString &reactedEvent, const QString &reactionKey);
-        void sticker(ImagePackModel *model, int row);
+        void sticker(CombinedImagePackModel *model, int row);
 
 private slots:
         void startTyping();
diff --git a/src/timeline/Permissions.cpp b/src/timeline/Permissions.cpp
index 1eaab468..e4957045 100644
--- a/src/timeline/Permissions.cpp
+++ b/src/timeline/Permissions.cpp
@@ -8,9 +8,9 @@
 #include "MatrixClient.h"
 #include "TimelineModel.h"
 
-Permissions::Permissions(TimelineModel *parent)
+Permissions::Permissions(QString roomId, QObject *parent)
   : QObject(parent)
-  , room(parent)
+  , roomId_(roomId)
 {
         invalidate();
 }
@@ -19,7 +19,7 @@ void
 Permissions::invalidate()
 {
         pl = cache::client()
-               ->getStateEvent<mtx::events::state::PowerLevels>(room->roomId().toStdString())
+               ->getStateEvent<mtx::events::state::PowerLevels>(roomId_.toStdString())
                .value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
                .content;
 }
diff --git a/src/timeline/Permissions.h b/src/timeline/Permissions.h
index f7e6f389..7aab1ddb 100644
--- a/src/timeline/Permissions.h
+++ b/src/timeline/Permissions.h
@@ -15,7 +15,7 @@ class Permissions : public QObject
         Q_OBJECT
 
 public:
-        Permissions(TimelineModel *parent);
+        Permissions(QString roomId, QObject *parent = nullptr);
 
         Q_INVOKABLE bool canInvite();
         Q_INVOKABLE bool canBan();
@@ -28,6 +28,6 @@ public:
         void invalidate();
 
 private:
-        TimelineModel *room;
+        QString roomId_;
         mtx::events::state::PowerLevels pl;
 };
diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp
index f7f377fb..f4c927ac 100644
--- a/src/timeline/RoomlistModel.cpp
+++ b/src/timeline/RoomlistModel.cpp
@@ -533,6 +533,8 @@ RoomlistModel::initializeRooms()
         for (const auto &id : cache::client()->roomIds())
                 addRoom(id, true);
 
+        nhlog::db()->info("Restored {} rooms from cache", rowCount());
+
         endResetModel();
 }
 
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index abfe28a9..99e00a67 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -25,11 +25,12 @@
 #include "Logging.h"
 #include "MainWindow.h"
 #include "MatrixClient.h"
+#include "MemberList.h"
 #include "MxcImageProvider.h"
 #include "Olm.h"
+#include "ReadReceiptsModel.h"
 #include "TimelineViewManager.h"
 #include "Utils.h"
-#include "dialogs/RawMessage.h"
 
 Q_DECLARE_METATYPE(QModelIndex)
 
@@ -307,6 +308,15 @@ qml_mtx_events::fromRoomEventType(qml_mtx_events::EventType t)
         case qml_mtx_events::KeyVerificationDone:
         case qml_mtx_events::KeyVerificationReady:
                 return mtx::events::EventType::RoomMessage;
+                //! m.image_pack, currently im.ponies.room_emotes
+        case qml_mtx_events::ImagePackInRoom:
+                return mtx::events::EventType::ImagePackRooms;
+        //! m.image_pack, currently im.ponies.user_emotes
+        case qml_mtx_events::ImagePackInAccountData:
+                return mtx::events::EventType::ImagePackInAccountData;
+        //! m.image_pack.rooms, currently im.ponies.emote_rooms
+        case qml_mtx_events::ImagePackRooms:
+                return mtx::events::EventType::ImagePackRooms;
         default:
                 return mtx::events::EventType::Unsupported;
         };
@@ -317,6 +327,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
   , events(room_id.toStdString(), this)
   , room_id_(room_id)
   , manager_(manager)
+  , permissions_{room_id}
 {
         lastMessage_.timestamp = 0;
 
@@ -325,6 +336,10 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
                 this->isSpace_ = create->content.type == mtx::events::state::room_type::space;
         this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString());
 
+        // this connection will simplify adding the plainRoomNameChanged() signal everywhere that it
+        // needs to be
+        connect(this, &TimelineModel::roomNameChanged, this, &TimelineModel::plainRoomNameChanged);
+
         connect(
           this,
           &TimelineModel::redactionFailed,
@@ -344,6 +359,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
           &EventStore::dataChanged,
           this,
           [this](int from, int to) {
+                  relatedEventCacheBuster++;
                   nhlog::ui()->debug(
                     "data changed {} to {}", events.size() - to - 1, events.size() - from - 1);
                   emit dataChanged(index(events.size() - to - 1, 0),
@@ -436,6 +452,7 @@ TimelineModel::roleNames() const
           {IsEditable, "isEditable"},
           {IsEncrypted, "isEncrypted"},
           {Trustlevel, "trustlevel"},
+          {EncryptionError, "encryptionError"},
           {ReplyTo, "replyTo"},
           {Reactions, "reactions"},
           {RoomId, "roomId"},
@@ -443,6 +460,7 @@ TimelineModel::roleNames() const
           {RoomTopic, "roomTopic"},
           {CallType, "callType"},
           {Dump, "dump"},
+          {RelatedEventCacheBuster, "relatedEventCacheBuster"},
         };
 }
 int
@@ -622,6 +640,9 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
                 return crypto::Trust::Unverified;
         }
 
+        case EncryptionError:
+                return events.decryptionError(event_id(event));
+
         case ReplyTo:
                 return QVariant(QString::fromStdString(relations(event).reply_to().value_or("")));
         case Reactions: {
@@ -673,9 +694,12 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
                 m.insert(names[RoomName], data(event, static_cast<int>(RoomName)));
                 m.insert(names[RoomTopic], data(event, static_cast<int>(RoomTopic)));
                 m.insert(names[CallType], data(event, static_cast<int>(CallType)));
+                m.insert(names[EncryptionError], data(event, static_cast<int>(EncryptionError)));
 
                 return QVariant(m);
         }
+        case RelatedEventCacheBuster:
+                return relatedEventCacheBuster;
         default:
                 return QVariant();
         }
@@ -1015,14 +1039,13 @@ TimelineModel::formatDateSeparator(QDate date) const
 }
 
 void
-TimelineModel::viewRawMessage(QString id) const
+TimelineModel::viewRawMessage(QString id)
 {
         auto e = events.get(id.toStdString(), "", false);
         if (!e)
                 return;
         std::string ev = mtx::accessors::serialize_event(*e).dump(4);
-        auto dialog    = new dialogs::RawMessage(QString::fromStdString(ev));
-        Q_UNUSED(dialog);
+        emit showRawMessageDialog(QString::fromStdString(ev));
 }
 
 void
@@ -1036,15 +1059,14 @@ TimelineModel::forwardMessage(QString eventId, QString roomId)
 }
 
 void
-TimelineModel::viewDecryptedRawMessage(QString id) const
+TimelineModel::viewDecryptedRawMessage(QString id)
 {
         auto e = events.get(id.toStdString(), "");
         if (!e)
                 return;
 
         std::string ev = mtx::accessors::serialize_event(*e).dump(4);
-        auto dialog    = new dialogs::RawMessage(QString::fromStdString(ev));
-        Q_UNUSED(dialog);
+        emit showRawMessageDialog(QString::fromStdString(ev));
 }
 
 void
@@ -1057,14 +1079,6 @@ TimelineModel::openUserProfile(QString userid)
 }
 
 void
-TimelineModel::openRoomSettings()
-{
-        RoomSettings *settings = new RoomSettings(roomId(), this);
-        connect(this, &TimelineModel::roomAvatarUrlChanged, settings, &RoomSettings::avatarChanged);
-        openRoomSettingsDialog(settings);
-}
-
-void
 TimelineModel::replyAction(QString id)
 {
         setReply(id);
@@ -1087,9 +1101,9 @@ TimelineModel::relatedInfo(QString id)
 }
 
 void
-TimelineModel::readReceiptsAction(QString id) const
+TimelineModel::showReadReceipts(QString id)
 {
-        MainWindow::instance()->openReadReceiptsDialog(id);
+        emit openReadReceiptsDialog(new ReadReceiptsProxy{id, roomId(), this});
 }
 
 void
@@ -1543,6 +1557,17 @@ TimelineModel::scrollTimerEvent()
 }
 
 void
+TimelineModel::requestKeyForEvent(QString id)
+{
+        auto encrypted_event = events.get(id.toStdString(), "", false);
+        if (encrypted_event) {
+                if (auto ev = std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
+                      encrypted_event))
+                        events.requestSession(*ev, true);
+        }
+}
+
+void
 TimelineModel::copyLinkToEvent(QString eventId) const
 {
         QStringList vias;
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 0e2895d4..ad7cfbbb 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -17,7 +17,10 @@
 #include "CacheStructs.h"
 #include "EventStore.h"
 #include "InputBar.h"
+#include "InviteesModel.h"
+#include "MemberList.h"
 #include "Permissions.h"
+#include "ReadReceiptsModel.h"
 #include "ui/RoomSettings.h"
 #include "ui/UserProfile.h"
 
@@ -104,7 +107,13 @@ enum EventType
         KeyVerificationCancel,
         KeyVerificationKey,
         KeyVerificationDone,
-        KeyVerificationReady
+        KeyVerificationReady,
+        //! m.image_pack, currently im.ponies.room_emotes
+        ImagePackInRoom,
+        //! m.image_pack, currently im.ponies.user_emotes
+        ImagePackInAccountData,
+        //! m.image_pack.rooms, currently im.ponies.emote_rooms
+        ImagePackRooms,
 };
 Q_ENUM_NS(EventType)
 mtx::events::EventType fromRoomEventType(qml_mtx_events::EventType);
@@ -158,7 +167,9 @@ class TimelineModel : public QAbstractListModel
         Q_PROPERTY(QString edit READ edit WRITE setEdit NOTIFY editChanged RESET resetEdit)
         Q_PROPERTY(
           bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged)
+        Q_PROPERTY(QString roomId READ roomId CONSTANT)
         Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged)
+        Q_PROPERTY(QString plainRoomName READ plainRoomName NOTIFY plainRoomNameChanged)
         Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged)
         Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged)
         Q_PROPERTY(int roomMemberCount READ roomMemberCount NOTIFY roomMemberCountChanged)
@@ -201,6 +212,7 @@ public:
                 IsEditable,
                 IsEncrypted,
                 Trustlevel,
+                EncryptionError,
                 ReplyTo,
                 Reactions,
                 RoomId,
@@ -208,6 +220,7 @@ public:
                 RoomTopic,
                 CallType,
                 Dump,
+                RelatedEventCacheBuster,
         };
         Q_ENUM(Roles);
 
@@ -230,14 +243,13 @@ public:
         Q_INVOKABLE QString formatGuestAccessEvent(QString id);
         Q_INVOKABLE QString formatPowerLevelEvent(QString id);
 
-        Q_INVOKABLE void viewRawMessage(QString id) const;
+        Q_INVOKABLE void viewRawMessage(QString id);
         Q_INVOKABLE void forwardMessage(QString eventId, QString roomId);
-        Q_INVOKABLE void viewDecryptedRawMessage(QString id) const;
+        Q_INVOKABLE void viewDecryptedRawMessage(QString id);
         Q_INVOKABLE void openUserProfile(QString userid);
-        Q_INVOKABLE void openRoomSettings();
         Q_INVOKABLE void editAction(QString id);
         Q_INVOKABLE void replyAction(QString id);
-        Q_INVOKABLE void readReceiptsAction(QString id) const;
+        Q_INVOKABLE void showReadReceipts(QString id);
         Q_INVOKABLE void redactEvent(QString id);
         Q_INVOKABLE int idToIndex(QString id) const;
         Q_INVOKABLE QString indexToId(int index) const;
@@ -253,6 +265,8 @@ public:
                 endResetModel();
         }
 
+        Q_INVOKABLE void requestKeyForEvent(QString id);
+
         std::vector<::Reaction> reactions(const std::string &event_id)
         {
                 auto list = events.reactions(event_id);
@@ -344,6 +358,8 @@ signals:
         void typingUsersChanged(std::vector<QString> users);
         void replyChanged(QString reply);
         void editChanged(QString reply);
+        void openReadReceiptsDialog(ReadReceiptsProxy *rr);
+        void showRawMessageDialog(QString rawMessage);
         void paginationInProgressChanged(const bool);
         void newCallEvent(const mtx::events::collections::TimelineEvents &event);
         void scrollToIndex(int index);
@@ -351,14 +367,13 @@ signals:
         void lastMessageChanged();
         void notificationsChanged();
 
-        void openRoomSettingsDialog(RoomSettings *settings);
-
         void newMessageToSend(mtx::events::collections::TimelineEvents event);
         void addPendingMessageToStore(mtx::events::collections::TimelineEvents event);
         void updateFlowEventId(std::string event_id);
 
         void encryptionChanged();
         void roomNameChanged();
+        void plainRoomNameChanged();
         void roomTopicChanged();
         void roomAvatarUrlChanged();
         void roomMemberCountChanged();
@@ -388,7 +403,7 @@ private:
         TimelineViewManager *manager_;
 
         InputBar input_{this};
-        Permissions permissions_{this};
+        Permissions permissions_;
 
         QTimer showEventTimer{this};
         QString eventIdToShow;
@@ -400,6 +415,8 @@ private:
 
         int notification_count = 0, highlight_count = 0;
 
+        unsigned int relatedEventCacheBuster = 0;
+
         bool decryptDescription     = true;
         bool m_paginationInProgress = false;
         bool isSpace_               = false;
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 3e69f92b..b23ed278 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -15,16 +15,20 @@
 #include "ChatPage.h"
 #include "Clipboard.h"
 #include "ColorImageProvider.h"
+#include "CombinedImagePackModel.h"
 #include "CompletionProxyModel.h"
 #include "DelegateChooser.h"
 #include "DeviceVerificationFlow.h"
 #include "EventAccessors.h"
-#include "ImagePackModel.h"
+#include "ImagePackListModel.h"
+#include "InviteesModel.h"
 #include "Logging.h"
 #include "MainWindow.h"
 #include "MatrixClient.h"
 #include "MxcImageProvider.h"
+#include "ReadReceiptsModel.h"
 #include "RoomsModel.h"
+#include "SingleImagePackModel.h"
 #include "UserSettingsPage.h"
 #include "UsersModel.h"
 #include "dialogs/ImageOverlay.h"
@@ -145,7 +149,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
         qRegisterMetaType<mtx::events::msg::KeyVerificationReady>();
         qRegisterMetaType<mtx::events::msg::KeyVerificationRequest>();
         qRegisterMetaType<mtx::events::msg::KeyVerificationStart>();
-        qRegisterMetaType<ImagePackModel *>();
+        qRegisterMetaType<CombinedImagePackModel *>();
 
         qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
                                          "im.nheko",
@@ -154,6 +158,8 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
                                          "MtxEvent",
                                          "Can't instantiate enum!");
         qmlRegisterUncreatableMetaObject(
+          olm::staticMetaObject, "im.nheko", 1, 0, "Olm", "Can't instantiate enum!");
+        qmlRegisterUncreatableMetaObject(
           crypto::staticMetaObject, "im.nheko", 1, 0, "Crypto", "Can't instantiate enum!");
         qmlRegisterUncreatableMetaObject(verification::staticMetaObject,
                                          "im.nheko",
@@ -174,6 +180,8 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
           0,
           "UserProfileModel",
           "UserProfile needs to be instantiated on the C++ side");
+        qmlRegisterUncreatableType<MemberList>(
+          "im.nheko", 1, 0, "MemberList", "MemberList needs to be instantiated on the C++ side");
         qmlRegisterUncreatableType<RoomSettings>(
           "im.nheko",
           1,
@@ -182,6 +190,30 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
           "Room Settings needs to be instantiated on the C++ side");
         qmlRegisterUncreatableType<TimelineModel>(
           "im.nheko", 1, 0, "Room", "Room needs to be instantiated on the C++ side");
+        qmlRegisterUncreatableType<ImagePackListModel>(
+          "im.nheko",
+          1,
+          0,
+          "ImagePackListModel",
+          "ImagePackListModel needs to be instantiated on the C++ side");
+        qmlRegisterUncreatableType<SingleImagePackModel>(
+          "im.nheko",
+          1,
+          0,
+          "SingleImagePackModel",
+          "SingleImagePackModel needs to be instantiated on the C++ side");
+        qmlRegisterUncreatableType<InviteesModel>(
+          "im.nheko",
+          1,
+          0,
+          "InviteesModel",
+          "InviteesModel needs to be instantiated on the C++ side");
+        qmlRegisterUncreatableType<ReadReceiptsProxy>(
+          "im.nheko",
+          1,
+          0,
+          "ReadReceiptsProxy",
+          "ReadReceiptsProxy needs to be instantiated on the C++ side");
 
         static auto self = this;
         qmlRegisterSingletonType<MainWindow>(
@@ -343,6 +375,41 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
 }
 
 void
+TimelineViewManager::openRoomMembers(QString room_id)
+{
+        MemberList *memberList = new MemberList(room_id, this);
+        emit openRoomMembersDialog(memberList);
+}
+
+void
+TimelineViewManager::openRoomSettings(QString room_id)
+{
+        RoomSettings *settings = new RoomSettings(room_id, this);
+        connect(rooms_->getRoomById(room_id).data(),
+                &TimelineModel::roomAvatarUrlChanged,
+                settings,
+                &RoomSettings::avatarChanged);
+        emit openRoomSettingsDialog(settings);
+}
+
+void
+TimelineViewManager::openInviteUsers(QString roomId)
+{
+        InviteesModel *model = new InviteesModel{this};
+        connect(model, &InviteesModel::accept, this, [this, model, roomId]() {
+                emit inviteUsers(roomId, model->mxids());
+        });
+        emit openInviteUsersDialog(model);
+}
+
+void
+TimelineViewManager::openGlobalUserProfile(QString userId)
+{
+        UserProfile *profile = new UserProfile{QString{}, userId, this};
+        emit openProfile(profile);
+}
+
+void
 TimelineViewManager::setVideoCallItem()
 {
         WebRTCSession::instance().setVideoItem(
@@ -400,6 +467,12 @@ TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId)
 }
 
 void
+TimelineViewManager::openImagePackSettings(QString roomid)
+{
+        emit showImagePackSettings(new ImagePackListModel(roomid.toStdString(), this));
+}
+
+void
 TimelineViewManager::openImageOverlayInternal(QString eventId, QImage img)
 {
         auto pixmap = QPixmap::fromImage(img);
@@ -422,17 +495,6 @@ TimelineViewManager::openImageOverlayInternal(QString eventId, QImage img)
 }
 
 void
-TimelineViewManager::openInviteUsersDialog()
-{
-        MainWindow::instance()->openInviteUsersDialog(
-          [this](const QStringList &invitees) { emit inviteUsers(invitees); });
-}
-void
-TimelineViewManager::openMemberListDialog(QString roomid) const
-{
-        MainWindow::instance()->openMemberListDialog(roomid);
-}
-void
 TimelineViewManager::openLeaveRoomDialog(QString roomid) const
 {
         MainWindow::instance()->openLeaveRoomDialog(roomid);
@@ -596,7 +658,7 @@ TimelineViewManager::completerFor(QString completerName, QString roomId)
                 roomModel->setParent(proxy);
                 return proxy;
         } else if (completerName == "stickers") {
-                auto stickerModel = new ImagePackModel(roomId.toStdString(), true);
+                auto stickerModel = new CombinedImagePackModel(roomId.toStdString(), true);
                 auto proxy = new CompletionProxyModel(stickerModel, 1, static_cast<size_t>(-1) / 4);
                 stickerModel->setParent(proxy);
                 return proxy;
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 15b4f523..54e3a935 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -33,6 +33,7 @@ class ColorImageProvider;
 class UserSettings;
 class ChatPage;
 class DeviceVerificationFlow;
+class ImagePackListModel;
 
 class TimelineViewManager : public QObject
 {
@@ -57,6 +58,7 @@ public:
         Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; }
         bool isWindowFocused() const { return isWindowFocused_; }
         Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId);
+        Q_INVOKABLE void openImagePackSettings(QString roomid);
         Q_INVOKABLE QColor userColor(QString id, QColor background);
         Q_INVOKABLE QString escapeEmoji(QString str) const;
         Q_INVOKABLE QString htmlEscape(QString str) const { return str.toHtmlEscaped(); }
@@ -64,9 +66,12 @@ public:
         Q_INVOKABLE QString userPresence(QString id) const;
         Q_INVOKABLE QString userStatus(QString id) const;
 
+        Q_INVOKABLE void openRoomMembers(QString room_id);
+        Q_INVOKABLE void openRoomSettings(QString room_id);
+        Q_INVOKABLE void openInviteUsers(QString roomId);
+        Q_INVOKABLE void openGlobalUserProfile(QString userId);
+
         Q_INVOKABLE void focusMessageInput();
-        Q_INVOKABLE void openInviteUsersDialog();
-        Q_INVOKABLE void openMemberListDialog(QString roomid) const;
         Q_INVOKABLE void openLeaveRoomDialog(QString roomid) const;
         Q_INVOKABLE void removeVerificationFlow(DeviceVerificationFlow *flow);
 
@@ -81,11 +86,17 @@ signals:
         void replyingEventChanged(QString replyingEvent);
         void replyClosed();
         void newDeviceVerificationRequest(DeviceVerificationFlow *flow);
-        void inviteUsers(QStringList users);
+        void inviteUsers(QString roomId, QStringList users);
+        void showRoomList();
+        void narrowViewChanged();
         void focusChanged();
         void focusInput();
         void openImageOverlayInternalCb(QString eventId, QImage img);
+        void openRoomMembersDialog(MemberList *members);
+        void openRoomSettingsDialog(RoomSettings *settings);
+        void openInviteUsersDialog(InviteesModel *invitees);
         void openProfile(UserProfile *profile);
+        void showImagePackSettings(ImagePackListModel *packlist);
 
 public slots:
         void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
diff --git a/src/ui/Avatar.cpp b/src/ui/Avatar.cpp
deleted file mode 100644
index 154a0e2c..00000000
--- a/src/ui/Avatar.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QPainter>
-#include <QPainterPath>
-#include <QSettings>
-
-#include "AvatarProvider.h"
-#include "Utils.h"
-#include "ui/Avatar.h"
-
-Avatar::Avatar(QWidget *parent, int size)
-  : QWidget(parent)
-  , size_(size)
-{
-        type_   = ui::AvatarType::Letter;
-        letter_ = "A";
-
-        QFont _font(font());
-        _font.setPointSizeF(ui::FontSize);
-        setFont(_font);
-
-        QSizePolicy policy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
-        setSizePolicy(policy);
-}
-
-QColor
-Avatar::textColor() const
-{
-        if (!text_color_.isValid())
-                return QColor("black");
-
-        return text_color_;
-}
-
-QColor
-Avatar::backgroundColor() const
-{
-        if (!text_color_.isValid())
-                return QColor("white");
-
-        return background_color_;
-}
-
-QSize
-Avatar::sizeHint() const
-{
-        return QSize(size_ + 2, size_ + 2);
-}
-
-void
-Avatar::setTextColor(const QColor &color)
-{
-        text_color_ = color;
-}
-
-void
-Avatar::setBackgroundColor(const QColor &color)
-{
-        background_color_ = color;
-}
-
-void
-Avatar::setLetter(const QString &letter)
-{
-        letter_ = letter;
-        type_   = ui::AvatarType::Letter;
-        update();
-}
-
-void
-Avatar::setImage(const QString &avatar_url)
-{
-        avatar_url_ = avatar_url;
-        AvatarProvider::resolve(avatar_url,
-                                static_cast<int>(size_ * pixmap_.devicePixelRatio()),
-                                this,
-                                [this, requestedRatio = pixmap_.devicePixelRatio()](QPixmap pm) {
-                                        if (pm.isNull())
-                                                return;
-                                        type_   = ui::AvatarType::Image;
-                                        pixmap_ = pm;
-                                        pixmap_.setDevicePixelRatio(requestedRatio);
-                                        update();
-                                });
-}
-
-void
-Avatar::setImage(const QString &room, const QString &user)
-{
-        room_ = room;
-        user_ = user;
-        AvatarProvider::resolve(room,
-                                user,
-                                static_cast<int>(size_ * pixmap_.devicePixelRatio()),
-                                this,
-                                [this, requestedRatio = pixmap_.devicePixelRatio()](QPixmap pm) {
-                                        if (pm.isNull())
-                                                return;
-                                        type_   = ui::AvatarType::Image;
-                                        pixmap_ = pm;
-                                        pixmap_.setDevicePixelRatio(requestedRatio);
-                                        update();
-                                });
-}
-
-void
-Avatar::setDevicePixelRatio(double ratio)
-{
-        if (type_ == ui::AvatarType::Image && abs(pixmap_.devicePixelRatio() - ratio) > 0.01) {
-                pixmap_ = pixmap_.scaled(QSize(size_, size_) * ratio);
-                pixmap_.setDevicePixelRatio(ratio);
-
-                if (!avatar_url_.isEmpty())
-                        setImage(avatar_url_);
-                else
-                        setImage(room_, user_);
-        }
-}
-
-void
-Avatar::paintEvent(QPaintEvent *)
-{
-        bool rounded = QSettings().value(QStringLiteral("user/avatar_circles"), true).toBool();
-
-        QPainter painter(this);
-
-        painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform |
-                               QPainter::TextAntialiasing);
-
-        QRectF r     = rect();
-        const int hs = size_ / 2;
-
-        if (type_ != ui::AvatarType::Image) {
-                QBrush brush;
-                brush.setStyle(Qt::SolidPattern);
-                brush.setColor(backgroundColor());
-
-                painter.setPen(Qt::NoPen);
-                painter.setBrush(brush);
-                rounded ? painter.drawEllipse(r) : painter.drawRoundedRect(r, 3, 3);
-        } else if (painter.isActive()) {
-                setDevicePixelRatio(painter.device()->devicePixelRatioF());
-        }
-
-        switch (type_) {
-        case ui::AvatarType::Image: {
-                QPainterPath ppath;
-
-                rounded ? ppath.addEllipse(width() / 2 - hs, height() / 2 - hs, size_, size_)
-                        : ppath.addRoundedRect(r, 3, 3);
-
-                painter.setClipPath(ppath);
-                painter.drawPixmap(QRect(width() / 2 - hs, height() / 2 - hs, size_, size_),
-                                   pixmap_);
-                break;
-        }
-        case ui::AvatarType::Letter: {
-                painter.setPen(textColor());
-                painter.setBrush(Qt::NoBrush);
-                painter.drawText(r.translated(0, -1), Qt::AlignCenter, letter_);
-                break;
-        }
-        default:
-                break;
-        }
-}
diff --git a/src/ui/Avatar.h b/src/ui/Avatar.h
deleted file mode 100644
index bbf05be3..00000000
--- a/src/ui/Avatar.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QImage>
-#include <QPixmap>
-#include <QWidget>
-
-#include "Theme.h"
-
-class Avatar : public QWidget
-{
-        Q_OBJECT
-
-        Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
-        Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
-
-public:
-        explicit Avatar(QWidget *parent = nullptr, int size = ui::AvatarSize);
-
-        void setBackgroundColor(const QColor &color);
-        void setImage(const QString &avatar_url);
-        void setImage(const QString &room, const QString &user);
-        void setLetter(const QString &letter);
-        void setTextColor(const QColor &color);
-        void setDevicePixelRatio(double ratio);
-
-        QColor backgroundColor() const;
-        QColor textColor() const;
-
-        QSize sizeHint() const override;
-
-protected:
-        void paintEvent(QPaintEvent *event) override;
-
-private:
-        void init();
-
-        ui::AvatarType type_;
-        QString letter_;
-        QString avatar_url_, room_, user_;
-        QColor background_color_;
-        QColor text_color_;
-        QPixmap pixmap_;
-        int size_;
-};
diff --git a/src/ui/InfoMessage.cpp b/src/ui/InfoMessage.cpp
index fb3b306a..ebe0e63f 100644
--- a/src/ui/InfoMessage.cpp
+++ b/src/ui/InfoMessage.cpp
@@ -29,13 +29,7 @@ InfoMessage::InfoMessage(QString msg, QWidget *parent)
         initFont();
 
         QFontMetrics fm{font()};
-#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
-        // width deprecated in 5.13
-        width_ = fm.width(msg_) + HPadding * 2;
-#else
-        width_ = fm.horizontalAdvance(msg_) + HPadding * 2;
-#endif
-
+        width_  = fm.horizontalAdvance(msg_) + HPadding * 2;
         height_ = fm.ascent() + 2 * VPadding;
 
         setFixedHeight(height_ + 2 * HMargin);
@@ -77,12 +71,7 @@ DateSeparator::DateSeparator(QDateTime datetime, QWidget *parent)
         msg_ = datetime.date().toString(fmt);
 
         QFontMetrics fm{font()};
-#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
-        // width deprecated in 5.13
-        width_ = fm.width(msg_) + HPadding * 2;
-#else
-        width_ = fm.horizontalAdvance(msg_) + HPadding * 2;
-#endif
+        width_  = fm.horizontalAdvance(msg_) + HPadding * 2;
         height_ = fm.ascent() + 2 * VPadding;
 
         setFixedHeight(height_ + 2 * HMargin);
diff --git a/src/ui/NhekoGlobalObject.cpp b/src/ui/NhekoGlobalObject.cpp
index fea10839..9e0d706b 100644
--- a/src/ui/NhekoGlobalObject.cpp
+++ b/src/ui/NhekoGlobalObject.cpp
@@ -6,6 +6,7 @@
 
 #include <QDesktopServices>
 #include <QUrl>
+#include <QWindow>
 
 #include "Cache_p.h"
 #include "ChatPage.h"
@@ -140,3 +141,9 @@ Nheko::openJoinRoomDialog() const
         MainWindow::instance()->openJoinRoomDialog(
           [](const QString &room_id) { ChatPage::instance()->joinRoom(room_id); });
 }
+
+void
+Nheko::reparent(QWindow *win) const
+{
+        win->setTransientParent(MainWindow::instance()->windowHandle());
+}
diff --git a/src/ui/NhekoGlobalObject.h b/src/ui/NhekoGlobalObject.h
index 14135fd1..d4d119dc 100644
--- a/src/ui/NhekoGlobalObject.h
+++ b/src/ui/NhekoGlobalObject.h
@@ -4,12 +4,15 @@
 
 #pragma once
 
+#include <QFontDatabase>
 #include <QObject>
 #include <QPalette>
 
 #include "Theme.h"
 #include "UserProfile.h"
 
+class QWindow;
+
 class Nheko : public QObject
 {
         Q_OBJECT
@@ -38,12 +41,17 @@ public:
         int paddingLarge() const { return 20; }
         UserProfile *currentUser() const;
 
+        Q_INVOKABLE QFont monospaceFont() const
+        {
+                return QFontDatabase::systemFont(QFontDatabase::FixedFont);
+        }
         Q_INVOKABLE void openLink(QString link) const;
         Q_INVOKABLE void setStatusMessage(QString msg) const;
         Q_INVOKABLE void showUserSettingsPage() const;
         Q_INVOKABLE void openLogoutDialog() const;
         Q_INVOKABLE void openCreateRoomDialog() const;
         Q_INVOKABLE void openJoinRoomDialog() const;
+        Q_INVOKABLE void reparent(QWindow *win) const;
 
 public slots:
         void updateUserProfile();
diff --git a/src/ui/Painter.h b/src/ui/Painter.h
index 3353f0c7..9f974116 100644
--- a/src/ui/Painter.h
+++ b/src/ui/Painter.h
@@ -27,12 +27,7 @@ public:
         {
                 QFontMetrics m(fontMetrics());
                 if (textWidth < 0) {
-#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
-                        // deprecated in 5.13:
-                        textWidth = m.width(text);
-#else
                         textWidth = m.horizontalAdvance(text);
-#endif
                 }
                 drawText((outerw - x - textWidth), y + m.ascent(), text);
         }
diff --git a/src/ui/RoomSettings.cpp b/src/ui/RoomSettings.cpp
index f78ef09b..fcba8205 100644
--- a/src/ui/RoomSettings.cpp
+++ b/src/ui/RoomSettings.cpp
@@ -291,19 +291,6 @@ RoomSettings::accessJoinRules()
         return accessRules_;
 }
 
-bool
-RoomSettings::respondsToKeyRequests()
-{
-        return usesEncryption_ && utils::respondsToKeyRequests(roomid_);
-}
-
-void
-RoomSettings::changeKeyRequestsPreference(bool isOn)
-{
-        utils::setKeyRequestsPreference(roomid_, isOn);
-        emit keyRequestsChanged();
-}
-
 void
 RoomSettings::enableEncryption()
 {
diff --git a/src/ui/RoomSettings.h b/src/ui/RoomSettings.h
index 367f3111..1c8b47d6 100644
--- a/src/ui/RoomSettings.h
+++ b/src/ui/RoomSettings.h
@@ -78,7 +78,6 @@ class RoomSettings : public QObject
         Q_PROPERTY(bool canChangeJoinRules READ canChangeJoinRules CONSTANT)
         Q_PROPERTY(bool canChangeNameAndTopic READ canChangeNameAndTopic CONSTANT)
         Q_PROPERTY(bool isEncryptionEnabled READ isEncryptionEnabled NOTIFY encryptionChanged)
-        Q_PROPERTY(bool respondsToKeyRequests READ respondsToKeyRequests NOTIFY keyRequestsChanged)
 
 public:
         RoomSettings(QString roomid, QObject *parent = nullptr);
@@ -91,7 +90,6 @@ public:
         int memberCount() const;
         int notifications();
         int accessJoinRules();
-        bool respondsToKeyRequests();
         bool isLoading() const;
         //! Whether the user has enough power level to send m.room.join_rules events.
         bool canChangeJoinRules() const;
@@ -106,7 +104,6 @@ public:
         Q_INVOKABLE void openEditModal();
         Q_INVOKABLE void changeAccessRules(int index);
         Q_INVOKABLE void changeNotifications(int currentIndex);
-        Q_INVOKABLE void changeKeyRequestsPreference(bool isOn);
 
 signals:
         void loadingChanged();
@@ -114,7 +111,6 @@ signals:
         void roomTopicChanged();
         void avatarUrlChanged();
         void encryptionChanged();
-        void keyRequestsChanged();
         void notificationsChanged();
         void accessJoinRulesChanged();
         void displayError(const QString &errorMessage);
@@ -136,4 +132,4 @@ private:
         RoomInfo info_;
         int notifications_ = 0;
         int accessRules_   = 0;
-};
\ No newline at end of file
+};