From 9d0be9748ebdd731434e7cfc814b13e44f4a3fcb Mon Sep 17 00:00:00 2001 From: Sergei Bakhtiarov Date: Thu, 30 Jul 2026 16:14:51 +0200 Subject: [PATCH] feat: support for new adminless groups feature events (WPB-25284) --- .../mapper/SystemMessageContentMapper.kt | 3 +++ .../messages/item/SystemMessageItem.kt | 22 +++++++++++++++++++ .../preview/PreviewSystemMessageItem.kt | 15 +++++++++++++ .../ui/home/conversations/model/UIMessage.kt | 5 +++++ app/src/main/res/values/strings.xml | 1 + .../mapper/SystemMessageContentMapperTest.kt | 13 +++++++++++ .../wire/android/util/DateAndTimeParsers.kt | 16 ++++++++++++++ .../com/wire/android/util/SupportPage.kt | 3 ++- .../ui-common/src/main/res/values/strings.xml | 1 + kalium | 2 +- 10 files changed, 79 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt index 6c51cd615d9..2486f272d37 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt @@ -81,6 +81,9 @@ class SystemMessageContentMapper @Inject constructor( content, members ) + + is MessageContent.AdminlessDeleteReminder -> + UIMessageContent.SystemMessage.AdminlessDeleteReminder(content.deletionScheduledFor) } private fun mapConversationConversationAppsAccessChanged( diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt index 196c555f110..45abc55c3e0 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt @@ -19,6 +19,7 @@ package com.wire.android.ui.home.conversations.messages.item +import android.text.format.DateFormat import androidx.annotation.DrawableRes import androidx.annotation.PluralsRes import androidx.annotation.StringRes @@ -63,6 +64,7 @@ import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography import com.wire.android.util.CustomTabsHelper import com.wire.android.util.SupportPage +import com.wire.android.util.formatMonthDayShortTime import com.wire.android.util.supportUrlResource import com.wire.android.util.ui.MarkdownTextStyle import com.wire.android.util.ui.UIText @@ -610,6 +612,26 @@ private fun SystemMessage.buildContent(isWireCellsEnabled: Boolean) = when (this } } } + + is SystemMessage.AdminlessDeleteReminder -> buildContent( + iconResId = commonR.drawable.ic_info, + iconTintColor = MaterialTheme.wireColorScheme.error, + learnMorePage = SupportPage.ADMINLESS_GROUP_DELETE, + ) { + val is24Hour = DateFormat.is24HourFormat(LocalContext.current) + val markdownTextStyle = DefaultMarkdownTextStyle.copy( + normalColor = MaterialTheme.wireColorScheme.error, + boldColor = MaterialTheme.wireColorScheme.error + ) + buildAnnotatedString { + append( + stringResource( + id = R.string.label_system_message_adminless_delete_reminder, + formatArgs = arrayOf(deletionScheduledFor.formatMonthDayShortTime(is24Hour)) + ).toMarkdownAnnotatedString(markdownTextStyle) + ) + } + } } private fun AnnotatedString.Builder.appendVerticalSpace() = withStyle(ParagraphStyle()) { append(" ") } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/preview/PreviewSystemMessageItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/preview/PreviewSystemMessageItem.kt index 389ac48d5cd..76626329d67 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/preview/PreviewSystemMessageItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/preview/PreviewSystemMessageItem.kt @@ -29,6 +29,7 @@ import com.wire.android.util.ui.PreviewMultipleThemes import com.wire.android.util.ui.UIText import com.wire.android.util.ui.toUIText import com.wire.kalium.logic.data.conversation.Conversation +import kotlinx.datetime.Instant @PreviewMultipleThemes @Composable @@ -486,3 +487,17 @@ fun PreviewSystemMessageConversationMessageAppsAccessEnabled() { ) } } + +@PreviewMultipleThemes +@Composable +fun PreviewSystemMessageAdminlessDeleteReminder() { + WireTheme { + SystemMessageItem( + message = mockMessageWithKnock.copy( + messageContent = UIMessageContent.SystemMessage.AdminlessDeleteReminder( + deletionScheduledFor = Instant.parse("2026-04-23T12:00:00Z") + ) + ) + ) + } +} diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt index f60ea33f4b5..09df03f8b10 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt @@ -650,6 +650,11 @@ sealed interface UIMessageContent { val isAuthorSelfUser: Boolean = false, val isAccessEnabled: Boolean ) : SystemMessage + + @Serializable + data class AdminlessDeleteReminder( + val deletionScheduledFor: Instant + ) : SystemMessage } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 41f0727a4e1..a8d49cf15a9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1903,6 +1903,7 @@ In group conversations, the group admin can overwrite this setting. %1$s disabled **apps** for this conversation Added apps have access to the content of this conversation. You were promoted to group admin + This group will be automatically deleted on %1$s, as there are no eligible group admins. More information about this backend diff --git a/app/src/test/kotlin/com/wire/android/mapper/SystemMessageContentMapperTest.kt b/app/src/test/kotlin/com/wire/android/mapper/SystemMessageContentMapperTest.kt index 6911ac9a13b..1eb8e62dced 100644 --- a/app/src/test/kotlin/com/wire/android/mapper/SystemMessageContentMapperTest.kt +++ b/app/src/test/kotlin/com/wire/android/mapper/SystemMessageContentMapperTest.kt @@ -44,6 +44,7 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import java.util.Locale +import kotlinx.datetime.Instant @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(CoroutineTestExtension::class) @@ -75,6 +76,18 @@ class SystemMessageContentMapperTest { assertTrue(uiContent is SystemMessage.ConversationMessageTimerDeactivated) } + @Test + fun givenAdminlessDeleteReminder_whenMappingToSystemMessage_thenScheduledDeletionIsPreserved() = runTest { + val (_, mapper) = Arrangement().arrange() + val scheduledDeletion = Instant.parse("2026-04-23T12:00:00Z") + val content = MessageContent.AdminlessDeleteReminder(scheduledDeletion) + + val uiContent = mapper.mapMessage(TestMessage.SYSTEM_MESSAGE.copy(content = content), emptyList()) + + assertIs(uiContent!!) + assertEquals(scheduledDeletion, uiContent.deletionScheduledFor) + } + @Test fun givenMemberDetails_whenMappingToSystemMessageMemberName_thenCorrectValuesShouldBeReturned() = runTest { // Given diff --git a/core/ui-common/src/main/kotlin/com/wire/android/util/DateAndTimeParsers.kt b/core/ui-common/src/main/kotlin/com/wire/android/util/DateAndTimeParsers.kt index 26c4db066d2..1936f71b2e3 100644 --- a/core/ui-common/src/main/kotlin/com/wire/android/util/DateAndTimeParsers.kt +++ b/core/ui-common/src/main/kotlin/com/wire/android/util/DateAndTimeParsers.kt @@ -17,9 +17,11 @@ */ package com.wire.android.util +import android.text.format.DateFormat import androidx.compose.runtime.Stable import kotlinx.datetime.Instant import kotlinx.datetime.toJavaInstant +import java.text.SimpleDateFormat import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.format.FormatStyle @@ -39,6 +41,10 @@ fun Instant.formatMediumDateTime(): String = DateAndTimeParsers.formatMediumDate @Stable fun Instant.formatFullDateShortTime(): String = DateAndTimeParsers.formatFullDateShortTime(this) +@Stable +fun Instant.formatMonthDayShortTime(is24Hour: Boolean): String = + DateAndTimeParsers.formatMonthDayShortTime(this, is24Hour) + @Stable fun Instant.uiMessageDateTime(): String = DateAndTimeParsers.uiMessageDateTime(this) @@ -107,6 +113,16 @@ class DateAndTimeParsers private constructor() { fun formatFullDateShortTime(instant: Instant): String = fullDateShortTimeFormatter.format(instant.toJavaInstant()) + fun formatMonthDayShortTime(instant: Instant, is24Hour: Boolean): String { + val locale = Locale.getDefault() + val date = Date.from(instant.toJavaInstant()) + val datePattern = DateFormat.getBestDateTimePattern(locale, "MMMMd") + val timePattern = DateFormat.getBestDateTimePattern(locale, if (is24Hour) "Hm" else "hm") + val formattedDate = SimpleDateFormat(datePattern, locale).format(date) + val formattedTime = SimpleDateFormat(timePattern, locale).format(date) + return "$formattedDate, $formattedTime" + } + fun cellTimeFormat(instant: Instant): String { val timeFormatter = java.text.DateFormat.getTimeInstance( java.text.DateFormat.SHORT, diff --git a/core/ui-common/src/main/kotlin/com/wire/android/util/SupportPage.kt b/core/ui-common/src/main/kotlin/com/wire/android/util/SupportPage.kt index db5852fb98c..daba36cb668 100644 --- a/core/ui-common/src/main/kotlin/com/wire/android/util/SupportPage.kt +++ b/core/ui-common/src/main/kotlin/com/wire/android/util/SupportPage.kt @@ -52,5 +52,6 @@ enum class SupportPage( LEGAL_HOLD("legal_hold", R.string.url_legal_hold_learn_more), SEARCH("search", R.string.url_learn_about_search), CELLS_CONVERSATION("cells_conversation", R.string.empty_screen_learn_more_link_conversation), - CELLS_ALL_FILES("cells_all_files", R.string.empty_screen_learn_more_link_all_files_screen) + CELLS_ALL_FILES("cells_all_files", R.string.empty_screen_learn_more_link_all_files_screen), + ADMINLESS_GROUP_DELETE("adminless_group_delete", R.string.url_system_message_adminless_group_delete), } diff --git a/core/ui-common/src/main/res/values/strings.xml b/core/ui-common/src/main/res/values/strings.xml index f062c97de8b..92786b6cf99 100644 --- a/core/ui-common/src/main/res/values/strings.xml +++ b/core/ui-common/src/main/res/values/strings.xml @@ -59,6 +59,7 @@ https://support.wire.com/hc/articles/360002018278 https://support.wire.com/hc/articles/32207745256221 https://support.wire.com/hc/articles/32207800433309 + https://support.wire.com/hc/en-us/articles/37518608388125-Prevent-adminless-groups Learn more about legal hold Connecting not possible You can not connect to this user due to legal hold. diff --git a/kalium b/kalium index e68500602d0..7056798774e 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit e68500602d0914e1263045aadeb69a414517f896 +Subproject commit 7056798774ebcd741dd5a605d108f9bb26bf52b4