mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 20:38:20 +00:00
android, desktop: role-based preferences, SimpleX links preference (#4023)
* android, desktop: role-based preferences, SimpleX links preference * disable for now * different way of disabling send button * mic icon filled * icons * braces * colors --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
co-authored by
Evgeny Poberezkin
parent
433b3652fb
commit
de81434b36
+37
-7
@@ -805,6 +805,24 @@ data class Chat(
|
||||
|
||||
val id: String get() = chatInfo.id
|
||||
|
||||
fun groupFeatureEnabled(feature: GroupFeature): Boolean =
|
||||
if (chatInfo is ChatInfo.Group) {
|
||||
val groupInfo = chatInfo.groupInfo
|
||||
val p = groupInfo.fullGroupPreferences
|
||||
when (feature) {
|
||||
GroupFeature.TimedMessages -> p.timedMessages.on
|
||||
GroupFeature.DirectMessages -> p.directMessages.on(groupInfo.membership)
|
||||
GroupFeature.FullDelete -> p.fullDelete.on
|
||||
GroupFeature.Reactions -> p.reactions.on
|
||||
GroupFeature.Voice -> p.voice.on(groupInfo.membership)
|
||||
GroupFeature.Files -> p.files.on(groupInfo.membership)
|
||||
GroupFeature.SimplexLinks -> p.simplexLinks.on(groupInfo.membership)
|
||||
GroupFeature.History -> p.history.on
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ChatStats(val unreadCount: Int = 0, val minUnreadItemId: Long = 0, val unreadChat: Boolean = false)
|
||||
|
||||
@@ -1240,7 +1258,7 @@ data class GroupInfo (
|
||||
ChatFeature.TimedMessages -> fullGroupPreferences.timedMessages.on
|
||||
ChatFeature.FullDelete -> fullGroupPreferences.fullDelete.on
|
||||
ChatFeature.Reactions -> fullGroupPreferences.reactions.on
|
||||
ChatFeature.Voice -> fullGroupPreferences.voice.on
|
||||
ChatFeature.Voice -> fullGroupPreferences.voice.on(membership)
|
||||
ChatFeature.Calls -> false
|
||||
}
|
||||
override val timedMessagesTTL: Int? get() = with(fullGroupPreferences.timedMessages) { if (on) ttl else null }
|
||||
@@ -2319,8 +2337,8 @@ sealed class CIContent: ItemContent {
|
||||
@Serializable @SerialName("sndChatFeature") class SndChatFeature(val feature: ChatFeature, val enabled: FeatureEnabled, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvChatPreference") class RcvChatPreference(val feature: ChatFeature, val allowed: FeatureAllowed, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("sndChatPreference") class SndChatPreference(val feature: ChatFeature, val allowed: FeatureAllowed, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvGroupFeature") class RcvGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("sndGroupFeature") class SndGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvGroupFeature") class RcvGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null, val memberRole_: GroupMemberRole?): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("sndGroupFeature") class SndGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null, val memberRole_: GroupMemberRole?): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvChatFeatureRejected") class RcvChatFeatureRejected(val feature: ChatFeature): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvGroupFeatureRejected") class RcvGroupFeatureRejected(val groupFeature: GroupFeature): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("sndModerated") object SndModerated: CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@@ -2352,8 +2370,8 @@ sealed class CIContent: ItemContent {
|
||||
is SndChatFeature -> featureText(feature, enabled.text, param)
|
||||
is RcvChatPreference -> preferenceText(feature, allowed, param)
|
||||
is SndChatPreference -> preferenceText(feature, allowed, param)
|
||||
is RcvGroupFeature -> featureText(groupFeature, preference.enable.text, param)
|
||||
is SndGroupFeature -> featureText(groupFeature, preference.enable.text, param)
|
||||
is RcvGroupFeature -> featureText(groupFeature, preference.enable.text, param, memberRole_)
|
||||
is SndGroupFeature -> featureText(groupFeature, preference.enable.text, param, memberRole_)
|
||||
is RcvChatFeatureRejected -> "${feature.text}: ${generalGetString(MR.strings.feature_received_prohibited)}"
|
||||
is RcvGroupFeatureRejected -> "${groupFeature.text}: ${generalGetString(MR.strings.feature_received_prohibited)}"
|
||||
is SndModerated -> generalGetString(MR.strings.moderated_description)
|
||||
@@ -2390,11 +2408,23 @@ sealed class CIContent: ItemContent {
|
||||
|
||||
private val e2eeInfoNoPQStr: String = generalGetString(MR.strings.e2ee_info_no_pq_short)
|
||||
|
||||
fun featureText(feature: Feature, enabled: String, param: Int?): String =
|
||||
if (feature.hasParam) {
|
||||
fun featureText(feature: Feature, enabled: String, param: Int?, role: GroupMemberRole? = null): String =
|
||||
(if (feature.hasParam) {
|
||||
"${feature.text}: ${timeText(param)}"
|
||||
} else {
|
||||
"${feature.text}: $enabled"
|
||||
}) + (
|
||||
if (feature.hasRole && role != null)
|
||||
" (${roleText(role)})"
|
||||
else
|
||||
""
|
||||
)
|
||||
|
||||
private fun roleText(role: GroupMemberRole?): String =
|
||||
when (role) {
|
||||
GroupMemberRole.Owner -> generalGetString(MR.strings.feature_roles_owners)
|
||||
GroupMemberRole.Admin -> generalGetString(MR.strings.feature_roles_admins)
|
||||
else -> generalGetString(MR.strings.feature_roles_all_members)
|
||||
}
|
||||
|
||||
fun preferenceText(feature: Feature, allowed: FeatureAllowed, param: Int?): String = when {
|
||||
|
||||
+63
-12
@@ -3430,6 +3430,7 @@ interface Feature {
|
||||
@Composable
|
||||
fun iconFilled(): Painter
|
||||
val hasParam: Boolean
|
||||
val hasRole: Boolean
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@@ -3449,6 +3450,7 @@ enum class ChatFeature: Feature {
|
||||
TimedMessages -> true
|
||||
else -> false
|
||||
}
|
||||
override val hasRole: Boolean = false
|
||||
|
||||
override val text: String
|
||||
get() = when(this) {
|
||||
@@ -3549,6 +3551,7 @@ enum class GroupFeature: Feature {
|
||||
@SerialName("reactions") Reactions,
|
||||
@SerialName("voice") Voice,
|
||||
@SerialName("files") Files,
|
||||
@SerialName("simplexLinks") SimplexLinks,
|
||||
@SerialName("history") History;
|
||||
|
||||
override val hasParam: Boolean get() = when(this) {
|
||||
@@ -3556,6 +3559,18 @@ enum class GroupFeature: Feature {
|
||||
else -> false
|
||||
}
|
||||
|
||||
override val hasRole: Boolean
|
||||
get() = when (this) {
|
||||
TimedMessages -> false
|
||||
DirectMessages -> true
|
||||
FullDelete -> false
|
||||
Reactions -> false
|
||||
Voice -> true
|
||||
Files -> true
|
||||
SimplexLinks -> true
|
||||
History -> false
|
||||
}
|
||||
|
||||
override val text: String
|
||||
get() = when(this) {
|
||||
TimedMessages -> generalGetString(MR.strings.timed_messages)
|
||||
@@ -3564,6 +3579,7 @@ enum class GroupFeature: Feature {
|
||||
Reactions -> generalGetString(MR.strings.message_reactions)
|
||||
Voice -> generalGetString(MR.strings.voice_messages)
|
||||
Files -> generalGetString(MR.strings.files_and_media)
|
||||
SimplexLinks -> generalGetString(MR.strings.simplex_links)
|
||||
History -> generalGetString(MR.strings.recent_history)
|
||||
}
|
||||
|
||||
@@ -3575,6 +3591,7 @@ enum class GroupFeature: Feature {
|
||||
Reactions -> painterResource(MR.images.ic_add_reaction)
|
||||
Voice -> painterResource(MR.images.ic_keyboard_voice)
|
||||
Files -> painterResource(MR.images.ic_draft)
|
||||
SimplexLinks -> painterResource(MR.images.ic_link)
|
||||
History -> painterResource(MR.images.ic_schedule)
|
||||
}
|
||||
|
||||
@@ -3586,6 +3603,7 @@ enum class GroupFeature: Feature {
|
||||
Reactions -> painterResource(MR.images.ic_add_reaction_filled)
|
||||
Voice -> painterResource(MR.images.ic_keyboard_voice_filled)
|
||||
Files -> painterResource(MR.images.ic_draft_filled)
|
||||
SimplexLinks -> painterResource(MR.images.ic_link)
|
||||
History -> painterResource(MR.images.ic_schedule_filled)
|
||||
}
|
||||
|
||||
@@ -3616,6 +3634,10 @@ enum class GroupFeature: Feature {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.allow_to_send_files)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.prohibit_sending_files)
|
||||
}
|
||||
SimplexLinks -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.allow_to_send_simplex_links)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.prohibit_sending_simplex_links)
|
||||
}
|
||||
History -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.enable_sending_recent_history)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.disable_sending_recent_history)
|
||||
@@ -3647,6 +3669,10 @@ enum class GroupFeature: Feature {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.group_members_can_send_files)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.files_are_prohibited_in_group)
|
||||
}
|
||||
SimplexLinks -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.group_members_can_send_simplex_links)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.simplex_links_are_prohibited_in_group)
|
||||
}
|
||||
History -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.recent_history_is_sent_to_new_members)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.recent_history_is_not_sent_to_new_members)
|
||||
@@ -3760,11 +3786,12 @@ enum class FeatureAllowed {
|
||||
@Serializable
|
||||
data class FullGroupPreferences(
|
||||
val timedMessages: TimedMessagesGroupPreference,
|
||||
val directMessages: GroupPreference,
|
||||
val directMessages: RoleGroupPreference,
|
||||
val fullDelete: GroupPreference,
|
||||
val reactions: GroupPreference,
|
||||
val voice: GroupPreference,
|
||||
val files: GroupPreference,
|
||||
val voice: RoleGroupPreference,
|
||||
val files: RoleGroupPreference,
|
||||
val simplexLinks: RoleGroupPreference,
|
||||
val history: GroupPreference,
|
||||
) {
|
||||
fun toGroupPreferences(): GroupPreferences =
|
||||
@@ -3775,17 +3802,19 @@ data class FullGroupPreferences(
|
||||
reactions = reactions,
|
||||
voice = voice,
|
||||
files = files,
|
||||
simplexLinks = simplexLinks,
|
||||
history = history
|
||||
)
|
||||
|
||||
companion object {
|
||||
val sampleData = FullGroupPreferences(
|
||||
timedMessages = TimedMessagesGroupPreference(GroupFeatureEnabled.OFF),
|
||||
directMessages = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
directMessages = RoleGroupPreference(GroupFeatureEnabled.OFF, role = null),
|
||||
fullDelete = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
reactions = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = GroupPreference(GroupFeatureEnabled.ON),
|
||||
files = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
files = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
simplexLinks = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
history = GroupPreference(GroupFeatureEnabled.ON),
|
||||
)
|
||||
}
|
||||
@@ -3794,21 +3823,23 @@ data class FullGroupPreferences(
|
||||
@Serializable
|
||||
data class GroupPreferences(
|
||||
val timedMessages: TimedMessagesGroupPreference? = null,
|
||||
val directMessages: GroupPreference? = null,
|
||||
val directMessages: RoleGroupPreference? = null,
|
||||
val fullDelete: GroupPreference? = null,
|
||||
val reactions: GroupPreference? = null,
|
||||
val voice: GroupPreference? = null,
|
||||
val files: GroupPreference? = null,
|
||||
val voice: RoleGroupPreference? = null,
|
||||
val files: RoleGroupPreference? = null,
|
||||
val simplexLinks: RoleGroupPreference? = null,
|
||||
val history: GroupPreference? = null,
|
||||
) {
|
||||
companion object {
|
||||
val sampleData = GroupPreferences(
|
||||
timedMessages = TimedMessagesGroupPreference(GroupFeatureEnabled.OFF),
|
||||
directMessages = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
directMessages = RoleGroupPreference(GroupFeatureEnabled.OFF, role = null),
|
||||
fullDelete = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
reactions = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = GroupPreference(GroupFeatureEnabled.ON),
|
||||
files = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
files = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
simplexLinks = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
history = GroupPreference(GroupFeatureEnabled.ON),
|
||||
)
|
||||
}
|
||||
@@ -3819,6 +3850,26 @@ data class GroupPreference(
|
||||
val enable: GroupFeatureEnabled
|
||||
) {
|
||||
val on: Boolean get() = enable == GroupFeatureEnabled.ON
|
||||
|
||||
fun enabled(role: GroupMemberRole?, m: GroupMember?): GroupFeatureEnabled =
|
||||
when (enable) {
|
||||
GroupFeatureEnabled.OFF -> GroupFeatureEnabled.OFF
|
||||
GroupFeatureEnabled.ON ->
|
||||
if (role != null && m != null) {
|
||||
if (m.memberRole >= role) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF
|
||||
} else {
|
||||
GroupFeatureEnabled.ON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class RoleGroupPreference(
|
||||
val enable: GroupFeatureEnabled,
|
||||
val role: GroupMemberRole? = null,
|
||||
) {
|
||||
fun on(m: GroupMember): Boolean =
|
||||
enable == GroupFeatureEnabled.ON && m.memberRole >= (role ?: GroupMemberRole.Observer)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
||||
+1
@@ -85,6 +85,7 @@ fun TerminalLayout(
|
||||
isDirectChat = false,
|
||||
liveMessageAlertShown = SharedPreference(get = { false }, set = {}),
|
||||
sendMsgEnabled = true,
|
||||
sendButtonEnabled = true,
|
||||
nextSendGrpInv = false,
|
||||
needToAllowVoiceToContact = false,
|
||||
allowedVoiceByPrefs = false,
|
||||
|
||||
+63
-17
@@ -1,6 +1,7 @@
|
||||
@file:UseSerializers(UriSerializer::class)
|
||||
package chat.simplex.common.views.chat
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.*
|
||||
@@ -11,6 +12,8 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -18,8 +21,7 @@ import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.filesToDelete
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.Indigo
|
||||
import chat.simplex.common.ui.theme.isSystemInDarkTheme
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
@@ -115,6 +117,15 @@ data class ComposeState(
|
||||
}
|
||||
}
|
||||
|
||||
val attachmentPreview: Boolean
|
||||
get() = when (preview) {
|
||||
ComposePreview.NoPreview -> false
|
||||
is ComposePreview.CLinkPreview -> false
|
||||
is ComposePreview.MediaPreview -> preview.content.isNotEmpty()
|
||||
is ComposePreview.VoicePreview -> false
|
||||
is ComposePreview.FilePreview -> true
|
||||
}
|
||||
|
||||
val empty: Boolean
|
||||
get() = message.isEmpty() && preview is ComposePreview.NoPreview && contextItem is ComposeContextItem.NoContextItem
|
||||
|
||||
@@ -243,10 +254,23 @@ fun ComposeView(
|
||||
attachmentOption: MutableState<AttachmentOption?>,
|
||||
showChooseAttachment: () -> Unit
|
||||
) {
|
||||
val cancelledLinks = rememberSaveable { mutableSetOf<String>() }
|
||||
fun isSimplexLink(link: String): Boolean =
|
||||
link.startsWith("https://simplex.chat", true) || link.startsWith("http://simplex.chat", true)
|
||||
|
||||
fun parseMessage(msg: String): Pair<String?, Boolean> {
|
||||
if (msg.isBlank()) return null to false
|
||||
val parsedMsg = parseToMarkdown(msg) ?: return null to false
|
||||
val link = parsedMsg.firstOrNull { ft -> ft.format is Format.Uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text) }
|
||||
val simplexLink = parsedMsg.any { ft -> ft.format is Format.SimplexLink }
|
||||
return link?.text to simplexLink
|
||||
}
|
||||
|
||||
val linkUrl = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
// default value parsed because of draft
|
||||
val hasSimplexLink = rememberSaveable { mutableStateOf(parseMessage(composeState.value.message).second) }
|
||||
val prevLinkUrl = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val pendingLinkUrl = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val cancelledLinks = rememberSaveable { mutableSetOf<String>() }
|
||||
val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get()
|
||||
val saveLastDraft = chatModel.controller.appPrefs.privacySaveLastDraft.get()
|
||||
val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground)
|
||||
@@ -255,15 +279,6 @@ fun ComposeView(
|
||||
|
||||
AttachmentSelection(composeState, attachmentOption, composeState::processPickedFile) { uris, text -> CoroutineScope(Dispatchers.IO).launch { composeState.processPickedMedia(uris, text) } }
|
||||
|
||||
fun isSimplexLink(link: String): Boolean =
|
||||
link.startsWith("https://simplex.chat", true) || link.startsWith("http://simplex.chat", true)
|
||||
|
||||
fun parseMessage(msg: String): String? {
|
||||
val parsedMsg = parseToMarkdown(msg)
|
||||
val link = parsedMsg?.firstOrNull { ft -> ft.format is Format.Uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text) }
|
||||
return link?.text
|
||||
}
|
||||
|
||||
fun loadLinkPreview(url: String, wait: Long? = null) {
|
||||
if (pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null))
|
||||
@@ -283,7 +298,9 @@ fun ComposeView(
|
||||
|
||||
fun showLinkPreview(s: String) {
|
||||
prevLinkUrl.value = linkUrl.value
|
||||
linkUrl.value = parseMessage(s)
|
||||
val parsed = parseMessage(s)
|
||||
linkUrl.value = parsed.first
|
||||
hasSimplexLink.value = parsed.second
|
||||
val url = linkUrl.value
|
||||
if (url != null) {
|
||||
if (url != composeState.value.linkPreview?.uri && url != pendingLinkUrl.value) {
|
||||
@@ -387,7 +404,7 @@ fun ComposeView(
|
||||
fun checkLinkPreview(): MsgContent {
|
||||
return when (val composePreview = cs.preview) {
|
||||
is ComposePreview.CLinkPreview -> {
|
||||
val url = parseMessage(msgText)
|
||||
val url = parseMessage(msgText).first
|
||||
val lp = composePreview.linkPreview
|
||||
if (lp != null && url == lp.uri) {
|
||||
MsgContent.MCLink(msgText, preview = lp)
|
||||
@@ -563,8 +580,16 @@ fun ComposeView(
|
||||
} else {
|
||||
textStyle.value = smallFont
|
||||
if (composeState.value.linkPreviewAllowed) {
|
||||
if (s.isNotEmpty()) showLinkPreview(s)
|
||||
else resetLinkPreview()
|
||||
if (s.isNotEmpty()) {
|
||||
showLinkPreview(s)
|
||||
} else {
|
||||
resetLinkPreview()
|
||||
hasSimplexLink.value = false
|
||||
}
|
||||
} else if (s.isNotEmpty() && !chat.groupFeatureEnabled(GroupFeature.SimplexLinks)) {
|
||||
hasSimplexLink.value = parseMessage(s).second
|
||||
} else {
|
||||
hasSimplexLink.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -700,6 +725,16 @@ fun ComposeView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MsgNotAllowedView(reason: String, icon: Painter) {
|
||||
val color = CurrentColors.collectAsState().value.appColors.receivedMessage
|
||||
Row(Modifier.padding(top = 5.dp).fillMaxWidth().background(color).padding(horizontal = DEFAULT_PADDING_HALF, vertical = DEFAULT_PADDING_HALF * 1.5f), verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(icon, null, tint = MaterialTheme.colors.secondary)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
|
||||
Text(reason, fontStyle = FontStyle.Italic)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun contextItemView() {
|
||||
when (val contextItem = composeState.value.contextItem) {
|
||||
@@ -743,7 +778,17 @@ fun ComposeView(
|
||||
if (nextSendGrpInv.value) {
|
||||
ComposeContextInvitingContactMemberView()
|
||||
}
|
||||
val simplexLinkProhibited = hasSimplexLink.value && !chat.groupFeatureEnabled(GroupFeature.SimplexLinks)
|
||||
val fileProhibited = composeState.value.attachmentPreview && !chat.groupFeatureEnabled(GroupFeature.Files)
|
||||
val voiceProhibited = composeState.value.preview is ComposePreview.VoicePreview && !chat.chatInfo.featureEnabled(ChatFeature.Voice)
|
||||
if (composeState.value.preview !is ComposePreview.VoicePreview || composeState.value.editing) {
|
||||
if (simplexLinkProhibited) {
|
||||
MsgNotAllowedView(generalGetString(MR.strings.simplex_links_not_allowed), icon = painterResource(MR.images.ic_link))
|
||||
} else if (fileProhibited) {
|
||||
MsgNotAllowedView(generalGetString(MR.strings.files_and_media_not_allowed), icon = painterResource(MR.images.ic_draft))
|
||||
} else if (voiceProhibited) {
|
||||
MsgNotAllowedView(generalGetString(MR.strings.voice_messages_not_allowed), icon = painterResource(MR.images.ic_mic))
|
||||
}
|
||||
contextItemView()
|
||||
when {
|
||||
composeState.value.editing && composeState.value.preview is ComposePreview.VoicePreview -> {}
|
||||
@@ -764,7 +809,7 @@ fun ComposeView(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
val isGroupAndProhibitedFiles = chat.chatInfo is ChatInfo.Group && !chat.chatInfo.groupInfo.fullGroupPreferences.files.on
|
||||
val isGroupAndProhibitedFiles = chat.chatInfo is ChatInfo.Group && !chat.chatInfo.groupInfo.fullGroupPreferences.files.on(chat.chatInfo.groupInfo.membership)
|
||||
val attachmentClicked = if (isGroupAndProhibitedFiles) {
|
||||
{
|
||||
AlertManager.shared.showAlertMsg(
|
||||
@@ -871,6 +916,7 @@ fun ComposeView(
|
||||
chat.chatInfo is ChatInfo.Direct,
|
||||
liveMessageAlertShown = chatModel.controller.appPrefs.liveMessageAlertShown,
|
||||
sendMsgEnabled = sendMsgEnabled.value,
|
||||
sendButtonEnabled = sendMsgEnabled.value && !(simplexLinkProhibited || fileProhibited || voiceProhibited),
|
||||
nextSendGrpInv = nextSendGrpInv.value,
|
||||
needToAllowVoiceToContact,
|
||||
allowedVoiceByPrefs,
|
||||
|
||||
+7
-2
@@ -39,6 +39,7 @@ fun SendMsgView(
|
||||
isDirectChat: Boolean,
|
||||
liveMessageAlertShown: SharedPreference<Boolean>,
|
||||
sendMsgEnabled: Boolean,
|
||||
sendButtonEnabled: Boolean,
|
||||
nextSendGrpInv: Boolean,
|
||||
needToAllowVoiceToContact: Boolean,
|
||||
allowedVoiceByPrefs: Boolean,
|
||||
@@ -75,7 +76,8 @@ fun SendMsgView(
|
||||
val showDeleteTextButton = rememberSaveable { mutableStateOf(false) }
|
||||
val sendMsgButtonDisabled = !sendMsgEnabled || !cs.sendEnabled() ||
|
||||
(!allowedVoiceByPrefs && cs.preview is ComposePreview.VoicePreview) ||
|
||||
cs.endLiveDisabled
|
||||
cs.endLiveDisabled ||
|
||||
!sendButtonEnabled
|
||||
PlatformTextField(composeState, sendMsgEnabled, sendMsgButtonDisabled, textStyle, showDeleteTextButton, userIsObserver, onMessageChange, editPrevMessage, onFilesPasted) {
|
||||
if (!cs.inProgress) {
|
||||
sendMessage(null)
|
||||
@@ -430,7 +432,7 @@ private fun SendMsgButton(
|
||||
.padding(4.dp)
|
||||
.alpha(alpha.value)
|
||||
.clip(CircleShape)
|
||||
.background(if (enabled) sendButtonColor else MaterialTheme.colors.secondary)
|
||||
.background(if (enabled) sendButtonColor else MaterialTheme.colors.secondary.copy(alpha = 0.75f))
|
||||
.padding(3.dp)
|
||||
)
|
||||
}
|
||||
@@ -552,6 +554,7 @@ fun PreviewSendMsgView() {
|
||||
isDirectChat = true,
|
||||
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
||||
sendMsgEnabled = true,
|
||||
sendButtonEnabled = true,
|
||||
nextSendGrpInv = false,
|
||||
needToAllowVoiceToContact = false,
|
||||
allowedVoiceByPrefs = true,
|
||||
@@ -586,6 +589,7 @@ fun PreviewSendMsgViewEditing() {
|
||||
isDirectChat = true,
|
||||
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
||||
sendMsgEnabled = true,
|
||||
sendButtonEnabled = true,
|
||||
nextSendGrpInv = false,
|
||||
needToAllowVoiceToContact = false,
|
||||
allowedVoiceByPrefs = true,
|
||||
@@ -620,6 +624,7 @@ fun PreviewSendMsgViewInProgress() {
|
||||
isDirectChat = true,
|
||||
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
||||
sendMsgEnabled = true,
|
||||
sendButtonEnabled = true,
|
||||
nextSendGrpInv = false,
|
||||
needToAllowVoiceToContact = false,
|
||||
allowedVoiceByPrefs = true,
|
||||
|
||||
+2
-2
@@ -309,7 +309,7 @@ fun GroupMemberInfoLayout(
|
||||
SectionView {
|
||||
if (contactId != null && knownDirectChat(contactId) != null) {
|
||||
OpenChatButton(onClick = { openDirectChat(contactId) })
|
||||
} else if (groupInfo.fullGroupPreferences.directMessages.on) {
|
||||
} else if (groupInfo.fullGroupPreferences.directMessages.on(groupInfo.membership)) {
|
||||
if (contactId != null) {
|
||||
OpenChatButton(onClick = { openDirectChat(contactId) })
|
||||
} else if (member.activeConn?.peerChatVRange?.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) == true) {
|
||||
@@ -335,7 +335,7 @@ fun GroupMemberInfoLayout(
|
||||
val clipboard = LocalClipboardManager.current
|
||||
ShareAddressButton { clipboard.shareText(simplexChatLink(member.contactLink)) }
|
||||
if (contactId != null) {
|
||||
if (knownDirectChat(contactId) == null && !groupInfo.fullGroupPreferences.directMessages.on) {
|
||||
if (knownDirectChat(contactId) == null && !groupInfo.fullGroupPreferences.directMessages.on(groupInfo.membership)) {
|
||||
ConnectViaAddressButton(onClick = { connectViaAddress(member.contactLink) })
|
||||
}
|
||||
} else {
|
||||
|
||||
+57
-18
@@ -6,7 +6,6 @@ import SectionDividerSpaced
|
||||
import SectionItemView
|
||||
import SectionTextFooter
|
||||
import SectionView
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
@@ -20,13 +19,20 @@ import chat.simplex.common.views.usersettings.PreferenceToggleWithIcon
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
|
||||
private val featureRoles: List<Pair<GroupMemberRole?, String>> = listOf(
|
||||
null to generalGetString(MR.strings.feature_roles_all_members),
|
||||
GroupMemberRole.Admin to generalGetString(MR.strings.feature_roles_admins),
|
||||
GroupMemberRole.Owner to generalGetString(MR.strings.feature_roles_owners)
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () -> Unit,) {
|
||||
fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () -> Unit) {
|
||||
val groupInfo = remember { derivedStateOf {
|
||||
val ch = m.getChat(chatId)
|
||||
val g = (ch?.chatInfo as? ChatInfo.Group)?.groupInfo
|
||||
if (g == null || ch?.remoteHostId != rhId) null else g
|
||||
if (g == null || ch.remoteHostId != rhId) null else g
|
||||
}}
|
||||
val gInfo = groupInfo.value ?: return
|
||||
var preferences by rememberSaveable(gInfo, stateSaver = serializableSaver()) { mutableStateOf(gInfo.fullGroupPreferences) }
|
||||
@@ -81,7 +87,7 @@ private fun GroupPreferencesLayout(
|
||||
val onTTLUpdated = { ttl: Int? ->
|
||||
applyPrefs(preferences.copy(timedMessages = preferences.timedMessages.copy(ttl = ttl)))
|
||||
}
|
||||
FeatureSection(GroupFeature.TimedMessages, timedMessages, groupInfo, preferences, onTTLUpdated) { enable ->
|
||||
FeatureSection(GroupFeature.TimedMessages, timedMessages, null, groupInfo, preferences, onTTLUpdated) { enable, _ ->
|
||||
if (enable == GroupFeatureEnabled.ON) {
|
||||
applyPrefs(preferences.copy(timedMessages = TimedMessagesGroupPreference(enable = enable, ttl = preferences.timedMessages.ttl ?: 86400)))
|
||||
} else {
|
||||
@@ -90,33 +96,45 @@ private fun GroupPreferencesLayout(
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowDirectMessages = remember(preferences) { mutableStateOf(preferences.directMessages.enable) }
|
||||
FeatureSection(GroupFeature.DirectMessages, allowDirectMessages, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(directMessages = GroupPreference(enable = it)))
|
||||
val directMessagesRole = remember(preferences) { mutableStateOf(preferences.directMessages.role) }
|
||||
FeatureSection(GroupFeature.DirectMessages, allowDirectMessages, directMessagesRole, groupInfo, preferences, onTTLUpdated) { enable, role ->
|
||||
applyPrefs(preferences.copy(directMessages = RoleGroupPreference(enable = enable, role)))
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowFullDeletion = remember(preferences) { mutableStateOf(preferences.fullDelete.enable) }
|
||||
FeatureSection(GroupFeature.FullDelete, allowFullDeletion, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(fullDelete = GroupPreference(enable = it)))
|
||||
FeatureSection(GroupFeature.FullDelete, allowFullDeletion, null, groupInfo, preferences, onTTLUpdated) { enable, _ ->
|
||||
applyPrefs(preferences.copy(fullDelete = GroupPreference(enable = enable)))
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowReactions = remember(preferences) { mutableStateOf(preferences.reactions.enable) }
|
||||
FeatureSection(GroupFeature.Reactions, allowReactions, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(reactions = GroupPreference(enable = it)))
|
||||
FeatureSection(GroupFeature.Reactions, allowReactions, null, groupInfo, preferences, onTTLUpdated) { enable, _ ->
|
||||
applyPrefs(preferences.copy(reactions = GroupPreference(enable = enable)))
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowVoice = remember(preferences) { mutableStateOf(preferences.voice.enable) }
|
||||
FeatureSection(GroupFeature.Voice, allowVoice, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(voice = GroupPreference(enable = it)))
|
||||
val voiceRole = remember(preferences) { mutableStateOf(preferences.voice.role) }
|
||||
FeatureSection(GroupFeature.Voice, allowVoice, voiceRole, groupInfo, preferences, onTTLUpdated) { enable, role ->
|
||||
applyPrefs(preferences.copy(voice = RoleGroupPreference(enable = enable, role)))
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowFiles = remember(preferences) { mutableStateOf(preferences.files.enable) }
|
||||
FeatureSection(GroupFeature.Files, allowFiles, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(files = GroupPreference(enable = it)))
|
||||
val filesRole = remember(preferences) { mutableStateOf(preferences.files.role) }
|
||||
FeatureSection(GroupFeature.Files, allowFiles, filesRole, groupInfo, preferences, onTTLUpdated) { enable, role ->
|
||||
applyPrefs(preferences.copy(files = RoleGroupPreference(enable = enable, role)))
|
||||
}
|
||||
|
||||
// TODO enable simplexLinks preference in 5.8
|
||||
// SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
// val allowSimplexLinks = remember(preferences) { mutableStateOf(preferences.simplexLinks.enable) }
|
||||
// val simplexLinksRole = remember(preferences) { mutableStateOf(preferences.simplexLinks.role) }
|
||||
// FeatureSection(GroupFeature.SimplexLinks, allowSimplexLinks, simplexLinksRole, groupInfo, preferences, onTTLUpdated) { enable, role ->
|
||||
// applyPrefs(preferences.copy(simplexLinks = RoleGroupPreference(enable = enable, role)))
|
||||
// }
|
||||
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val enableHistory = remember(preferences) { mutableStateOf(preferences.history.enable) }
|
||||
FeatureSection(GroupFeature.History, enableHistory, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(history = GroupPreference(enable = it)))
|
||||
FeatureSection(GroupFeature.History, enableHistory, null, groupInfo, preferences, onTTLUpdated) { enable, _ ->
|
||||
applyPrefs(preferences.copy(history = GroupPreference(enable = enable)))
|
||||
}
|
||||
if (groupInfo.canEdit) {
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
@@ -134,10 +152,11 @@ private fun GroupPreferencesLayout(
|
||||
private fun FeatureSection(
|
||||
feature: GroupFeature,
|
||||
enableFeature: State<GroupFeatureEnabled>,
|
||||
enableForRole: State<GroupMemberRole?>? = null,
|
||||
groupInfo: GroupInfo,
|
||||
preferences: FullGroupPreferences,
|
||||
onTTLUpdated: (Int?) -> Unit,
|
||||
onSelected: (GroupFeatureEnabled) -> Unit
|
||||
onSelected: (GroupFeatureEnabled, GroupMemberRole?) -> Unit
|
||||
) {
|
||||
SectionView {
|
||||
val on = enableFeature.value == GroupFeatureEnabled.ON
|
||||
@@ -151,7 +170,7 @@ private fun FeatureSection(
|
||||
iconTint,
|
||||
enableFeature.value == GroupFeatureEnabled.ON,
|
||||
) { checked ->
|
||||
onSelected(if (checked) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF)
|
||||
onSelected(if (checked) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF, enableForRole?.value)
|
||||
}
|
||||
if (timedOn) {
|
||||
val ttl = rememberSaveable(preferences.timedMessages) { mutableStateOf(preferences.timedMessages.ttl) }
|
||||
@@ -165,6 +184,18 @@ private fun FeatureSection(
|
||||
onSelected = onTTLUpdated
|
||||
)
|
||||
}
|
||||
if (enableFeature.value == GroupFeatureEnabled.ON && enableForRole != null) {
|
||||
ExposedDropDownSettingRow(
|
||||
generalGetString(MR.strings.feature_enabled_for),
|
||||
featureRoles,
|
||||
enableForRole,
|
||||
// remove in v5.8
|
||||
enabled = remember { mutableStateOf(false) },
|
||||
onSelected = { value ->
|
||||
onSelected(enableFeature.value, value)
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
InfoRow(
|
||||
feature.text,
|
||||
@@ -175,6 +206,14 @@ private fun FeatureSection(
|
||||
if (timedOn) {
|
||||
InfoRow(generalGetString(MR.strings.delete_after), timeText(preferences.timedMessages.ttl))
|
||||
}
|
||||
if (enableFeature.value == GroupFeatureEnabled.ON && enableForRole != null) {
|
||||
InfoRow(generalGetString(MR.strings.feature_enabled_for), featureRoles.firstOrNull { it.first == enableForRole.value }?.second ?: generalGetString(MR.strings.feature_roles_all_members), textColor = MaterialTheme.colors.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyChangeEffect(enableFeature.value) {
|
||||
if (enableFeature.value == GroupFeatureEnabled.OFF) {
|
||||
onSelected(enableFeature.value, null)
|
||||
}
|
||||
}
|
||||
SectionTextFooter(feature.enableDescription(enableFeature.value, groupInfo.canEdit))
|
||||
|
||||
+7
-6
@@ -16,6 +16,7 @@ import chat.simplex.common.platform.onRightClick
|
||||
|
||||
@Composable
|
||||
fun CIChatFeatureView(
|
||||
chatInfo: ChatInfo,
|
||||
chatItem: ChatItem,
|
||||
feature: Feature,
|
||||
iconColor: Color,
|
||||
@@ -23,7 +24,7 @@ fun CIChatFeatureView(
|
||||
revealed: MutableState<Boolean>,
|
||||
showMenu: MutableState<Boolean>,
|
||||
) {
|
||||
val merged = if (!revealed.value) mergedFeatures(chatItem) else emptyList()
|
||||
val merged = if (!revealed.value) mergedFeatures(chatItem, chatInfo) else emptyList()
|
||||
Box(
|
||||
Modifier
|
||||
.combinedClickable(
|
||||
@@ -70,7 +71,7 @@ private fun Feature.toFeatureInfo(color: Color, param: Int?, type: String): Feat
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun mergedFeatures(chatItem: ChatItem): List<FeatureInfo>? {
|
||||
private fun mergedFeatures(chatItem: ChatItem, chatInfo: ChatInfo): List<FeatureInfo>? {
|
||||
val m = ChatModel
|
||||
val fs: ArrayList<FeatureInfo> = arrayListOf()
|
||||
val icons: MutableSet<PainterBox> = mutableSetOf()
|
||||
@@ -78,7 +79,7 @@ private fun mergedFeatures(chatItem: ChatItem): List<FeatureInfo>? {
|
||||
if (i != null) {
|
||||
val reversedChatItems = m.chatItems.asReversed()
|
||||
while (i < reversedChatItems.size) {
|
||||
val f = featureInfo(reversedChatItems[i]) ?: break
|
||||
val f = featureInfo(reversedChatItems[i], chatInfo) ?: break
|
||||
if (!icons.contains(f.icon)) {
|
||||
fs.add(0, f)
|
||||
icons.add(f.icon)
|
||||
@@ -90,12 +91,12 @@ private fun mergedFeatures(chatItem: ChatItem): List<FeatureInfo>? {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun featureInfo(ci: ChatItem): FeatureInfo? =
|
||||
private fun featureInfo(ci: ChatItem, chatInfo: ChatInfo): FeatureInfo? =
|
||||
when (ci.content) {
|
||||
is CIContent.RcvChatFeature -> ci.content.feature.toFeatureInfo(ci.content.enabled.iconColor, ci.content.param, ci.content.feature.name)
|
||||
is CIContent.SndChatFeature -> ci.content.feature.toFeatureInfo(ci.content.enabled.iconColor, ci.content.param, ci.content.feature.name)
|
||||
is CIContent.RcvGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enable.iconColor, ci.content.param, ci.content.groupFeature.name)
|
||||
is CIContent.SndGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enable.iconColor, ci.content.param, ci.content.groupFeature.name)
|
||||
is CIContent.RcvGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enabled(ci.content.memberRole_, (chatInfo as ChatInfo.Group).groupInfo.membership).iconColor, ci.content.param, ci.content.groupFeature.name)
|
||||
is CIContent.SndGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enabled(ci.content.memberRole_, (chatInfo as ChatInfo.Group).groupInfo.membership).iconColor, ci.content.param, ci.content.groupFeature.name)
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -442,11 +442,11 @@ fun ChatItemView(
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvChatFeature -> {
|
||||
CIChatFeatureView(cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.SndChatFeature -> {
|
||||
CIChatFeatureView(cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvChatPreference -> {
|
||||
@@ -454,23 +454,23 @@ fun ChatItemView(
|
||||
CIFeaturePreferenceView(cItem, ct, c.feature, c.allowed, acceptFeature)
|
||||
}
|
||||
is CIContent.SndChatPreference -> {
|
||||
CIChatFeatureView(cItem, c.feature, MaterialTheme.colors.secondary, icon = c.feature.icon, revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, MaterialTheme.colors.secondary, icon = c.feature.icon, revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvGroupFeature -> {
|
||||
CIChatFeatureView(cItem, c.groupFeature, c.preference.enable.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as ChatInfo.Group).groupInfo.membership).iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.SndGroupFeature -> {
|
||||
CIChatFeatureView(cItem, c.groupFeature, c.preference.enable.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as ChatInfo.Group).groupInfo.membership).iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvChatFeatureRejected -> {
|
||||
CIChatFeatureView(cItem, c.feature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvGroupFeatureRejected -> {
|
||||
CIChatFeatureView(cItem, c.groupFeature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.groupFeature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.SndModerated -> DeletedItem()
|
||||
|
||||
+2
-2
@@ -254,12 +254,12 @@ fun TextIconSpaced(extraPadding: Boolean = false) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color? = null) {
|
||||
fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color? = null, textColor: Color = MaterialTheme.colors.onBackground) {
|
||||
SectionItemViewSpaceBetween {
|
||||
Row {
|
||||
val iconSize = with(LocalDensity.current) { 21.sp.toDp() }
|
||||
if (icon != null) Icon(icon, title, Modifier.padding(end = 8.dp).size(iconSize), tint = iconTint ?: MaterialTheme.colors.secondary)
|
||||
Text(title)
|
||||
Text(title, color = textColor)
|
||||
}
|
||||
Text(value, color = MaterialTheme.colors.secondary)
|
||||
}
|
||||
|
||||
@@ -348,6 +348,9 @@
|
||||
<string name="files_and_media_prohibited">Files and media prohibited!</string>
|
||||
<string name="only_owners_can_enable_files_and_media">Only group owners can enable files and media.</string>
|
||||
<string name="compose_send_direct_message_to_connect">Send direct message to connect</string>
|
||||
<string name="simplex_links_not_allowed">SimpleX links not allowed</string>
|
||||
<string name="files_and_media_not_allowed">Files and media not allowed</string>
|
||||
<string name="voice_messages_not_allowed">Voice messages not allowed</string>
|
||||
|
||||
<!-- Images - chat.simplex.app.views.chat.item.CIImageView.kt -->
|
||||
<string name="image_descr">Image</string>
|
||||
@@ -1532,6 +1535,7 @@
|
||||
<string name="message_reactions">Message reactions</string>
|
||||
<string name="voice_messages">Voice messages</string>
|
||||
<string name="files_and_media">Files and media</string>
|
||||
<string name="simplex_links">SimpleX links</string>
|
||||
<string name="recent_history">Visible history</string>
|
||||
<string name="audio_video_calls">Audio/video calls</string>
|
||||
<string name="available_in_v51">\nAvailable in v5.1</string>
|
||||
@@ -1589,6 +1593,8 @@
|
||||
<string name="prohibit_message_reactions_group">Prohibit messages reactions.</string>
|
||||
<string name="allow_to_send_files">Allow to send files and media.</string>
|
||||
<string name="prohibit_sending_files">Prohibit sending files and media.</string>
|
||||
<string name="allow_to_send_simplex_links">Allow to send SimpleX links.</string>
|
||||
<string name="prohibit_sending_simplex_links">Prohibit sending SimpleX links</string>
|
||||
<string name="enable_sending_recent_history">Send up to 100 last messages to new members.</string>
|
||||
<string name="disable_sending_recent_history">Do not send history to new members.</string>
|
||||
<string name="group_members_can_send_disappearing">Group members can send disappearing messages.</string>
|
||||
@@ -1603,6 +1609,8 @@
|
||||
<string name="message_reactions_are_prohibited">Message reactions are prohibited in this group.</string>
|
||||
<string name="group_members_can_send_files">Group members can send files and media.</string>
|
||||
<string name="files_are_prohibited_in_group">Files and media are prohibited in this group.</string>
|
||||
<string name="group_members_can_send_simplex_links">Group members can send SimpleX links.</string>
|
||||
<string name="simplex_links_are_prohibited_in_group">SimpleX links are prohibited in this group.</string>
|
||||
<string name="recent_history_is_sent_to_new_members">Up to 100 last messages are sent to new members.</string>
|
||||
<string name="recent_history_is_not_sent_to_new_members">History is not sent to new members.</string>
|
||||
<string name="delete_after">Delete after</string>
|
||||
@@ -1625,6 +1633,10 @@
|
||||
<string name="feature_offered_item">offered %s</string>
|
||||
<string name="feature_offered_item_with_param">offered %s: %2s</string>
|
||||
<string name="feature_cancelled_item">cancelled %s</string>
|
||||
<string name="feature_roles_all_members">all members</string>
|
||||
<string name="feature_roles_admins">admins</string>
|
||||
<string name="feature_roles_owners">owners</string>
|
||||
<string name="feature_enabled_for">Enabled for</string>
|
||||
|
||||
<!-- WhatsNewView.kt -->
|
||||
<string name="whats_new">What\'s new</string>
|
||||
|
||||
Reference in New Issue
Block a user