diff --git a/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/i18n/I18n.kt b/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/i18n/I18n.kt
index 96952ab0b32ce644cf949c3b7aff42de86158346..955a82a43ccb163ce45ad46550326168c913b110 100644
--- a/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/i18n/I18n.kt
+++ b/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/i18n/I18n.kt
@@ -152,6 +152,16 @@ abstract class I18n(languages: Languages, settings: MatrixMessengerSettingsHolde
         DE - "Nachricht wurde von $username gelöscht"
     }
 
+    fun eventMessageRedactedByMe() = translate {
+        EN - "You deleted this message"
+        DE - "Sie haben diese Nachricht gelöscht"
+    }
+
+    fun eventMessageRedactedByUnknown() = translate {
+        EN - "This message has been deleted"
+        DE - "Diese Nachricht ist gelöscht worden"
+    }
+
     fun eventRoomCreated(username: String, groupOrChat: String) = translate {
         EN - "$username has created $groupOrChat"
         DE - "$username hat $groupOrChat erstellt"
diff --git a/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/RedactedMessageViewModel.kt b/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/RedactedMessageViewModel.kt
index 8a9644f0c7ebd2dd757c8aa0129a774223c465e0..b72859b149c2197d5c303be369d9857b337b8efc 100644
--- a/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/RedactedMessageViewModel.kt
+++ b/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/RedactedMessageViewModel.kt
@@ -6,10 +6,13 @@ import de.connect2x.trixnity.messenger.viewmodel.i18n
 import de.connect2x.trixnity.messenger.viewmodel.util.formatDate
 import de.connect2x.trixnity.messenger.viewmodel.util.formatTime
 import de.connect2x.trixnity.messenger.viewmodel.util.timezone
+import io.github.oshai.kotlinlogging.KotlinLogging
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.datetime.Instant
@@ -17,9 +20,13 @@ import kotlinx.datetime.TimeZone
 import kotlinx.datetime.toLocalDateTime
 import net.folivo.trixnity.client.store.TimelineEvent
 import net.folivo.trixnity.client.store.unsigned
+import net.folivo.trixnity.client.user
+import net.folivo.trixnity.core.model.RoomId
+import net.folivo.trixnity.core.model.UserId
 import net.folivo.trixnity.core.model.events.RedactedEventContent
 import net.folivo.trixnity.core.model.events.originTimestampOrNull
 
+private val log = KotlinLogging.logger { }
 
 interface RedactedMessageViewModelFactory {
     fun create(
@@ -35,6 +42,8 @@ interface RedactedMessageViewModelFactory {
         showSender: Flow<Boolean>,
         sender: Flow<UserInfoElement>,
         invitation: Flow<String?>,
+        selectedRoomId: RoomId,
+        redactedBy: UserId?
     ): RedactedMessageViewModel {
         return RedactedMessageViewModelImpl(
             viewModelContext,
@@ -46,9 +55,11 @@ interface RedactedMessageViewModelFactory {
             isByMe,
             showChatBubbleEdge,
             showBigGap,
+            selectedRoomId,
             showSender,
             sender,
             invitation,
+            redactedBy
         )
     }
 
@@ -70,9 +81,11 @@ open class RedactedMessageViewModelImpl(
     override val isByMe: Boolean,
     override val showChatBubbleEdge: Boolean,
     override val showBigGap: Boolean,
+    selectedRoomId: RoomId,
     showSender: Flow<Boolean>,
     sender: Flow<UserInfoElement>,
     invitation: Flow<String?>,
+    redactedBy: UserId?,
 ) : RedactedMessageViewModel, MatrixClientViewModelContext by viewModelContext {
     override val invitation: StateFlow<String?> =
         invitation.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
@@ -81,18 +94,24 @@ open class RedactedMessageViewModelImpl(
     override val showSender: StateFlow<Boolean> =
         showSender.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), true)
 
-    override val formattedMessage = sender.map { userInfo ->
-        i18n.eventMessageRedacted(userInfo.name)
-    }.stateIn(
-        coroutineScope,
-        SharingStarted.WhileSubscribed(),
-        i18n.eventMessageRedacted(i18n.commonUnknown())
-    )
+    override val formattedMessage = when (redactedBy) {
+            null -> MutableStateFlow(i18n.eventMessageRedactedByUnknown())
+            matrixClient.userId -> MutableStateFlow(i18n.eventMessageRedactedByMe())
+            else -> matrixClient.user.getById(selectedRoomId, redactedBy).map {
+                i18n.eventMessageRedacted(it?.name ?: redactedBy.full)
+            }.stateIn(
+                coroutineScope,
+                SharingStarted.WhileSubscribed(),
+                i18n.eventMessageRedacted(i18n.commonUnknown())
+            )
+        }
 
-    override val redactedAtDateTime: String? = timelineEvent?.unsigned?.redactedBecause?.originTimestampOrNull?.let {
-        val localDateTime = Instant.fromEpochMilliseconds(it).toLocalDateTime(TimeZone.of(timezone()))
-        "${formatDate(localDateTime)}, ${formatTime(localDateTime)}"
-    }
+
+    override val redactedAtDateTime: String? =
+        timelineEvent?.unsigned?.redactedBecause?.originTimestampOrNull?.let {
+            val localDateTime = Instant.fromEpochMilliseconds(it).toLocalDateTime(TimeZone.of(timezone()))
+            "${formatDate(localDateTime)}, ${formatTime(localDateTime)}"
+        }
 }
 
 class PreviewRedactedMessageViewModel() : RedactedMessageViewModel {
@@ -107,4 +126,4 @@ class PreviewRedactedMessageViewModel() : RedactedMessageViewModel {
     override val formattedDate: String = "23.12.21"
     override val showDateAbove: Boolean = false
     override val redactedAtDateTime: String = "25.12.21, 13:18"
-}
\ No newline at end of file
+}
diff --git a/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/TimelineElementHolderViewModel.kt b/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/TimelineElementHolderViewModel.kt
index 03338a8ae86bfad2fee5316760a65b20ba693bcf..263ac352806b76ed113f80a64637bdf2b526749f 100644
--- a/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/TimelineElementHolderViewModel.kt
+++ b/trixnity-messenger/src/commonMain/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/TimelineElementHolderViewModel.kt
@@ -47,6 +47,7 @@ import net.folivo.trixnity.client.store.isReplaced
 import net.folivo.trixnity.client.store.isReplacing
 import net.folivo.trixnity.client.store.membership
 import net.folivo.trixnity.client.store.roomId
+import net.folivo.trixnity.client.store.unsigned
 import net.folivo.trixnity.client.user
 import net.folivo.trixnity.client.user.canSendEvent
 import net.folivo.trixnity.core.model.EventId
@@ -503,6 +504,8 @@ open class TimelineElementHolderViewModelImpl(
 
             is RedactedEventContent -> {
                 log.trace { "Create redacted text message view model: ${event.id}" }
+                val redactedBy = timelineEvent.unsigned?.redactedBecause?.sender
+
                 get<RedactedMessageViewModelFactory>().create(
                     viewModelContext = this,
                     timelineEvent = timelineEvent,
@@ -513,9 +516,11 @@ open class TimelineElementHolderViewModelImpl(
                     formattedDate = formatDate(receivedDateTime),
                     showDateAbove = showDateAbove,
                     isByMe = isByMe,
+                    selectedRoomId = selectedRoomId,
                     showChatBubbleEdge = showChatBubbleEdge,
                     showBigGap = showChatBubbleEdge,
                     invitation = invitation,
+                    redactedBy = redactedBy
                 )
             }
 
diff --git a/trixnity-messenger/src/commonTest/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/RedactedMessageViewModelTest.kt b/trixnity-messenger/src/commonTest/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/RedactedMessageViewModelTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..617e49fd36bf00bf52400609d5e9c417f6357af4
--- /dev/null
+++ b/trixnity-messenger/src/commonTest/kotlin/de/connect2x/trixnity/messenger/viewmodel/room/timeline/elements/RedactedMessageViewModelTest.kt
@@ -0,0 +1,215 @@
+package de.connect2x.trixnity.messenger.viewmodel.room.timeline.elements
+
+import com.arkivanov.decompose.DefaultComponentContext
+import com.arkivanov.essenty.lifecycle.LifecycleRegistry
+import de.connect2x.trixnity.messenger.viewmodel.MatrixClientViewModelContextImpl
+import de.connect2x.trixnity.messenger.viewmodel.UserInfoElement
+import de.connect2x.trixnity.messenger.viewmodel.util.cancelNeverEndingCoroutines
+import de.connect2x.trixnity.messenger.viewmodel.util.createTestDefaultTrixnityMessengerModules
+import io.kotest.core.spec.style.ShouldSpec
+import io.kotest.core.test.testCoroutineScheduler
+import io.kotest.matchers.shouldBe
+import isTimelineEvent
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.currentCoroutineContext
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.setMain
+import net.folivo.trixnity.client.MatrixClient
+import net.folivo.trixnity.client.room.RoomService
+import net.folivo.trixnity.client.store.RoomUser
+import net.folivo.trixnity.client.store.TimelineEvent
+import net.folivo.trixnity.client.store.eventId
+import net.folivo.trixnity.client.user.UserService
+import net.folivo.trixnity.core.model.EventId
+import net.folivo.trixnity.core.model.RoomId
+import net.folivo.trixnity.core.model.UserId
+import net.folivo.trixnity.core.model.events.ClientEvent
+import net.folivo.trixnity.core.model.events.MessageEventContent
+import net.folivo.trixnity.core.model.events.RedactedEventContent
+import net.folivo.trixnity.core.model.events.RoomEventContent
+import net.folivo.trixnity.core.model.events.m.room.MemberEventContent
+import net.folivo.trixnity.core.model.events.m.room.Membership
+import org.kodein.mock.Mock
+import org.kodein.mock.Mocker
+import org.koin.dsl.koinApplication
+import org.koin.dsl.module
+import kotlin.coroutines.CoroutineContext
+
+@OptIn(ExperimentalStdlibApi::class)
+class RedactedMessageViewModelTest : ShouldSpec() {
+
+    private val roomId = RoomId("room1", "localhost")
+    private val ourUserId = UserId("bob", "localhost")
+    private val me = UserId("jonas", "localhost")
+    val eventId = EventId("0")
+
+    val mocker = Mocker()
+
+    @Mock
+    lateinit var matrixClientMock: MatrixClient
+
+    @Mock
+    lateinit var roomServiceMock: RoomService
+
+    @Mock
+    lateinit var userServiceMock: UserService
+
+    init {
+        coroutineTestScope = true
+
+        beforeTest {
+            mocker.reset()
+            injectMocks(mocker)
+            with(mocker) {
+                every { matrixClientMock.di } returns koinApplication {
+                    modules(
+                        module {
+                            single { roomServiceMock }
+                            single { userServiceMock }
+                        }
+                    )
+                }.koin
+
+                every { matrixClientMock.userId } returns me
+                mocker.every { userServiceMock.getById(roomId, ourUserId) } returns MutableStateFlow(
+                    roomUser(me, "TestUser")
+                )
+            }
+        }
+
+
+        should("format generic message when redactedBy is null") {
+            val timelineEventFlow = timelineEvent(messageEvent(RedactedEventContent("somethig"), sender = ourUserId))
+            val cut = redactedMessageViewModel(
+                timelineEvent = timelineEventFlow,
+                coroutineContext = coroutineContext,
+            )
+            val subscriberJob = launch { cut.formattedMessage.collect {} }
+            testCoroutineScheduler.advanceUntilIdle()
+
+            cut.formattedMessage.value shouldBe "This message has been deleted"
+
+            subscriberJob.cancel()
+            cancelNeverEndingCoroutines()
+        }
+
+        should("append 'message deleted by me' when redactedBy ID matches current userId") {
+            val timelineEventFlow = timelineEvent(messageEvent(RedactedEventContent("somethig"), sender = me))
+            val cut = redactedMessageViewModel(
+                timelineEvent = timelineEventFlow,
+                coroutineContext = coroutineContext,
+                redactedBy = me
+            )
+            val subscriberJob = launch { cut.formattedMessage.collect {} }
+            testCoroutineScheduler.advanceUntilIdle()
+
+            cut.formattedMessage.value shouldBe "You deleted this message"
+
+            subscriberJob.cancel()
+            cancelNeverEndingCoroutines()
+        }
+
+
+        should("append 'redacted by other user' when redactedBy does not match current userId") {
+            val timelineEventFlow = timelineEvent(messageEvent(RedactedEventContent("somethig"), sender = ourUserId))
+
+            val cut = redactedMessageViewModel(
+                timelineEvent = timelineEventFlow,
+                coroutineContext = coroutineContext,
+                redactedBy = ourUserId
+            )
+            val subscriberJob = launch { cut.formattedMessage.collect {} }
+            testCoroutineScheduler.advanceUntilIdle()
+
+            cut.formattedMessage.value shouldBe "message has been deleted by TestUser"
+
+            subscriberJob.cancel()
+            cancelNeverEndingCoroutines()
+        }
+    }
+
+
+    @OptIn(ExperimentalStdlibApi::class)
+    private suspend fun redactedMessageViewModel(
+        timelineEvent: TimelineEvent,
+        redactedBy: UserId? = null,
+        coroutineContext: CoroutineContext,
+    ): RedactedMessageViewModelImpl {
+        Dispatchers.setMain(checkNotNull(currentCoroutineContext()[CoroutineDispatcher]))
+        val di = koinApplication {
+            modules(
+                createTestDefaultTrixnityMessengerModules(mapOf(UserId("test", "server") to matrixClientMock))
+            )
+        }.koin
+        return RedactedMessageViewModelImpl(
+            viewModelContext = MatrixClientViewModelContextImpl(
+                componentContext = DefaultComponentContext(LifecycleRegistry()),
+                di = di,
+                userId = UserId("test", "server"),
+                coroutineContext = coroutineContext
+            ),
+            timelineEvent = timelineEvent,
+            content = timelineEvent.event.content as RedactedEventContent,
+            formattedDate = "",
+            showDateAbove = false,
+            invitation = MutableStateFlow(""),
+            sender = MutableStateFlow(UserInfoElement("Bob")),
+            formattedTime = "",
+            isByMe = false,
+            redactedBy = redactedBy,
+            selectedRoomId = roomId,
+            showBigGap = false,
+            showSender = MutableStateFlow(false),
+            showChatBubbleEdge = false
+
+        )
+    }
+
+    private fun timelineEvent(
+        event: ClientEvent.RoomEvent<*>,
+        content: Result<RoomEventContent>? = null,
+        previousEvent: TimelineEvent? = null
+    ): TimelineEvent {
+        val timelineEvent = TimelineEvent(
+            event = event,
+            content = content,
+            previousEventId = previousEvent?.eventId,
+            nextEventId = null,
+            gap = null,
+        )
+
+        mocker.every {
+            roomServiceMock.getPreviousTimelineEvent(
+                isTimelineEvent(timelineEvent),
+                isAny(),
+            )
+        } returns
+                previousEvent?.let { MutableStateFlow(it) }
+
+        return timelineEvent
+    }
+
+    private fun messageEvent(content: MessageEventContent, sender: UserId) = ClientEvent.RoomEvent.MessageEvent(
+        content,
+        id = EventId(""),
+        sender = sender,
+        roomId = roomId,
+        originTimestamp = 0L,
+    )
+
+    private fun roomUser(userId: UserId, name: String) = RoomUser(
+        roomId,
+        userId,
+        name,
+        event = ClientEvent.RoomEvent.StateEvent(
+            MemberEventContent(membership = Membership.JOIN),
+            EventId(""),
+            UserId(""),
+            RoomId(""),
+            0L,
+            stateKey = ""
+        )
+    )
+}