diff --git a/app/src/main/java/org/futo/circles/feature/circles/CirclesDataSource.kt b/app/src/main/java/org/futo/circles/feature/circles/CirclesDataSource.kt
index ad3c3d1a1ee005e7cf9b83244a0eba5a656e80ed..efb3b622e4a2dba5262444cc65f5b121e2dc9b3a 100644
--- a/app/src/main/java/org/futo/circles/feature/circles/CirclesDataSource.kt
+++ b/app/src/main/java/org/futo/circles/feature/circles/CirclesDataSource.kt
@@ -1,18 +1,10 @@
 package org.futo.circles.feature.circles
 
-import androidx.lifecycle.asFlow
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.update
-import kotlinx.coroutines.withContext
-import org.futo.circles.core.extensions.getKnownUsersFlow
+import org.futo.circles.core.feature.room.RoomListHelper
 import org.futo.circles.core.feature.workspace.SharedCircleDataSource
 import org.futo.circles.core.feature.workspace.SpacesTreeAccountDataSource
 import org.futo.circles.core.model.CIRCLES_SPACE_ACCOUNT_DATA_KEY
 import org.futo.circles.core.model.TIMELINE_TYPE
-import org.futo.circles.core.provider.MatrixSessionProvider
 import org.futo.circles.core.utils.getJoinedRoomById
 import org.futo.circles.mapping.toInviteCircleListItem
 import org.futo.circles.mapping.toJoinedCircleListItem
@@ -20,42 +12,26 @@ import org.futo.circles.model.CircleListItem
 import org.futo.circles.model.CirclesHeaderItem
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
-import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
 import javax.inject.Inject
 
 class CirclesDataSource @Inject constructor(
     private val spacesTreeAccountDataSource: SpacesTreeAccountDataSource,
-    private val sharedCircleDataSource: SharedCircleDataSource
+    private val sharedCircleDataSource: SharedCircleDataSource,
+    private val roomListHelper: RoomListHelper
 ) {
 
-    private val roomIdsToUnblurProfile = MutableStateFlow<Set<String>>(emptySet())
-
-    fun getCirclesFlow() = combine(
-        MatrixSessionProvider.getSessionOrThrow().roomService()
-            .getRoomSummariesLive(roomSummaryQueryParams { excludeType = null })
-            .asFlow(),
-        MatrixSessionProvider.getSessionOrThrow().getKnownUsersFlow(),
-        roomIdsToUnblurProfile,
-        MatrixSessionProvider.getSessionOrThrow().roomService().getChangeMembershipsLive().asFlow()
-    ) { roomSummaries, knownUsers, roomIdsToUnblur, _ ->
-        withContext(Dispatchers.IO) {
-            buildCirclesList(
-                roomSummaries,
-                knownUsers.map { it.userId }.toSet(),
-                roomIdsToUnblur
-            )
-        }
-    }.distinctUntilChanged()
+    fun getCirclesFlow() = roomListHelper.getRoomsFlow(::buildCirclesList, null)
 
     private fun buildCirclesList(
         list: List<RoomSummary>,
         knownUsersIds: Set<String>,
         roomIdsToUnblur: Set<String>
     ): List<CircleListItem> {
-        val invites =
-            list.filter { isInviteToCircleTimeline(it) }.map { it.toInviteCircleListItem(
-                shouldBlurIconFor(it, knownUsersIds, roomIdsToUnblur)
-            ) }
+        val invites = list.filter { isInviteToCircleTimeline(it) }.map {
+            it.toInviteCircleListItem(
+                roomListHelper.shouldBlurIconFor(it, knownUsersIds, roomIdsToUnblur)
+            )
+        }
 
         val joinedCirclesSpaceIds = getJoinedCirclesIds()
         val joinedCircles = list.filter { isJoinedCircle(it, joinedCirclesSpaceIds) }
@@ -108,18 +84,6 @@ class CirclesDataSource @Inject constructor(
         }
     }
 
-    private fun shouldBlurIconFor(
-        roomSummary: RoomSummary,
-        knownUserIds: Set<String>,
-        roomIdsToUnblur: Set<String>
-    ): Boolean {
-        val isKnownUser = knownUserIds.contains(roomSummary.inviterId)
-        val isRoomUnbluredByUser = roomIdsToUnblur.contains(roomSummary.roomId)
-        val hasIcon = roomSummary.avatarUrl.isNotEmpty()
-        return !isKnownUser && !isRoomUnbluredByUser && hasIcon
-    }
+    fun unblurProfileImageFor(id: String) = roomListHelper.unblurProfileImageFor(id)
 
-    fun unblurProfileImageFor(id: String) {
-        roomIdsToUnblurProfile.update { set -> set.toMutableSet().apply { add(id) } }
-    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/org/futo/circles/feature/circles/CirclesViewModel.kt b/app/src/main/java/org/futo/circles/feature/circles/CirclesViewModel.kt
index bf1bfeb1ec150f2c53bad4b1123f86774ab1a8b6..39f14d2665c93918a1a89f37653e1cf41e388b53 100644
--- a/app/src/main/java/org/futo/circles/feature/circles/CirclesViewModel.kt
+++ b/app/src/main/java/org/futo/circles/feature/circles/CirclesViewModel.kt
@@ -10,13 +10,12 @@ import org.futo.circles.core.base.SingleEventLiveData
 import org.futo.circles.core.extensions.Response
 import org.futo.circles.core.extensions.createResult
 import org.futo.circles.core.extensions.launchBg
-import org.futo.circles.core.feature.room.CreateRoomDataSource
+import org.futo.circles.core.feature.room.create.CreateRoomDataSource
 import org.futo.circles.core.feature.room.invite.InviteRequestsDataSource
 import org.futo.circles.core.model.LoadingData
 import org.futo.circles.core.provider.MatrixSessionProvider
 import org.futo.circles.core.utils.getTimelineRoomFor
 import org.futo.circles.model.CircleListItem
-import org.futo.circles.model.GroupListItem
 import org.matrix.android.sdk.api.session.getRoomSummary
 import javax.inject.Inject
 
diff --git a/app/src/main/java/org/futo/circles/feature/groups/GroupsDataSource.kt b/app/src/main/java/org/futo/circles/feature/groups/GroupsDataSource.kt
index 851e32b74c897edc3279eafe19ac044f75a46299..3bad78275366f0f976932fbabf9f03920b9b26c1 100644
--- a/app/src/main/java/org/futo/circles/feature/groups/GroupsDataSource.kt
+++ b/app/src/main/java/org/futo/circles/feature/groups/GroupsDataSource.kt
@@ -1,39 +1,18 @@
 package org.futo.circles.feature.groups
 
-import androidx.lifecycle.asFlow
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.update
-import kotlinx.coroutines.withContext
-import org.futo.circles.core.extensions.getKnownUsersFlow
+import org.futo.circles.core.feature.room.RoomListHelper
 import org.futo.circles.core.model.GROUP_TYPE
-import org.futo.circles.core.provider.MatrixSessionProvider
 import org.futo.circles.mapping.toInviteGroupListItem
 import org.futo.circles.mapping.toJoinedGroupListItem
 import org.futo.circles.model.GroupListItem
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
-import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
 import javax.inject.Inject
 
-class GroupsDataSource @Inject constructor() {
-
-    private val roomIdsToUnblurProfile = MutableStateFlow<Set<String>>(emptySet())
-
-    fun getGroupsFlow() = combine(
-        MatrixSessionProvider.getSessionOrThrow().roomService()
-            .getRoomSummariesLive(roomSummaryQueryParams()).asFlow(),
-        MatrixSessionProvider.getSessionOrThrow().getKnownUsersFlow(),
-        roomIdsToUnblurProfile,
-        MatrixSessionProvider.getSessionOrThrow().roomService().getChangeMembershipsLive().asFlow()
-    ) { roomSummaries, knownUsers, roomIdsToUnblur, _ ->
-        withContext(Dispatchers.IO) {
-            filterGroups(roomSummaries, knownUsers.map { it.userId }.toSet(), roomIdsToUnblur)
-        }
-    }.distinctUntilChanged()
-
+class GroupsDataSource @Inject constructor(
+    private val roomListHelper: RoomListHelper
+) {
+    fun getGroupsFlow() = roomListHelper.getRoomsFlow(::filterGroups)
 
     private fun filterGroups(
         list: List<RoomSummary>,
@@ -46,25 +25,12 @@ class GroupsDataSource @Inject constructor() {
         return mutableListOf<GroupListItem>().apply {
             addAll(invites.map {
                 it.toInviteGroupListItem(
-                    shouldBlurIconFor(it.roomId, it.inviterId, knownUsersIds, roomIdsToUnblur)
+                    roomListHelper.shouldBlurIconFor(it, knownUsersIds, roomIdsToUnblur)
                 )
             })
             addAll(joinedGroups.map { it.toJoinedGroupListItem() })
         }
     }
 
-    private fun shouldBlurIconFor(
-        roomId: String,
-        inviterId: String?,
-        knownUserIds: Set<String>,
-        roomIdsToUnblur: Set<String>
-    ): Boolean {
-        val isKnownUser = knownUserIds.contains(inviterId)
-        val isRoomUnbluredByUser = roomIdsToUnblur.contains(roomId)
-        return !isKnownUser && !isRoomUnbluredByUser
-    }
-
-    fun unblurProfileImageFor(id: String) {
-        roomIdsToUnblurProfile.update { set -> set.toMutableSet().apply { add(id) } }
-    }
+    fun unblurProfileImageFor(id: String) = roomListHelper.unblurProfileImageFor(id)
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/workspace/data_source/ConfigureWorkspaceDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/workspace/data_source/ConfigureWorkspaceDataSource.kt
index 71a1ea30b5cdf789bc13877fc0e8de780501a662..64699409eb49fea3f91c5d3f7c84155184173bcc 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/workspace/data_source/ConfigureWorkspaceDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/workspace/data_source/ConfigureWorkspaceDataSource.kt
@@ -1,7 +1,7 @@
 package org.futo.circles.auth.feature.workspace.data_source
 
 import kotlinx.coroutines.delay
-import org.futo.circles.core.feature.room.CreateRoomDataSource
+import org.futo.circles.core.feature.room.create.CreateRoomDataSource
 import org.futo.circles.core.feature.room.RoomRelationsBuilder
 import org.futo.circles.core.feature.workspace.SpacesTreeAccountDataSource
 import org.futo.circles.core.model.CirclesRoom
diff --git a/core/src/main/java/org/futo/circles/core/feature/room/RoomListHelper.kt b/core/src/main/java/org/futo/circles/core/feature/room/RoomListHelper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..eddf740b0b1585afb50906ffb1aae795bea13ea4
--- /dev/null
+++ b/core/src/main/java/org/futo/circles/core/feature/room/RoomListHelper.kt
@@ -0,0 +1,55 @@
+package org.futo.circles.core.feature.room
+
+import androidx.lifecycle.asFlow
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.withContext
+import org.futo.circles.core.extensions.getKnownUsersFlow
+import org.futo.circles.core.provider.MatrixSessionProvider
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.session.room.model.RoomType
+import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
+import javax.inject.Inject
+
+class RoomListHelper @Inject constructor() {
+
+    private val roomIdsToUnblurProfile = MutableStateFlow<Set<String>>(emptySet())
+
+    fun <T> getRoomsFlow(
+        buildList: (List<RoomSummary>, Set<String>, Set<String>) -> List<T>,
+        excludeRoomTypes: List<String>? = listOf(RoomType.SPACE)
+    ) = combine(
+        MatrixSessionProvider.getSessionOrThrow().roomService()
+            .getRoomSummariesLive(roomSummaryQueryParams { excludeType = excludeRoomTypes })
+            .asFlow(),
+        MatrixSessionProvider.getSessionOrThrow().getKnownUsersFlow(),
+        roomIdsToUnblurProfile,
+        MatrixSessionProvider.getSessionOrThrow().roomService().getChangeMembershipsLive().asFlow()
+    ) { roomSummaries, knownUsers, roomIdsToUnblur, _ ->
+        withContext(Dispatchers.IO) {
+            buildList(
+                roomSummaries,
+                knownUsers.map { it.userId }.toSet(),
+                roomIdsToUnblur
+            )
+        }
+    }.distinctUntilChanged()
+
+    fun shouldBlurIconFor(
+        roomSummary: RoomSummary,
+        knownUserIds: Set<String>,
+        roomIdsToUnblur: Set<String>
+    ): Boolean {
+        val isKnownUser = knownUserIds.contains(roomSummary.inviterId)
+        val isRoomUnbluredByUser = roomIdsToUnblur.contains(roomSummary.roomId)
+        val hasIcon = roomSummary.avatarUrl.isNotEmpty()
+        return !isKnownUser && !isRoomUnbluredByUser && hasIcon
+    }
+
+    fun unblurProfileImageFor(id: String) {
+        roomIdsToUnblurProfile.update { set -> set.toMutableSet().apply { add(id) } }
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/futo/circles/core/feature/room/CreateRoomDataSource.kt b/core/src/main/java/org/futo/circles/core/feature/room/create/CreateRoomDataSource.kt
similarity index 97%
rename from core/src/main/java/org/futo/circles/core/feature/room/CreateRoomDataSource.kt
rename to core/src/main/java/org/futo/circles/core/feature/room/create/CreateRoomDataSource.kt
index 8b1fb857efe1b196626ca05502d10e999aed7eda..bbaf8bf14f2590a0116a553d05f2771364b23183 100644
--- a/core/src/main/java/org/futo/circles/core/feature/room/CreateRoomDataSource.kt
+++ b/core/src/main/java/org/futo/circles/core/feature/room/create/CreateRoomDataSource.kt
@@ -1,8 +1,9 @@
-package org.futo.circles.core.feature.room
+package org.futo.circles.core.feature.room.create
 
 import android.content.Context
 import android.net.Uri
 import dagger.hilt.android.qualifiers.ApplicationContext
+import org.futo.circles.core.feature.room.RoomRelationsBuilder
 import org.futo.circles.core.feature.workspace.SharedCircleDataSource
 import org.futo.circles.core.feature.workspace.SpacesTreeAccountDataSource
 import org.futo.circles.core.model.Circle
diff --git a/core/src/main/java/org/futo/circles/core/feature/room/create/CreateRoomViewModel.kt b/core/src/main/java/org/futo/circles/core/feature/room/create/CreateRoomViewModel.kt
index 74bb7d178f7caeb09f523261a982a773bdec17b4..cda1c60888a10c6e157e1865c85985db37920eee 100644
--- a/core/src/main/java/org/futo/circles/core/feature/room/create/CreateRoomViewModel.kt
+++ b/core/src/main/java/org/futo/circles/core/feature/room/create/CreateRoomViewModel.kt
@@ -8,7 +8,6 @@ import org.futo.circles.core.base.SingleEventLiveData
 import org.futo.circles.core.extensions.Response
 import org.futo.circles.core.extensions.createResult
 import org.futo.circles.core.extensions.launchBg
-import org.futo.circles.core.feature.room.CreateRoomDataSource
 import org.futo.circles.core.model.Circle
 import org.futo.circles.core.model.CircleRoomTypeArg
 import org.futo.circles.core.model.Gallery
diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosDataSource.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosDataSource.kt
index 7af8d31a9cfa7ff1e17462a1271184e8933ea0de..b7e9253303a7a52f1f568307c363e0a52ca125f0 100644
--- a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosDataSource.kt
+++ b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosDataSource.kt
@@ -1,39 +1,19 @@
 package org.futo.circles.gallery.feature
 
-import androidx.lifecycle.asFlow
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.update
-import kotlinx.coroutines.withContext
-import org.futo.circles.core.extensions.getKnownUsersFlow
+import org.futo.circles.core.feature.room.RoomListHelper
 import org.futo.circles.core.mapping.toInvitedGalleryListItem
 import org.futo.circles.core.mapping.toJoinedGalleryListItem
 import org.futo.circles.core.model.GALLERY_TYPE
 import org.futo.circles.core.model.GalleryListItem
-import org.futo.circles.core.provider.MatrixSessionProvider
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
-import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
 import javax.inject.Inject
 
-class PhotosDataSource @Inject constructor() {
-
-    private val roomIdsToUnblurProfile = MutableStateFlow<Set<String>>(emptySet())
-
-    fun getGalleriesFlow() = combine(
-        MatrixSessionProvider.getSessionOrThrow().roomService()
-            .getRoomSummariesLive(roomSummaryQueryParams()).asFlow(),
-        MatrixSessionProvider.getSessionOrThrow().getKnownUsersFlow(),
-        roomIdsToUnblurProfile,
-        MatrixSessionProvider.getSessionOrThrow().roomService().getChangeMembershipsLive().asFlow()
-    ) { roomSummaries, knownUsers, roomIdsToUnblur, _ ->
-        withContext(Dispatchers.IO) {
-            filterGalleries(roomSummaries, knownUsers.map { it.userId }.toSet(), roomIdsToUnblur)
-        }
-    }.distinctUntilChanged()
+class PhotosDataSource @Inject constructor(
+    private val roomListHelper: RoomListHelper
+) {
 
+    fun getGalleriesFlow() = roomListHelper.getRoomsFlow(::filterGalleries)
 
     private fun filterGalleries(
         list: List<RoomSummary>,
@@ -46,7 +26,7 @@ class PhotosDataSource @Inject constructor() {
         return mutableListOf<GalleryListItem>().apply {
             addAll(invites.map {
                 it.toInvitedGalleryListItem(
-                    shouldBlurIconFor(it.roomId, it.inviterId, knownUsersIds, roomIdsToUnblur)
+                    roomListHelper.shouldBlurIconFor(it, knownUsersIds, roomIdsToUnblur)
                 )
             })
             addAll(joined.map { it.toJoinedGalleryListItem() })
@@ -64,7 +44,5 @@ class PhotosDataSource @Inject constructor() {
         return !isKnownUser && !isRoomUnbluredByUser
     }
 
-    fun unblurProfileImageFor(id: String) {
-        roomIdsToUnblurProfile.update { set -> set.toMutableSet().apply { add(id) } }
-    }
+    fun unblurProfileImageFor(id: String) = roomListHelper.unblurProfileImageFor(id)
 }
diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/backup/MediaBackupDataSource.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/backup/MediaBackupDataSource.kt
index 0e6a4f44448b935e0632d76bdc46129aae042dea..8e1b30c6f6363384c500c43fea25a0c3cce5cfd3 100644
--- a/gallery/src/main/java/org/futo/circles/gallery/feature/backup/MediaBackupDataSource.kt
+++ b/gallery/src/main/java/org/futo/circles/gallery/feature/backup/MediaBackupDataSource.kt
@@ -4,7 +4,7 @@ import android.content.Context
 import android.database.Cursor
 import android.provider.MediaStore
 import dagger.hilt.android.qualifiers.ApplicationContext
-import org.futo.circles.core.feature.room.CreateRoomDataSource
+import org.futo.circles.core.feature.room.create.CreateRoomDataSource
 import org.futo.circles.core.feature.timeline.post.SendMessageDataSource
 import org.futo.circles.core.model.Gallery
 import org.futo.circles.core.model.MediaType