android, desktop: support chat item ttl per chat

This commit is contained in:
Diogo
2025-01-17 23:26:49 +00:00
committed by Evgeny Poberezkin
parent 7e864f9178
commit a294f58cc7
7 changed files with 215 additions and 35 deletions
@@ -1484,6 +1484,7 @@ data class Contact(
val contactGroupMemberId: Long? = null,
val contactGrpInvSent: Boolean,
val chatTags: List<Long>,
val chatItemTTL: Long?,
override val chatDeleted: Boolean,
val uiThemes: ThemeModeOverrides? = null,
): SomeChat, NamedChat {
@@ -1567,7 +1568,8 @@ data class Contact(
contactGrpInvSent = false,
chatDeleted = false,
uiThemes = null,
chatTags = emptyList()
chatTags = emptyList(),
chatItemTTL = null,
)
}
}
@@ -1726,6 +1728,7 @@ data class GroupInfo (
val chatTs: Instant?,
val uiThemes: ThemeModeOverrides? = null,
val chatTags: List<Long>,
val chatItemTTL: Long?,
override val localAlias: String,
): SomeChat, NamedChat {
override val chatType get() = ChatType.Group
@@ -1774,7 +1777,8 @@ data class GroupInfo (
chatTs = Clock.System.now(),
uiThemes = null,
chatTags = emptyList(),
localAlias = ""
localAlias = "",
chatItemTTL = null
)
}
}
@@ -4186,32 +4190,49 @@ enum class SwitchPhase {
@SerialName("completed") Completed
}
sealed class ChatItemTTL: Comparable<ChatItemTTL?> {
sealed class ChatItemTTL: Comparable<ChatItemTTL> {
object Day: ChatItemTTL()
object Week: ChatItemTTL()
object Month: ChatItemTTL()
object Year: ChatItemTTL()
data class Seconds(val secs: Long): ChatItemTTL()
object None: ChatItemTTL()
override fun compareTo(other: ChatItemTTL?): Int = (seconds ?: Long.MAX_VALUE).compareTo(other?.seconds ?: Long.MAX_VALUE)
override fun compareTo(other: ChatItemTTL): Int =
(seconds.takeIf { it != 0L } ?: Long.MAX_VALUE)
.compareTo(other.seconds.takeIf { it != 0L } ?: Long.MAX_VALUE)
val seconds: Long?
val seconds: Long
get() =
when (this) {
is None -> null
is None -> 0
is Day -> 86400L
is Week -> 7 * 86400L
is Month -> 30 * 86400L
is Year -> 365 * 86400L
is Seconds -> secs
}
val text: String
get() = when(this) {
is None -> generalGetString(MR.strings.chat_item_ttl_none)
is Day -> generalGetString(MR.strings.chat_item_ttl_day)
is Week -> generalGetString(MR.strings.chat_item_ttl_week)
is Month -> generalGetString(MR.strings.chat_item_ttl_month)
is Year -> generalGetString(MR.strings.chat_item_ttl_year)
is Seconds -> String.format(generalGetString(MR.strings.chat_item_ttl_seconds), secs)
}
val doesNotExpire: Boolean get() = this is None
companion object {
fun fromSeconds(seconds: Long?): ChatItemTTL =
fun fromSeconds(seconds: Long): ChatItemTTL =
when (seconds) {
null -> None
0L -> None
86400L -> Day
7 * 86400L -> Week
30 * 86400L -> Month
365 * 86400L -> Year
else -> Seconds(seconds)
}
}
@@ -1182,7 +1182,13 @@ object ChatController {
suspend fun getChatItemTTL(rh: Long?): ChatItemTTL {
val userId = currentUserId("getChatItemTTL")
val r = sendCmd(rh, CC.APIGetChatItemTTL(userId))
if (r is CR.ChatItemTTL) return ChatItemTTL.fromSeconds(r.chatItemTTL)
if (r is CR.ChatItemTTL) {
if (r.chatItemTTL != null) {
return ChatItemTTL.fromSeconds(r.chatItemTTL)
} else {
throw Exception("chatItemTTLResponse: invalid ttl")
}
}
throw Exception("failed to get chat item TTL: ${r.responseType} ${r.details}")
}
@@ -1193,6 +1199,13 @@ object ChatController {
throw Exception("failed to set chat item TTL: ${r.responseType} ${r.details}")
}
suspend fun setChatTTL(rh: Long?, chatType: ChatType, id: Long, chatItemTTL: ChatItemTTL?) {
val userId = currentUserId("setChatTTL")
val r = sendCmd(rh, CC.APISetChatTTL(userId, chatType, id, chatItemTTL?.seconds))
if (r is CR.CmdOk) return
throw Exception("failed to set chat TTL: ${r.responseType} ${r.details}")
}
suspend fun apiSetNetworkConfig(cfg: NetCfg, showAlertOnError: Boolean = true, ctrl: ChatCtrl? = null): Boolean {
val r = sendCmd(null, CC.APISetNetworkConfig(cfg), ctrl)
return when (r) {
@@ -3383,8 +3396,9 @@ sealed class CC {
class ApiGetUsageConditions(): CC()
class ApiSetConditionsNotified(val conditionsId: Long): CC()
class ApiAcceptConditions(val conditionsId: Long, val operatorIds: List<Long>): CC()
class APISetChatItemTTL(val userId: Long, val seconds: Long?): CC()
class APISetChatItemTTL(val userId: Long, val seconds: Long): CC()
class APIGetChatItemTTL(val userId: Long): CC()
class APISetChatTTL(val userId: Long, val chatType: ChatType, val id: Long, val seconds: Long?): CC()
class APISetNetworkConfig(val networkConfig: NetCfg): CC()
class APIGetNetworkConfig: CC()
class APISetNetworkInfo(val networkInfo: UserNetworkInfo): CC()
@@ -3567,6 +3581,7 @@ sealed class CC {
is ApiAcceptConditions -> "/_accept_conditions ${conditionsId} ${operatorIds.joinToString(",")}"
is APISetChatItemTTL -> "/_ttl $userId ${chatItemTTLStr(seconds)}"
is APIGetChatItemTTL -> "/_ttl $userId"
is APISetChatTTL -> "/_ttl $userId ${chatRef(chatType, id)} ${chatItemTTLStr(seconds)}"
is APISetNetworkConfig -> "/_network ${json.encodeToString(networkConfig)}"
is APIGetNetworkConfig -> "/network"
is APISetNetworkInfo -> "/_network info ${json.encodeToString(networkInfo)}"
@@ -3727,6 +3742,7 @@ sealed class CC {
is ApiAcceptConditions -> "apiAcceptConditions"
is APISetChatItemTTL -> "apiSetChatItemTTL"
is APIGetChatItemTTL -> "apiGetChatItemTTL"
is APISetChatTTL -> "apiSetChatTTL"
is APISetNetworkConfig -> "apiSetNetworkConfig"
is APIGetNetworkConfig -> "apiGetNetworkConfig"
is APISetNetworkInfo -> "apiSetNetworkInfo"
@@ -3812,7 +3828,7 @@ sealed class CC {
data class ItemRange(val from: Long, val to: Long)
fun chatItemTTLStr(seconds: Long?): String {
if (seconds == null) return "none"
if (seconds == null) return "default"
return seconds.toString()
}
@@ -16,8 +16,10 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.*
@@ -36,12 +38,13 @@ import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.model.ChatModel.withChats
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.*
import chat.simplex.common.platform.*
import chat.simplex.common.views.chat.group.ProgressIndicator
import chat.simplex.common.views.chatlist.updateChatSettings
import chat.simplex.common.views.database.*
import chat.simplex.common.views.newchat.*
import chat.simplex.res.MR
import kotlinx.coroutines.delay
@@ -74,6 +77,9 @@ fun ChatInfoView(
}
val chatRh = chat.remoteHostId
val sendReceipts = remember(contact.id) { mutableStateOf(SendReceipts.fromBool(contact.chatSettings.sendRcpts, currentUser.sendRcptsContacts)) }
val chatItemTTL = remember(contact.id) { mutableStateOf(if (contact.chatItemTTL != null) ChatItemTTL.fromSeconds(contact.chatItemTTL) else null) }
val progressIndicator = rememberSaveable(contact.id) { mutableStateOf(false) }
ChatInfoLayout(
chat,
contact,
@@ -84,6 +90,13 @@ fun ChatInfoView(
updateChatSettings(chat.remoteHostId, chat.chatInfo, chatSettings, chatModel)
sendReceipts.value = sendRcpts
},
chatItemTTL = chatItemTTL,
setChatItemTTL = {
val previousChatTTL = chatItemTTL.value
chatItemTTL.value = it
setChatTTLAlert(chatModel, chat.remoteHostId, chat.chatInfo, chatItemTTL, previousChatTTL, progressIndicator)
},
connStats = connStats,
contactNetworkStatus.value,
customUserProfile,
@@ -173,8 +186,13 @@ fun ChatInfoView(
}
},
close = close,
onSearchClicked = onSearchClicked
onSearchClicked = onSearchClicked,
enabled = remember { derivedStateOf { !progressIndicator.value } }
)
if (progressIndicator.value) {
ProgressIndicator()
}
}
}
@@ -504,6 +522,8 @@ fun ChatInfoLayout(
currentUser: User,
sendReceipts: State<SendReceipts>,
setSendReceipts: (SendReceipts) -> Unit,
chatItemTTL: MutableState<ChatItemTTL?>,
setChatItemTTL: (ChatItemTTL?) -> Unit,
connStats: MutableState<ConnectionStats?>,
contactNetworkStatus: NetworkStatus,
customUserProfile: Profile?,
@@ -520,7 +540,8 @@ fun ChatInfoLayout(
syncContactConnectionForce: () -> Unit,
verifyClicked: () -> Unit,
close: () -> Unit,
onSearchClicked: () -> Unit
onSearchClicked: () -> Unit,
enabled: State<Boolean>
) {
val cStats = connStats.value
val scrollState = rememberScrollState()
@@ -528,7 +549,7 @@ fun ChatInfoLayout(
KeyChangeEffect(chat.id) {
scope.launch { scrollState.scrollTo(0) }
}
ColumnWithScrollBar {
ColumnWithScrollBar(Modifier.alpha(if (enabled.value) 1f else 0.6f)) {
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
@@ -583,6 +604,13 @@ fun ChatInfoLayout(
// } else if (developerTools) {
// SynchronizeConnectionButtonForce(syncContactConnectionForce)
// }
TtlOptions(
chatItemTTL,
enabled = remember { mutableStateOf(true) },
onSelected = setChatItemTTL,
default = chatModel.chatItemTTL,
icon = painterResource(MR.images.ic_delete),
)
}
WallpaperButton {
@@ -1308,6 +1336,66 @@ fun queueInfoText(info: Pair<RcvMsgInfo?, ServerQueueInfo>): String {
return generalGetString(MR.strings.message_queue_info_server_info).format(json.encodeToString(qInfo), msgInfo)
}
fun setChatTTLAlert(
m: ChatModel,
rhId: Long?,
chatInfo: ChatInfo,
selectedChatTTL: MutableState<ChatItemTTL?>,
previousChatTTL: ChatItemTTL?,
progressIndicator: MutableState<Boolean>
) {
val disablingExpiration = selectedChatTTL.value?.doesNotExpire == true || (selectedChatTTL.value == null && chatModel.chatItemTTL.value.doesNotExpire)
val changingExpiration = selectedChatTTL.value == null || (previousChatTTL?.doesNotExpire == false)
AlertManager.shared.showAlertDialog(
title = generalGetString(
if (disablingExpiration) {
MR.strings.disable_automatic_deletion_question
} else if (changingExpiration) {
MR.strings.change_automatic_deletion_question
} else MR.strings.enable_automatic_deletion_question),
text = generalGetString(if (disablingExpiration) MR.strings.disable_automatic_deletion_message else MR.strings.change_automatic_chat_deletion_message),
confirmText = generalGetString(if (disablingExpiration) MR.strings.disable_automatic_deletion else MR.strings.delete_messages),
onConfirm = { setChatTTL(m, rhId, chatInfo, selectedChatTTL, progressIndicator, previousChatTTL) },
onDismiss = { selectedChatTTL.value = previousChatTTL },
onDismissRequest = { selectedChatTTL.value = previousChatTTL },
destructive = true,
)
}
private fun setChatTTL(
m: ChatModel,
rhId: Long?,
chatInfo: ChatInfo,
chatTTL: MutableState<ChatItemTTL?>,
progressIndicator: MutableState<Boolean>,
previousChatTTL: ChatItemTTL?
) {
progressIndicator.value = true
withBGApi {
try {
m.controller.setChatTTL(rhId, chatInfo.chatType, chatInfo.apiId, chatTTL.value)
afterSetChatTTL(m, chatInfo, progressIndicator)
} catch (e: Exception) {
chatTTL.value = previousChatTTL
afterSetChatTTL(m, chatInfo, progressIndicator)
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_changing_message_deletion), e.stackTraceToString())
}
}
}
private fun afterSetChatTTL(m: ChatModel, chatInfo: ChatInfo, progressIndicator: MutableState<Boolean>) {
withApi {
try {
// this is using current remote host on purpose - if it changes during update, it will load correct chats
apiLoadMessages(m.remoteHostId(), chatInfo.chatType, chatInfo.apiId, contentTag = null, pagination = ChatPagination.Initial(ChatPagination.INITIAL_COUNT), replaceChat = true)
} catch (e: Exception) {
Log.e(TAG, "apiGetChat error: ${e.message}")
} finally {
progressIndicator.value = false
}
}
}
@Preview
@Composable
fun PreviewChatInfoLayout() {
@@ -1322,6 +1410,8 @@ fun PreviewChatInfoLayout() {
User.sampleData,
sendReceipts = remember { mutableStateOf(SendReceipts.Yes) },
setSendReceipts = {},
chatItemTTL = remember { mutableStateOf(ChatItemTTL.fromSeconds(0)) },
setChatItemTTL = {},
localAlias = "",
connectionCode = "123",
developerTools = false,
@@ -1338,7 +1428,8 @@ fun PreviewChatInfoLayout() {
syncContactConnectionForce = {},
verifyClicked = {},
close = {},
onSearchClicked = {}
onSearchClicked = {},
enabled = remember { mutableStateOf(true) }
)
}
}
@@ -28,7 +28,8 @@ suspend fun apiLoadMessages(
contentTag: MsgContentTag?,
pagination: ChatPagination,
search: String = "",
visibleItemIndexesNonReversed: () -> IntRange = { 0 .. 0 }
visibleItemIndexesNonReversed: () -> IntRange = { 0 .. 0 },
replaceChat: Boolean = false
) = coroutineScope {
val (chat, navInfo) = chatModel.controller.apiGetChat(rhId, chatType, apiId, contentTag, pagination, search) ?: return@coroutineScope
// For .initial allow the chatItems to be empty as well as chatModel.chatId to not match this chat because these values become set after .initial finishes
@@ -47,6 +48,8 @@ suspend fun apiLoadMessages(
withChats {
if (getChat(chat.id) == null) {
addChat(chat)
} else if (replaceChat) {
replaceChat(chat.remoteHostId, chat.id, chat)
} else {
updateChatInfo(chat.remoteHostId, chat.chatInfo)
updateChatStats(chat.remoteHostId, chat.id, chat.chatStats)
@@ -17,6 +17,7 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.AnnotatedString
@@ -39,6 +40,7 @@ import chat.simplex.common.platform.*
import chat.simplex.common.views.chat.*
import chat.simplex.common.views.chat.item.ItemAction
import chat.simplex.common.views.chatlist.*
import chat.simplex.common.views.database.TtlOptions
import chat.simplex.res.MR
import dev.icerock.moko.resources.StringResource
import kotlinx.coroutines.*
@@ -55,7 +57,11 @@ fun ModalData.GroupChatInfoView(chatModel: ChatModel, rhId: Long?, chatId: Strin
if (chat != null && chat.chatInfo is ChatInfo.Group && currentUser != null) {
val groupInfo = chat.chatInfo.groupInfo
val sendReceipts = remember { mutableStateOf(SendReceipts.fromBool(groupInfo.chatSettings.sendRcpts, currentUser.sendRcptsSmallGroups)) }
val chatItemTTL = remember(groupInfo.id) { mutableStateOf(if (groupInfo.chatItemTTL != null) ChatItemTTL.fromSeconds(groupInfo.chatItemTTL) else null) }
val progressIndicator = rememberSaveable(groupInfo.id) { mutableStateOf(false) }
// TODO [ttl] disable all interactions while ttl is changing
val scope = rememberCoroutineScope()
GroupChatInfoLayout(
chat,
groupInfo,
@@ -66,6 +72,13 @@ fun ModalData.GroupChatInfoView(chatModel: ChatModel, rhId: Long?, chatId: Strin
updateChatSettings(chat.remoteHostId, chat.chatInfo, chatSettings, chatModel)
sendReceipts.value = sendRcpts
},
chatItemTTL = chatItemTTL,
setChatItemTTL = {
val previousChatTTL = chatItemTTL.value
chatItemTTL.value = it
setChatTTLAlert(chatModel, chat.remoteHostId, chat.chatInfo, chatItemTTL, previousChatTTL, progressIndicator)
},
members = remember { chatModel.groupMembers }.value
.filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved }
.sortedByDescending { it.memberRole },
@@ -125,8 +138,13 @@ fun ModalData.GroupChatInfoView(chatModel: ChatModel, rhId: Long?, chatId: Strin
manageGroupLink = {
ModalManager.end.showModal { GroupLinkView(chatModel, rhId, groupInfo, groupLink, groupLinkMemberRole, onGroupLinkUpdated) }
},
onSearchClicked = onSearchClicked
onSearchClicked = onSearchClicked,
enabled = remember { derivedStateOf { !progressIndicator.value } }
)
if (progressIndicator.value) {
ProgressIndicator()
}
}
}
@@ -285,6 +303,8 @@ fun ModalData.GroupChatInfoLayout(
currentUser: User,
sendReceipts: State<SendReceipts>,
setSendReceipts: (SendReceipts) -> Unit,
chatItemTTL: MutableState<ChatItemTTL?>,
setChatItemTTL: (ChatItemTTL?) -> Unit,
members: List<GroupMember>,
developerTools: Boolean,
onLocalAliasChanged: (String) -> Unit,
@@ -300,7 +320,8 @@ fun ModalData.GroupChatInfoLayout(
leaveGroup: () -> Unit,
manageGroupLink: () -> Unit,
close: () -> Unit = { ModalManager.closeAllModalsEverywhere()},
onSearchClicked: () -> Unit
onSearchClicked: () -> Unit,
enabled: State<Boolean>
) {
val listState = remember { appBarHandler.listState }
val scope = rememberCoroutineScope()
@@ -314,7 +335,7 @@ fun ModalData.GroupChatInfoLayout(
if (s.isEmpty()) members else members.filter { m -> m.anyNameContains(s) }
}
}
Box {
Box(Modifier.alpha(if (enabled.value) 1f else 0.6f)) {
val oneHandUI = remember { appPrefs.oneHandUI.state }
LazyColumnWithScrollBar(
state = listState,
@@ -382,6 +403,14 @@ fun ModalData.GroupChatInfoLayout(
SendReceiptsOptionDisabled()
}
TtlOptions(
chatItemTTL,
enabled = remember { mutableStateOf(true) },
onSelected = setChatItemTTL,
default = chatModel.chatItemTTL,
icon = painterResource(MR.images.ic_delete),
)
WallpaperButton {
ModalManager.end.showModal {
val chat = remember { derivedStateOf { chatModel.chats.value.firstOrNull { it.id == chat.id } } }
@@ -770,12 +799,14 @@ fun PreviewGroupChatInfoLayout() {
User.sampleData,
sendReceipts = remember { mutableStateOf(SendReceipts.Yes) },
setSendReceipts = {},
chatItemTTL = remember { mutableStateOf(ChatItemTTL.fromSeconds(0)) },
setChatItemTTL = {},
members = listOf(GroupMember.sampleData, GroupMember.sampleData, GroupMember.sampleData),
developerTools = false,
onLocalAliasChanged = {},
groupLink = null,
scrollToItemId = remember { mutableStateOf(null) },
addMembers = {}, showMemberInfo = {}, editGroupProfile = {}, addOrEditWelcomeMessage = {}, openPreferences = {}, deleteGroup = {}, clearChat = {}, leaveGroup = {}, manageGroupLink = {}, onSearchClicked = {},
addMembers = {}, showMemberInfo = {}, editGroupProfile = {}, addOrEditWelcomeMessage = {}, openPreferences = {}, deleteGroup = {}, clearChat = {}, leaveGroup = {}, manageGroupLink = {}, onSearchClicked = {}, enabled = remember { mutableStateOf(true) }
)
}
}
@@ -12,6 +12,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.*
@@ -108,6 +109,9 @@ fun DatabaseView() {
}
},
onChatItemTTLSelected = {
if (it == null) {
return@DatabaseLayout
}
val oldValue = chatItemTTL.value
chatItemTTL.value = it
if (it < oldValue) {
@@ -158,7 +162,7 @@ fun DatabaseLayout(
exportArchive: () -> Unit,
deleteChatAlert: () -> Unit,
deleteAppFilesAndMedia: () -> Unit,
onChatItemTTLSelected: (ChatItemTTL) -> Unit,
onChatItemTTLSelected: (ChatItemTTL?) -> Unit,
disconnectAllHosts: () -> Unit,
) {
val operationsDisabled = progressIndicator && !chatModel.desktopNoUserNoRemote
@@ -300,27 +304,34 @@ private fun setChatItemTTLAlert(
}
@Composable
private fun TtlOptions(current: State<ChatItemTTL>, enabled: State<Boolean>, onSelected: (ChatItemTTL) -> Unit) {
fun TtlOptions(
current: State<ChatItemTTL?>,
enabled: State<Boolean>,
onSelected: (ChatItemTTL?) -> Unit,
default: State<ChatItemTTL>? = null,
icon: Painter? = null
) {
val values = remember {
val all: ArrayList<ChatItemTTL> = arrayListOf(ChatItemTTL.None, ChatItemTTL.Month, ChatItemTTL.Week, ChatItemTTL.Day)
if (current.value is ChatItemTTL.Seconds) {
all.add(current.value)
val all: ArrayList<ChatItemTTL> = arrayListOf(ChatItemTTL.None, ChatItemTTL.Year, ChatItemTTL.Month, ChatItemTTL.Week, ChatItemTTL.Day)
val currentValue = current.value
if (currentValue is ChatItemTTL.Seconds) {
all.add(currentValue)
}
all.map {
when (it) {
is ChatItemTTL.None -> it to generalGetString(MR.strings.chat_item_ttl_none)
is ChatItemTTL.Day -> it to generalGetString(MR.strings.chat_item_ttl_day)
is ChatItemTTL.Week -> it to generalGetString(MR.strings.chat_item_ttl_week)
is ChatItemTTL.Month -> it to generalGetString(MR.strings.chat_item_ttl_month)
is ChatItemTTL.Seconds -> it to String.format(generalGetString(MR.strings.chat_item_ttl_seconds), it.secs)
}
val options = mutableListOf<Pair<ChatItemTTL?, String>>().apply {
all.mapTo(this) { it to it.text }
}
if (default != null) {
options.add(null to String.format(generalGetString(MR.strings.chat_item_ttl_default), default.value.text))
}
options
}
ExposedDropDownSettingRow(
generalGetString(MR.strings.delete_messages_after),
values,
current,
icon = null,
icon = icon,
enabled = enabled,
onSelected = onSelected
)
@@ -540,6 +540,11 @@
<!-- Chat Info Settings - ChatInfoView.kt -->
<string name="notifications">Notifications</string>
<string name="disable_automatic_deletion_question">Disable automatic message deletion?</string>
<string name="change_automatic_deletion_question">Change automatic message deletion?</string>
<string name="disable_automatic_deletion_message">Messages on this chat will never be deleted.</string>
<string name="change_automatic_chat_deletion_message">This action cannot be undone - the messages sent and received on this chat earlier than selected will be deleted. It may take several minutes.</string>
<string name="disable_automatic_deletion">Disable delete messages</string>
<!-- Chat Info Actions - ChatInfoView.kt -->
<string name="info_view_connect_button">connect</string>
@@ -1396,7 +1401,9 @@
<string name="chat_item_ttl_day">1 day</string>
<string name="chat_item_ttl_week">1 week</string>
<string name="chat_item_ttl_month">1 month</string>
<string name="chat_item_ttl_year">1 year</string>
<string name="chat_item_ttl_seconds">%s second(s)</string>
<string name="chat_item_ttl_default">default (%s)</string>
<string name="messages_section_title">Messages</string>
<string name="messages_section_description">This setting applies to messages in your current chat profile</string>
<string name="delete_messages_after">Delete messages after</string>