mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-28 20:29:53 +00:00
ui: label in compose when user cannot send messages (#5922)
* ui: label in compose when user cannot send messages * gray buttons when user cannot send messages * improve * kotlin * fix order * fix alert --------- Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
This commit is contained in:
parent
26e5742354
commit
7b362ff655
14 changed files with 164 additions and 128 deletions
|
@ -1152,27 +1152,6 @@ final class Chat: ObservableObject, Identifiable, ChatLike {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var userCanSend: Bool {
|
|
||||||
switch chatInfo {
|
|
||||||
case .direct: return true
|
|
||||||
case let .group(groupInfo):
|
|
||||||
let m = groupInfo.membership
|
|
||||||
return m.memberActive && m.memberRole >= .member
|
|
||||||
case .local:
|
|
||||||
return true
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var userIsObserver: Bool {
|
|
||||||
switch chatInfo {
|
|
||||||
case let .group(groupInfo):
|
|
||||||
let m = groupInfo.membership
|
|
||||||
return m.memberActive && m.memberRole == .observer
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var unreadTag: Bool {
|
var unreadTag: Bool {
|
||||||
switch chatInfo.chatSettings?.enableNtfs {
|
switch chatInfo.chatSettings?.enableNtfs {
|
||||||
case .all: chatStats.unreadChat || chatStats.unreadCount > 0
|
case .all: chatStats.unreadChat || chatStats.unreadCount > 0
|
||||||
|
|
|
@ -98,14 +98,24 @@ struct ChatView: View {
|
||||||
}
|
}
|
||||||
connectingText()
|
connectingText()
|
||||||
if selectedChatItems == nil {
|
if selectedChatItems == nil {
|
||||||
|
let reason = chat.chatInfo.userCantSendReason
|
||||||
ComposeView(
|
ComposeView(
|
||||||
chat: chat,
|
chat: chat,
|
||||||
composeState: $composeState,
|
composeState: $composeState,
|
||||||
keyboardVisible: $keyboardVisible,
|
keyboardVisible: $keyboardVisible,
|
||||||
keyboardHiddenDate: $keyboardHiddenDate,
|
keyboardHiddenDate: $keyboardHiddenDate,
|
||||||
selectedRange: $selectedRange
|
selectedRange: $selectedRange,
|
||||||
|
disabledText: reason?.composeLabel
|
||||||
)
|
)
|
||||||
.disabled(!cInfo.sendMsgEnabled)
|
.disabled(!cInfo.sendMsgEnabled)
|
||||||
|
.if(!cInfo.sendMsgEnabled) { v in
|
||||||
|
v.disabled(true).onTapGesture {
|
||||||
|
AlertManager.shared.showAlertMsg(
|
||||||
|
title: "You can't send messages!",
|
||||||
|
message: reason?.alertMessage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
SelectedItemsBottomToolbar(
|
SelectedItemsBottomToolbar(
|
||||||
chatItems: ItemsModel.shared.reversedChatItems,
|
chatItems: ItemsModel.shared.reversedChatItems,
|
||||||
|
|
|
@ -327,6 +327,7 @@ struct ComposeView: View {
|
||||||
@Binding var keyboardVisible: Bool
|
@Binding var keyboardVisible: Bool
|
||||||
@Binding var keyboardHiddenDate: Date
|
@Binding var keyboardHiddenDate: Date
|
||||||
@Binding var selectedRange: NSRange
|
@Binding var selectedRange: NSRange
|
||||||
|
var disabledText: LocalizedStringKey? = nil
|
||||||
|
|
||||||
@State var linkUrl: URL? = nil
|
@State var linkUrl: URL? = nil
|
||||||
@State var hasSimplexLink: Bool = false
|
@State var hasSimplexLink: Bool = false
|
||||||
|
@ -391,7 +392,7 @@ struct ComposeView: View {
|
||||||
Image(systemName: "paperclip")
|
Image(systemName: "paperclip")
|
||||||
.resizable()
|
.resizable()
|
||||||
}
|
}
|
||||||
.disabled(composeState.attachmentDisabled || !chat.userCanSend || (chat.chatInfo.contact?.nextSendGrpInv ?? false))
|
.disabled(composeState.attachmentDisabled || !chat.chatInfo.sendMsgEnabled || (chat.chatInfo.contact?.nextSendGrpInv ?? false))
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
.padding(.bottom, 16)
|
.padding(.bottom, 16)
|
||||||
.padding(.leading, 12)
|
.padding(.leading, 12)
|
||||||
|
@ -441,19 +442,13 @@ struct ComposeView: View {
|
||||||
: theme.colors.primary
|
: theme.colors.primary
|
||||||
)
|
)
|
||||||
.padding(.trailing, 12)
|
.padding(.trailing, 12)
|
||||||
.disabled(!chat.userCanSend)
|
.disabled(!chat.chatInfo.sendMsgEnabled)
|
||||||
|
|
||||||
if chat.userIsObserver {
|
if let disabledText {
|
||||||
Text("you are observer")
|
Text(disabledText)
|
||||||
.italic()
|
.italic()
|
||||||
.foregroundColor(theme.colors.secondary)
|
.foregroundColor(theme.colors.secondary)
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.onTapGesture {
|
|
||||||
AlertManager.shared.showAlertMsg(
|
|
||||||
title: "You can't send messages!",
|
|
||||||
message: "Please contact group admin."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,8 +474,8 @@ struct ComposeView: View {
|
||||||
hasSimplexLink = false
|
hasSimplexLink = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: chat.userCanSend) { canSend in
|
.onChange(of: chat.chatInfo.sendMsgEnabled) { sendEnabled in
|
||||||
if !canSend {
|
if !sendEnabled {
|
||||||
cancelCurrentVoiceRecording()
|
cancelCurrentVoiceRecording()
|
||||||
clearCurrentDraft()
|
clearCurrentDraft()
|
||||||
clearState()
|
clearState()
|
||||||
|
|
|
@ -15,6 +15,7 @@ struct SendMessageView: View {
|
||||||
@Binding var composeState: ComposeState
|
@Binding var composeState: ComposeState
|
||||||
@Binding var selectedRange: NSRange
|
@Binding var selectedRange: NSRange
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
|
@Environment(\.isEnabled) var isEnabled
|
||||||
var sendMessage: (Int?) -> Void
|
var sendMessage: (Int?) -> Void
|
||||||
var sendLiveMessage: (() async -> Void)? = nil
|
var sendLiveMessage: (() async -> Void)? = nil
|
||||||
var updateLiveMessage: (() async -> Void)? = nil
|
var updateLiveMessage: (() async -> Void)? = nil
|
||||||
|
@ -255,6 +256,7 @@ struct SendMessageView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct RecordVoiceMessageButton: View {
|
private struct RecordVoiceMessageButton: View {
|
||||||
|
@Environment(\.isEnabled) var isEnabled
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
var startVoiceMessageRecording: (() -> Void)?
|
var startVoiceMessageRecording: (() -> Void)?
|
||||||
var finishVoiceMessageRecording: (() -> Void)?
|
var finishVoiceMessageRecording: (() -> Void)?
|
||||||
|
@ -263,11 +265,11 @@ struct SendMessageView: View {
|
||||||
@State private var pressed: TimeInterval? = nil
|
@State private var pressed: TimeInterval? = nil
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Image(systemName: "mic.fill")
|
Image(systemName: isEnabled ? "mic.fill" : "mic")
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: 20, height: 20)
|
.frame(width: 20, height: 20)
|
||||||
.foregroundColor(theme.colors.primary)
|
.foregroundColor(isEnabled ? theme.colors.primary : theme.colors.secondary)
|
||||||
.opacity(holdingVMR ? 0.7 : 1)
|
.opacity(holdingVMR ? 0.7 : 1)
|
||||||
.disabled(disabled)
|
.disabled(disabled)
|
||||||
.frame(width: 31, height: 31)
|
.frame(width: 31, height: 31)
|
||||||
|
@ -352,7 +354,7 @@ struct SendMessageView: View {
|
||||||
Image(systemName: "bolt.fill")
|
Image(systemName: "bolt.fill")
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.foregroundColor(theme.colors.primary)
|
.foregroundColor(isEnabled ? theme.colors.primary : theme.colors.secondary)
|
||||||
.frame(width: 20, height: 20)
|
.frame(width: 20, height: 20)
|
||||||
}
|
}
|
||||||
.frame(width: 29, height: 29)
|
.frame(width: 29, height: 29)
|
||||||
|
|
|
@ -1333,6 +1333,19 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? {
|
||||||
|
get {
|
||||||
|
switch self {
|
||||||
|
case let .direct(contact): return contact.userCantSendReason
|
||||||
|
case let .group(groupInfo): return groupInfo.userCantSendReason
|
||||||
|
case let .local(noteFolder): return noteFolder.userCantSendReason
|
||||||
|
case let .contactRequest(contactRequest): return contactRequest.userCantSendReason
|
||||||
|
case let .contactConnection(contactConnection): return contactConnection.userCantSendReason
|
||||||
|
case .invalidJSON: return ("can't send messages", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var sendMsgEnabled: Bool {
|
public var sendMsgEnabled: Bool {
|
||||||
get {
|
get {
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -1642,15 +1655,16 @@ public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
|
||||||
public var ready: Bool { get { activeConn?.connStatus == .ready } }
|
public var ready: Bool { get { activeConn?.connStatus == .ready } }
|
||||||
public var sndReady: Bool { get { ready || activeConn?.connStatus == .sndReady } }
|
public var sndReady: Bool { get { ready || activeConn?.connStatus == .sndReady } }
|
||||||
public var active: Bool { get { contactStatus == .active } }
|
public var active: Bool { get { contactStatus == .active } }
|
||||||
public var sendMsgEnabled: Bool { get {
|
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? {
|
||||||
(
|
// TODO [short links] this will have additional statuses for pending contact requests before they are accepted
|
||||||
sndReady
|
if nextSendGrpInv { return nil }
|
||||||
&& active
|
if !active { return ("contact deleted", nil) }
|
||||||
&& !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false)
|
if !sndReady { return ("contact not ready", nil) }
|
||||||
&& !(activeConn?.connDisabled ?? true)
|
if activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false { return ("not synchronized", nil) }
|
||||||
)
|
if activeConn?.connDisabled ?? true { return ("contact disabled", nil) }
|
||||||
|| nextSendGrpInv
|
return nil
|
||||||
} }
|
}
|
||||||
|
public var sendMsgEnabled: Bool { userCantSendReason == nil }
|
||||||
public var nextSendGrpInv: Bool { get { contactGroupMemberId != nil && !contactGrpInvSent } }
|
public var nextSendGrpInv: Bool { get { contactGroupMemberId != nil && !contactGrpInvSent } }
|
||||||
public var displayName: String { localAlias == "" ? profile.displayName : localAlias }
|
public var displayName: String { localAlias == "" ? profile.displayName : localAlias }
|
||||||
public var fullName: String { get { profile.fullName } }
|
public var fullName: String { get { profile.fullName } }
|
||||||
|
@ -1829,6 +1843,7 @@ public struct UserContactRequest: Decodable, NamedChat, Hashable {
|
||||||
public var id: ChatId { get { "<@\(contactRequestId)" } }
|
public var id: ChatId { get { "<@\(contactRequestId)" } }
|
||||||
public var apiId: Int64 { get { contactRequestId } }
|
public var apiId: Int64 { get { contactRequestId } }
|
||||||
var ready: Bool { get { true } }
|
var ready: Bool { get { true } }
|
||||||
|
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { ("can't send messages", nil) }
|
||||||
public var sendMsgEnabled: Bool { get { false } }
|
public var sendMsgEnabled: Bool { get { false } }
|
||||||
public var displayName: String { get { profile.displayName } }
|
public var displayName: String { get { profile.displayName } }
|
||||||
public var fullName: String { get { profile.fullName } }
|
public var fullName: String { get { profile.fullName } }
|
||||||
|
@ -1861,6 +1876,7 @@ public struct PendingContactConnection: Decodable, NamedChat, Hashable {
|
||||||
public var id: ChatId { get { ":\(pccConnId)" } }
|
public var id: ChatId { get { ":\(pccConnId)" } }
|
||||||
public var apiId: Int64 { get { pccConnId } }
|
public var apiId: Int64 { get { pccConnId } }
|
||||||
var ready: Bool { get { false } }
|
var ready: Bool { get { false } }
|
||||||
|
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { ("can't send messages", nil) }
|
||||||
public var sendMsgEnabled: Bool { get { false } }
|
public var sendMsgEnabled: Bool { get { false } }
|
||||||
var localDisplayName: String {
|
var localDisplayName: String {
|
||||||
get { String.localizedStringWithFormat(NSLocalizedString("connection:%@", comment: "connection information"), pccConnId) }
|
get { String.localizedStringWithFormat(NSLocalizedString("connection:%@", comment: "connection information"), pccConnId) }
|
||||||
|
@ -1990,7 +2006,20 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable {
|
||||||
public var id: ChatId { get { "#\(groupId)" } }
|
public var id: ChatId { get { "#\(groupId)" } }
|
||||||
public var apiId: Int64 { get { groupId } }
|
public var apiId: Int64 { get { groupId } }
|
||||||
public var ready: Bool { get { true } }
|
public var ready: Bool { get { true } }
|
||||||
public var sendMsgEnabled: Bool { get { membership.memberActive } }
|
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? {
|
||||||
|
return if membership.memberActive {
|
||||||
|
membership.memberRole == .observer ? ("you are observer", "Please contact group admin.") : nil
|
||||||
|
} else {
|
||||||
|
switch membership.memberStatus {
|
||||||
|
case .memRejected: ("request to join rejected", nil)
|
||||||
|
case .memGroupDeleted: ("group is deleted", nil)
|
||||||
|
case .memRemoved: ("removed from group", nil)
|
||||||
|
case .memLeft: ("you left", nil)
|
||||||
|
default: ("can't send messages", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public var sendMsgEnabled: Bool { userCantSendReason == nil }
|
||||||
public var displayName: String { localAlias == "" ? groupProfile.displayName : localAlias }
|
public var displayName: String { localAlias == "" ? groupProfile.displayName : localAlias }
|
||||||
public var fullName: String { get { groupProfile.fullName } }
|
public var fullName: String { get { groupProfile.fullName } }
|
||||||
public var image: String? { get { groupProfile.image } }
|
public var image: String? { get { groupProfile.image } }
|
||||||
|
@ -2357,6 +2386,7 @@ public struct NoteFolder: Identifiable, Decodable, NamedChat, Hashable {
|
||||||
public var id: ChatId { get { "*\(noteFolderId)" } }
|
public var id: ChatId { get { "*\(noteFolderId)" } }
|
||||||
public var apiId: Int64 { get { noteFolderId } }
|
public var apiId: Int64 { get { noteFolderId } }
|
||||||
public var ready: Bool { get { true } }
|
public var ready: Bool { get { true } }
|
||||||
|
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { nil }
|
||||||
public var sendMsgEnabled: Bool { get { true } }
|
public var sendMsgEnabled: Bool { get { true } }
|
||||||
public var displayName: String { get { ChatInfo.privateNotesChatName } }
|
public var displayName: String { get { ChatInfo.privateNotesChatName } }
|
||||||
public var fullName: String { get { "" } }
|
public var fullName: String { get { "" } }
|
||||||
|
|
|
@ -42,7 +42,6 @@ import chat.simplex.common.views.helpers.*
|
||||||
import chat.simplex.res.MR
|
import chat.simplex.res.MR
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
@ -51,10 +50,10 @@ import java.net.URI
|
||||||
actual fun PlatformTextField(
|
actual fun PlatformTextField(
|
||||||
composeState: MutableState<ComposeState>,
|
composeState: MutableState<ComposeState>,
|
||||||
sendMsgEnabled: Boolean,
|
sendMsgEnabled: Boolean,
|
||||||
|
disabledText: String?,
|
||||||
sendMsgButtonDisabled: Boolean,
|
sendMsgButtonDisabled: Boolean,
|
||||||
textStyle: MutableState<TextStyle>,
|
textStyle: MutableState<TextStyle>,
|
||||||
showDeleteTextButton: MutableState<Boolean>,
|
showDeleteTextButton: MutableState<Boolean>,
|
||||||
userIsObserver: Boolean,
|
|
||||||
placeholder: String,
|
placeholder: String,
|
||||||
showVoiceButton: Boolean,
|
showVoiceButton: Boolean,
|
||||||
onMessageChange: (ComposeMessage) -> Unit,
|
onMessageChange: (ComposeMessage) -> Unit,
|
||||||
|
@ -197,16 +196,16 @@ actual fun PlatformTextField(
|
||||||
showDeleteTextButton.value = it.lineCount >= 4 && !cs.inProgress
|
showDeleteTextButton.value = it.lineCount >= 4 && !cs.inProgress
|
||||||
}
|
}
|
||||||
if (composeState.value.preview is ComposePreview.VoicePreview) {
|
if (composeState.value.preview is ComposePreview.VoicePreview) {
|
||||||
ComposeOverlay(MR.strings.voice_message_send_text, textStyle, padding)
|
ComposeOverlay(generalGetString(MR.strings.voice_message_send_text), textStyle, padding)
|
||||||
} else if (userIsObserver) {
|
} else if (disabledText != null) {
|
||||||
ComposeOverlay(MR.strings.you_are_observer, textStyle, padding)
|
ComposeOverlay(disabledText, textStyle, padding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ComposeOverlay(textId: StringResource, textStyle: MutableState<TextStyle>, padding: PaddingValues) {
|
private fun ComposeOverlay(text: String, textStyle: MutableState<TextStyle>, padding: PaddingValues) {
|
||||||
Text(
|
Text(
|
||||||
generalGetString(textId),
|
text,
|
||||||
Modifier.padding(padding),
|
Modifier.padding(padding),
|
||||||
color = MaterialTheme.colors.secondary,
|
color = MaterialTheme.colors.secondary,
|
||||||
style = textStyle.value.copy(fontStyle = FontStyle.Italic)
|
style = textStyle.value.copy(fontStyle = FontStyle.Italic)
|
||||||
|
|
|
@ -1204,6 +1204,7 @@ interface SomeChat {
|
||||||
val apiId: Long
|
val apiId: Long
|
||||||
val ready: Boolean
|
val ready: Boolean
|
||||||
val chatDeleted: Boolean
|
val chatDeleted: Boolean
|
||||||
|
val userCantSendReason: Pair<String, String?>?
|
||||||
val sendMsgEnabled: Boolean
|
val sendMsgEnabled: Boolean
|
||||||
val incognito: Boolean
|
val incognito: Boolean
|
||||||
fun featureEnabled(feature: ChatFeature): Boolean
|
fun featureEnabled(feature: ChatFeature): Boolean
|
||||||
|
@ -1228,14 +1229,6 @@ data class Chat(
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
val userIsObserver: Boolean get() = when(chatInfo) {
|
|
||||||
is ChatInfo.Group -> {
|
|
||||||
val m = chatInfo.groupInfo.membership
|
|
||||||
m.memberActive && m.memberRole == GroupMemberRole.Observer
|
|
||||||
}
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
val unreadTag: Boolean get() = when (chatInfo.chatSettings?.enableNtfs) {
|
val unreadTag: Boolean get() = when (chatInfo.chatSettings?.enableNtfs) {
|
||||||
All -> chatStats.unreadChat || chatStats.unreadCount > 0
|
All -> chatStats.unreadChat || chatStats.unreadCount > 0
|
||||||
Mentions -> chatStats.unreadChat || chatStats.unreadMentions > 0
|
Mentions -> chatStats.unreadChat || chatStats.unreadMentions > 0
|
||||||
|
@ -1282,6 +1275,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||||
override val apiId get() = contact.apiId
|
override val apiId get() = contact.apiId
|
||||||
override val ready get() = contact.ready
|
override val ready get() = contact.ready
|
||||||
override val chatDeleted get() = contact.chatDeleted
|
override val chatDeleted get() = contact.chatDeleted
|
||||||
|
override val userCantSendReason get() = contact.userCantSendReason
|
||||||
override val sendMsgEnabled get() = contact.sendMsgEnabled
|
override val sendMsgEnabled get() = contact.sendMsgEnabled
|
||||||
override val incognito get() = contact.incognito
|
override val incognito get() = contact.incognito
|
||||||
override fun featureEnabled(feature: ChatFeature) = contact.featureEnabled(feature)
|
override fun featureEnabled(feature: ChatFeature) = contact.featureEnabled(feature)
|
||||||
|
@ -1307,6 +1301,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||||
override val apiId get() = groupInfo.apiId
|
override val apiId get() = groupInfo.apiId
|
||||||
override val ready get() = groupInfo.ready
|
override val ready get() = groupInfo.ready
|
||||||
override val chatDeleted get() = groupInfo.chatDeleted
|
override val chatDeleted get() = groupInfo.chatDeleted
|
||||||
|
override val userCantSendReason get() = groupInfo.userCantSendReason
|
||||||
override val sendMsgEnabled get() = groupInfo.sendMsgEnabled
|
override val sendMsgEnabled get() = groupInfo.sendMsgEnabled
|
||||||
override val incognito get() = groupInfo.incognito
|
override val incognito get() = groupInfo.incognito
|
||||||
override fun featureEnabled(feature: ChatFeature) = groupInfo.featureEnabled(feature)
|
override fun featureEnabled(feature: ChatFeature) = groupInfo.featureEnabled(feature)
|
||||||
|
@ -1331,6 +1326,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||||
override val apiId get() = noteFolder.apiId
|
override val apiId get() = noteFolder.apiId
|
||||||
override val ready get() = noteFolder.ready
|
override val ready get() = noteFolder.ready
|
||||||
override val chatDeleted get() = noteFolder.chatDeleted
|
override val chatDeleted get() = noteFolder.chatDeleted
|
||||||
|
override val userCantSendReason get() = noteFolder.userCantSendReason
|
||||||
override val sendMsgEnabled get() = noteFolder.sendMsgEnabled
|
override val sendMsgEnabled get() = noteFolder.sendMsgEnabled
|
||||||
override val incognito get() = noteFolder.incognito
|
override val incognito get() = noteFolder.incognito
|
||||||
override fun featureEnabled(feature: ChatFeature) = noteFolder.featureEnabled(feature)
|
override fun featureEnabled(feature: ChatFeature) = noteFolder.featureEnabled(feature)
|
||||||
|
@ -1355,6 +1351,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||||
override val apiId get() = contactRequest.apiId
|
override val apiId get() = contactRequest.apiId
|
||||||
override val ready get() = contactRequest.ready
|
override val ready get() = contactRequest.ready
|
||||||
override val chatDeleted get() = contactRequest.chatDeleted
|
override val chatDeleted get() = contactRequest.chatDeleted
|
||||||
|
override val userCantSendReason get() = contactRequest.userCantSendReason
|
||||||
override val sendMsgEnabled get() = contactRequest.sendMsgEnabled
|
override val sendMsgEnabled get() = contactRequest.sendMsgEnabled
|
||||||
override val incognito get() = contactRequest.incognito
|
override val incognito get() = contactRequest.incognito
|
||||||
override fun featureEnabled(feature: ChatFeature) = contactRequest.featureEnabled(feature)
|
override fun featureEnabled(feature: ChatFeature) = contactRequest.featureEnabled(feature)
|
||||||
|
@ -1379,6 +1376,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||||
override val apiId get() = contactConnection.apiId
|
override val apiId get() = contactConnection.apiId
|
||||||
override val ready get() = contactConnection.ready
|
override val ready get() = contactConnection.ready
|
||||||
override val chatDeleted get() = contactConnection.chatDeleted
|
override val chatDeleted get() = contactConnection.chatDeleted
|
||||||
|
override val userCantSendReason get() = contactConnection.userCantSendReason
|
||||||
override val sendMsgEnabled get() = contactConnection.sendMsgEnabled
|
override val sendMsgEnabled get() = contactConnection.sendMsgEnabled
|
||||||
override val incognito get() = contactConnection.incognito
|
override val incognito get() = contactConnection.incognito
|
||||||
override fun featureEnabled(feature: ChatFeature) = contactConnection.featureEnabled(feature)
|
override fun featureEnabled(feature: ChatFeature) = contactConnection.featureEnabled(feature)
|
||||||
|
@ -1408,6 +1406,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||||
override val id get() = "?$apiId"
|
override val id get() = "?$apiId"
|
||||||
override val ready get() = false
|
override val ready get() = false
|
||||||
override val chatDeleted get() = false
|
override val chatDeleted get() = false
|
||||||
|
override val userCantSendReason get() = generalGetString(MR.strings.cant_send_message_generic) to null
|
||||||
override val sendMsgEnabled get() = false
|
override val sendMsgEnabled get() = false
|
||||||
override val incognito get() = false
|
override val incognito get() = false
|
||||||
override fun featureEnabled(feature: ChatFeature) = false
|
override fun featureEnabled(feature: ChatFeature) = false
|
||||||
|
@ -1450,14 +1449,6 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||||
is InvalidJSON -> updatedAt
|
is InvalidJSON -> updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
val userCanSend: Boolean
|
|
||||||
get() = when (this) {
|
|
||||||
is ChatInfo.Direct -> true
|
|
||||||
is ChatInfo.Group -> groupInfo.membership.memberRole >= GroupMemberRole.Member
|
|
||||||
is ChatInfo.Local -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
val chatTags: List<Long>?
|
val chatTags: List<Long>?
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
is Direct -> contact.chatTags
|
is Direct -> contact.chatTags
|
||||||
|
@ -1528,13 +1519,17 @@ data class Contact(
|
||||||
override val ready get() = activeConn?.connStatus == ConnStatus.Ready
|
override val ready get() = activeConn?.connStatus == ConnStatus.Ready
|
||||||
val sndReady get() = ready || activeConn?.connStatus == ConnStatus.SndReady
|
val sndReady get() = ready || activeConn?.connStatus == ConnStatus.SndReady
|
||||||
val active get() = contactStatus == ContactStatus.Active
|
val active get() = contactStatus == ContactStatus.Active
|
||||||
override val sendMsgEnabled get() = (
|
override val userCantSendReason: Pair<String, String?>?
|
||||||
sndReady
|
get() {
|
||||||
&& active
|
// TODO [short links] this will have additional statuses for pending contact requests before they are accepted
|
||||||
&& !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?: false)
|
if (nextSendGrpInv) return null
|
||||||
&& !(activeConn?.connDisabled ?: true)
|
if (!active) return generalGetString(MR.strings.cant_send_message_contact_deleted) to null
|
||||||
)
|
if (!sndReady) return generalGetString(MR.strings.cant_send_message_contact_not_ready) to null
|
||||||
|| nextSendGrpInv
|
if (activeConn?.connectionStats?.ratchetSyncSendProhibited == true) return generalGetString(MR.strings.cant_send_message_contact_not_synchronized) to null
|
||||||
|
if (activeConn?.connDisabled == true) return generalGetString(MR.strings.cant_send_message_contact_disabled) to null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
override val sendMsgEnabled get() = userCantSendReason == null
|
||||||
val nextSendGrpInv get() = contactGroupMemberId != null && !contactGrpInvSent
|
val nextSendGrpInv get() = contactGroupMemberId != null && !contactGrpInvSent
|
||||||
override val incognito get() = contactConnIncognito
|
override val incognito get() = contactConnIncognito
|
||||||
override fun featureEnabled(feature: ChatFeature) = when (feature) {
|
override fun featureEnabled(feature: ChatFeature) = when (feature) {
|
||||||
|
@ -1768,7 +1763,23 @@ data class GroupInfo (
|
||||||
override val apiId get() = groupId
|
override val apiId get() = groupId
|
||||||
override val ready get() = membership.memberActive
|
override val ready get() = membership.memberActive
|
||||||
override val chatDeleted get() = false
|
override val chatDeleted get() = false
|
||||||
override val sendMsgEnabled get() = membership.memberActive
|
override val userCantSendReason: Pair<String, String?>? get() =
|
||||||
|
if (membership.memberActive) {
|
||||||
|
if (membership.memberRole == GroupMemberRole.Observer) {
|
||||||
|
generalGetString(MR.strings.observer_cant_send_message_title) to generalGetString(MR.strings.observer_cant_send_message_desc)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when (membership.memberStatus) {
|
||||||
|
GroupMemberStatus.MemRejected -> generalGetString(MR.strings.cant_send_message_rejected) to null
|
||||||
|
GroupMemberStatus.MemGroupDeleted -> generalGetString(MR.strings.cant_send_message_group_deleted) to null
|
||||||
|
GroupMemberStatus.MemRemoved -> generalGetString(MR.strings.cant_send_message_mem_removed) to null
|
||||||
|
GroupMemberStatus.MemLeft -> generalGetString(MR.strings.cant_send_message_you_left) to null
|
||||||
|
else -> generalGetString(MR.strings.cant_send_message_generic) to null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override val sendMsgEnabled get() = userCantSendReason == null
|
||||||
override val incognito get() = membership.memberIncognito
|
override val incognito get() = membership.memberIncognito
|
||||||
override fun featureEnabled(feature: ChatFeature) = when (feature) {
|
override fun featureEnabled(feature: ChatFeature) = when (feature) {
|
||||||
ChatFeature.TimedMessages -> fullGroupPreferences.timedMessages.on
|
ChatFeature.TimedMessages -> fullGroupPreferences.timedMessages.on
|
||||||
|
@ -2144,6 +2155,7 @@ class NoteFolder(
|
||||||
override val apiId get() = noteFolderId
|
override val apiId get() = noteFolderId
|
||||||
override val chatDeleted get() = false
|
override val chatDeleted get() = false
|
||||||
override val ready get() = true
|
override val ready get() = true
|
||||||
|
override val userCantSendReason: Pair<String, String?>? = null
|
||||||
override val sendMsgEnabled get() = true
|
override val sendMsgEnabled get() = true
|
||||||
override val incognito get() = false
|
override val incognito get() = false
|
||||||
override fun featureEnabled(feature: ChatFeature) = feature == ChatFeature.Voice
|
override fun featureEnabled(feature: ChatFeature) = feature == ChatFeature.Voice
|
||||||
|
@ -2180,6 +2192,7 @@ class UserContactRequest (
|
||||||
override val apiId get() = contactRequestId
|
override val apiId get() = contactRequestId
|
||||||
override val chatDeleted get() = false
|
override val chatDeleted get() = false
|
||||||
override val ready get() = true
|
override val ready get() = true
|
||||||
|
override val userCantSendReason = generalGetString(MR.strings.cant_send_message_generic) to null
|
||||||
override val sendMsgEnabled get() = false
|
override val sendMsgEnabled get() = false
|
||||||
override val incognito get() = false
|
override val incognito get() = false
|
||||||
override fun featureEnabled(feature: ChatFeature) = false
|
override fun featureEnabled(feature: ChatFeature) = false
|
||||||
|
@ -2219,6 +2232,7 @@ class PendingContactConnection(
|
||||||
override val apiId get() = pccConnId
|
override val apiId get() = pccConnId
|
||||||
override val chatDeleted get() = false
|
override val chatDeleted get() = false
|
||||||
override val ready get() = false
|
override val ready get() = false
|
||||||
|
override val userCantSendReason = generalGetString(MR.strings.cant_send_message_generic) to null
|
||||||
override val sendMsgEnabled get() = false
|
override val sendMsgEnabled get() = false
|
||||||
override val incognito get() = customUserProfileId != null
|
override val incognito get() = customUserProfileId != null
|
||||||
override fun featureEnabled(feature: ChatFeature) = false
|
override fun featureEnabled(feature: ChatFeature) = false
|
||||||
|
|
|
@ -12,10 +12,10 @@ import java.net.URI
|
||||||
expect fun PlatformTextField(
|
expect fun PlatformTextField(
|
||||||
composeState: MutableState<ComposeState>,
|
composeState: MutableState<ComposeState>,
|
||||||
sendMsgEnabled: Boolean,
|
sendMsgEnabled: Boolean,
|
||||||
|
disabledText: String?,
|
||||||
sendMsgButtonDisabled: Boolean,
|
sendMsgButtonDisabled: Boolean,
|
||||||
textStyle: MutableState<TextStyle>,
|
textStyle: MutableState<TextStyle>,
|
||||||
showDeleteTextButton: MutableState<Boolean>,
|
showDeleteTextButton: MutableState<Boolean>,
|
||||||
userIsObserver: Boolean,
|
|
||||||
placeholder: String,
|
placeholder: String,
|
||||||
showVoiceButton: Boolean,
|
showVoiceButton: Boolean,
|
||||||
onMessageChange: (ComposeMessage) -> Unit,
|
onMessageChange: (ComposeMessage) -> Unit,
|
||||||
|
|
|
@ -99,12 +99,11 @@ fun TerminalLayout(
|
||||||
isDirectChat = false,
|
isDirectChat = false,
|
||||||
liveMessageAlertShown = SharedPreference(get = { false }, set = {}),
|
liveMessageAlertShown = SharedPreference(get = { false }, set = {}),
|
||||||
sendMsgEnabled = true,
|
sendMsgEnabled = true,
|
||||||
|
userCantSendReason = null,
|
||||||
sendButtonEnabled = true,
|
sendButtonEnabled = true,
|
||||||
nextSendGrpInv = false,
|
nextSendGrpInv = false,
|
||||||
needToAllowVoiceToContact = false,
|
needToAllowVoiceToContact = false,
|
||||||
allowedVoiceByPrefs = false,
|
allowedVoiceByPrefs = false,
|
||||||
userIsObserver = false,
|
|
||||||
userCanSend = true,
|
|
||||||
allowVoiceToContact = {},
|
allowVoiceToContact = {},
|
||||||
placeholder = "",
|
placeholder = "",
|
||||||
sendMessage = { sendCommand() },
|
sendMessage = { sendCommand() },
|
||||||
|
|
|
@ -723,7 +723,7 @@ fun ChatLayout(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.desktopOnExternalDrag(
|
.desktopOnExternalDrag(
|
||||||
enabled = remember(attachmentDisabled.value, chatInfo.value?.userCanSend) { mutableStateOf(!attachmentDisabled.value && chatInfo.value?.userCanSend == true) }.value,
|
enabled = remember(attachmentDisabled.value, chatInfo.value?.sendMsgEnabled) { mutableStateOf(!attachmentDisabled.value && chatInfo.value?.sendMsgEnabled == true) }.value,
|
||||||
onFiles = { paths -> composeState.onFilesAttached(paths.map { it.toURI() }) },
|
onFiles = { paths -> composeState.onFilesAttached(paths.map { it.toURI() }) },
|
||||||
onImage = { file -> CoroutineScope(Dispatchers.IO).launch { composeState.processPickedMedia(listOf(file.toURI()), null) } },
|
onImage = { file -> CoroutineScope(Dispatchers.IO).launch { composeState.processPickedMedia(listOf(file.toURI()), null) } },
|
||||||
onText = {
|
onText = {
|
||||||
|
|
|
@ -999,9 +999,8 @@ fun ComposeView(
|
||||||
chatModel.sharedContent.value = null
|
chatModel.sharedContent.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
val userCanSend = rememberUpdatedState(chat.chatInfo.userCanSend)
|
|
||||||
val sendMsgEnabled = rememberUpdatedState(chat.chatInfo.sendMsgEnabled)
|
val sendMsgEnabled = rememberUpdatedState(chat.chatInfo.sendMsgEnabled)
|
||||||
val userIsObserver = rememberUpdatedState(chat.userIsObserver)
|
val userCantSendReason = rememberUpdatedState(chat.chatInfo.userCantSendReason)
|
||||||
val nextSendGrpInv = rememberUpdatedState(chat.nextSendGrpInv)
|
val nextSendGrpInv = rememberUpdatedState(chat.nextSendGrpInv)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
@ -1056,7 +1055,6 @@ fun ComposeView(
|
||||||
val attachmentEnabled =
|
val attachmentEnabled =
|
||||||
!composeState.value.attachmentDisabled
|
!composeState.value.attachmentDisabled
|
||||||
&& sendMsgEnabled.value
|
&& sendMsgEnabled.value
|
||||||
&& userCanSend.value
|
|
||||||
&& !isGroupAndProhibitedFiles
|
&& !isGroupAndProhibitedFiles
|
||||||
&& !nextSendGrpInv.value
|
&& !nextSendGrpInv.value
|
||||||
IconButton(
|
IconButton(
|
||||||
|
@ -1102,8 +1100,8 @@ fun ComposeView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(rememberUpdatedState(chat.chatInfo.userCanSend).value) {
|
LaunchedEffect(rememberUpdatedState(chat.chatInfo.sendMsgEnabled).value) {
|
||||||
if (!chat.chatInfo.userCanSend) {
|
if (!chat.chatInfo.sendMsgEnabled) {
|
||||||
clearCurrentDraft()
|
clearCurrentDraft()
|
||||||
clearState()
|
clearState()
|
||||||
}
|
}
|
||||||
|
@ -1159,13 +1157,12 @@ fun ComposeView(
|
||||||
chat.chatInfo is ChatInfo.Direct,
|
chat.chatInfo is ChatInfo.Direct,
|
||||||
liveMessageAlertShown = chatModel.controller.appPrefs.liveMessageAlertShown,
|
liveMessageAlertShown = chatModel.controller.appPrefs.liveMessageAlertShown,
|
||||||
sendMsgEnabled = sendMsgEnabled.value,
|
sendMsgEnabled = sendMsgEnabled.value,
|
||||||
|
userCantSendReason = userCantSendReason.value,
|
||||||
sendButtonEnabled = sendMsgEnabled.value && !(simplexLinkProhibited || fileProhibited || voiceProhibited),
|
sendButtonEnabled = sendMsgEnabled.value && !(simplexLinkProhibited || fileProhibited || voiceProhibited),
|
||||||
nextSendGrpInv = nextSendGrpInv.value,
|
nextSendGrpInv = nextSendGrpInv.value,
|
||||||
needToAllowVoiceToContact,
|
needToAllowVoiceToContact,
|
||||||
allowedVoiceByPrefs,
|
allowedVoiceByPrefs,
|
||||||
allowVoiceToContact = ::allowVoiceToContact,
|
allowVoiceToContact = ::allowVoiceToContact,
|
||||||
userIsObserver = userIsObserver.value,
|
|
||||||
userCanSend = userCanSend.value,
|
|
||||||
sendButtonColor = sendButtonColor,
|
sendButtonColor = sendButtonColor,
|
||||||
timedMessageAllowed = timedMessageAllowed,
|
timedMessageAllowed = timedMessageAllowed,
|
||||||
customDisappearingMessageTimePref = chatModel.controller.appPrefs.customDisappearingMessageTime,
|
customDisappearingMessageTimePref = chatModel.controller.appPrefs.customDisappearingMessageTime,
|
||||||
|
|
|
@ -15,9 +15,7 @@ import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.graphics.*
|
import androidx.compose.ui.graphics.*
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.text.TextRange
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.unit.*
|
||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
|
@ -41,12 +39,11 @@ fun SendMsgView(
|
||||||
isDirectChat: Boolean,
|
isDirectChat: Boolean,
|
||||||
liveMessageAlertShown: SharedPreference<Boolean>,
|
liveMessageAlertShown: SharedPreference<Boolean>,
|
||||||
sendMsgEnabled: Boolean,
|
sendMsgEnabled: Boolean,
|
||||||
|
userCantSendReason: Pair<String, String?>?,
|
||||||
sendButtonEnabled: Boolean,
|
sendButtonEnabled: Boolean,
|
||||||
nextSendGrpInv: Boolean,
|
nextSendGrpInv: Boolean,
|
||||||
needToAllowVoiceToContact: Boolean,
|
needToAllowVoiceToContact: Boolean,
|
||||||
allowedVoiceByPrefs: Boolean,
|
allowedVoiceByPrefs: Boolean,
|
||||||
userIsObserver: Boolean,
|
|
||||||
userCanSend: Boolean,
|
|
||||||
sendButtonColor: Color = MaterialTheme.colors.primary,
|
sendButtonColor: Color = MaterialTheme.colors.primary,
|
||||||
allowVoiceToContact: () -> Unit,
|
allowVoiceToContact: () -> Unit,
|
||||||
timedMessageAllowed: Boolean = false,
|
timedMessageAllowed: Boolean = false,
|
||||||
|
@ -82,14 +79,14 @@ fun SendMsgView(
|
||||||
(!allowedVoiceByPrefs && cs.preview is ComposePreview.VoicePreview) ||
|
(!allowedVoiceByPrefs && cs.preview is ComposePreview.VoicePreview) ||
|
||||||
cs.endLiveDisabled ||
|
cs.endLiveDisabled ||
|
||||||
!sendButtonEnabled
|
!sendButtonEnabled
|
||||||
val clicksOnTextFieldDisabled = !sendMsgEnabled || cs.preview is ComposePreview.VoicePreview || !userCanSend || cs.inProgress
|
val clicksOnTextFieldDisabled = !sendMsgEnabled || cs.preview is ComposePreview.VoicePreview || cs.inProgress
|
||||||
PlatformTextField(
|
PlatformTextField(
|
||||||
composeState,
|
composeState,
|
||||||
sendMsgEnabled,
|
sendMsgEnabled,
|
||||||
|
disabledText = userCantSendReason?.first,
|
||||||
sendMsgButtonDisabled,
|
sendMsgButtonDisabled,
|
||||||
textStyle,
|
textStyle,
|
||||||
showDeleteTextButton,
|
showDeleteTextButton,
|
||||||
userIsObserver,
|
|
||||||
if (clicksOnTextFieldDisabled) "" else placeholder,
|
if (clicksOnTextFieldDisabled) "" else placeholder,
|
||||||
showVoiceButton,
|
showVoiceButton,
|
||||||
onMessageChange,
|
onMessageChange,
|
||||||
|
@ -102,16 +99,23 @@ fun SendMsgView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (clicksOnTextFieldDisabled) {
|
if (clicksOnTextFieldDisabled) {
|
||||||
|
if (userCantSendReason != null) {
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.matchParentSize()
|
.matchParentSize()
|
||||||
.clickable(enabled = !userCanSend, indication = null, interactionSource = remember { MutableInteractionSource() }, onClick = {
|
.clickable(indication = null, interactionSource = remember { MutableInteractionSource() }, onClick = {
|
||||||
AlertManager.shared.showAlertMsg(
|
AlertManager.shared.showAlertMsg(
|
||||||
title = generalGetString(MR.strings.observer_cant_send_message_title),
|
title = generalGetString(MR.strings.cant_send_message_alert_title),
|
||||||
text = generalGetString(MR.strings.observer_cant_send_message_desc)
|
text = userCantSendReason.second
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.matchParentSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (showDeleteTextButton.value) {
|
if (showDeleteTextButton.value) {
|
||||||
DeleteTextButton(composeState)
|
DeleteTextButton(composeState)
|
||||||
|
@ -135,11 +139,11 @@ fun SendMsgView(
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
val stopRecOnNextClick = remember { mutableStateOf(false) }
|
val stopRecOnNextClick = remember { mutableStateOf(false) }
|
||||||
when {
|
when {
|
||||||
needToAllowVoiceToContact || !allowedVoiceByPrefs || !userCanSend -> {
|
needToAllowVoiceToContact || !allowedVoiceByPrefs -> {
|
||||||
DisallowedVoiceButton(userCanSend) {
|
DisallowedVoiceButton {
|
||||||
if (needToAllowVoiceToContact) {
|
if (needToAllowVoiceToContact) {
|
||||||
showNeedToAllowVoiceAlert(allowVoiceToContact)
|
showNeedToAllowVoiceAlert(allowVoiceToContact)
|
||||||
} else if (!allowedVoiceByPrefs) {
|
} else {
|
||||||
showDisabledVoiceAlert(isDirectChat)
|
showDisabledVoiceAlert(isDirectChat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,7 +159,7 @@ fun SendMsgView(
|
||||||
&& cs.contextItem is ComposeContextItem.NoContextItem
|
&& cs.contextItem is ComposeContextItem.NoContextItem
|
||||||
) {
|
) {
|
||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
StartLiveMessageButton(userCanSend) {
|
StartLiveMessageButton {
|
||||||
if (composeState.value.preview is ComposePreview.NoPreview) {
|
if (composeState.value.preview is ComposePreview.NoPreview) {
|
||||||
startLiveMessage(scope, sendLiveMessage, updateLiveMessage, sendButtonSize, sendButtonAlpha, composeState, liveMessageAlertShown)
|
startLiveMessage(scope, sendLiveMessage, updateLiveMessage, sendButtonSize, sendButtonAlpha, composeState, liveMessageAlertShown)
|
||||||
}
|
}
|
||||||
|
@ -343,8 +347,8 @@ private fun RecordVoiceView(recState: MutableState<RecordingState>, stopRecOnNex
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DisallowedVoiceButton(enabled: Boolean, onClick: () -> Unit) {
|
private fun DisallowedVoiceButton(onClick: () -> Unit) {
|
||||||
IconButton(onClick, Modifier.size(36.dp), enabled = enabled) {
|
IconButton(onClick, Modifier.size(36.dp)) {
|
||||||
Icon(
|
Icon(
|
||||||
painterResource(MR.images.ic_keyboard_voice),
|
painterResource(MR.images.ic_keyboard_voice),
|
||||||
stringResource(MR.strings.icon_descr_record_voice_message),
|
stringResource(MR.strings.icon_descr_record_voice_message),
|
||||||
|
@ -460,14 +464,13 @@ private fun SendMsgButton(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun StartLiveMessageButton(enabled: Boolean, onClick: () -> Unit) {
|
private fun StartLiveMessageButton(onClick: () -> Unit) {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val ripple = remember { ripple(bounded = false, radius = 24.dp) }
|
val ripple = remember { ripple(bounded = false, radius = 24.dp) }
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.requiredSize(36.dp)
|
modifier = Modifier.requiredSize(36.dp)
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
enabled = enabled,
|
|
||||||
role = Role.Button,
|
role = Role.Button,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
indication = ripple
|
indication = ripple
|
||||||
|
@ -477,7 +480,7 @@ private fun StartLiveMessageButton(enabled: Boolean, onClick: () -> Unit) {
|
||||||
Icon(
|
Icon(
|
||||||
BoltFilled,
|
BoltFilled,
|
||||||
stringResource(MR.strings.icon_descr_send_message),
|
stringResource(MR.strings.icon_descr_send_message),
|
||||||
tint = if (enabled) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
|
tint = MaterialTheme.colors.primary,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(36.dp)
|
.size(36.dp)
|
||||||
.padding(4.dp)
|
.padding(4.dp)
|
||||||
|
@ -576,12 +579,11 @@ fun PreviewSendMsgView() {
|
||||||
isDirectChat = true,
|
isDirectChat = true,
|
||||||
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
||||||
sendMsgEnabled = true,
|
sendMsgEnabled = true,
|
||||||
|
userCantSendReason = null,
|
||||||
sendButtonEnabled = true,
|
sendButtonEnabled = true,
|
||||||
nextSendGrpInv = false,
|
nextSendGrpInv = false,
|
||||||
needToAllowVoiceToContact = false,
|
needToAllowVoiceToContact = false,
|
||||||
allowedVoiceByPrefs = true,
|
allowedVoiceByPrefs = true,
|
||||||
userIsObserver = false,
|
|
||||||
userCanSend = true,
|
|
||||||
allowVoiceToContact = {},
|
allowVoiceToContact = {},
|
||||||
timedMessageAllowed = false,
|
timedMessageAllowed = false,
|
||||||
placeholder = "",
|
placeholder = "",
|
||||||
|
@ -612,12 +614,11 @@ fun PreviewSendMsgViewEditing() {
|
||||||
isDirectChat = true,
|
isDirectChat = true,
|
||||||
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
||||||
sendMsgEnabled = true,
|
sendMsgEnabled = true,
|
||||||
|
userCantSendReason = null,
|
||||||
sendButtonEnabled = true,
|
sendButtonEnabled = true,
|
||||||
nextSendGrpInv = false,
|
nextSendGrpInv = false,
|
||||||
needToAllowVoiceToContact = false,
|
needToAllowVoiceToContact = false,
|
||||||
allowedVoiceByPrefs = true,
|
allowedVoiceByPrefs = true,
|
||||||
userIsObserver = false,
|
|
||||||
userCanSend = true,
|
|
||||||
allowVoiceToContact = {},
|
allowVoiceToContact = {},
|
||||||
timedMessageAllowed = false,
|
timedMessageAllowed = false,
|
||||||
placeholder = "",
|
placeholder = "",
|
||||||
|
@ -648,12 +649,11 @@ fun PreviewSendMsgViewInProgress() {
|
||||||
isDirectChat = true,
|
isDirectChat = true,
|
||||||
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
||||||
sendMsgEnabled = true,
|
sendMsgEnabled = true,
|
||||||
|
userCantSendReason = null,
|
||||||
sendButtonEnabled = true,
|
sendButtonEnabled = true,
|
||||||
nextSendGrpInv = false,
|
nextSendGrpInv = false,
|
||||||
needToAllowVoiceToContact = false,
|
needToAllowVoiceToContact = false,
|
||||||
allowedVoiceByPrefs = true,
|
allowedVoiceByPrefs = true,
|
||||||
userIsObserver = false,
|
|
||||||
userCanSend = true,
|
|
||||||
allowVoiceToContact = {},
|
allowVoiceToContact = {},
|
||||||
timedMessageAllowed = false,
|
timedMessageAllowed = false,
|
||||||
placeholder = "",
|
placeholder = "",
|
||||||
|
|
|
@ -487,8 +487,6 @@
|
||||||
<string name="image_decoding_exception_desc">The image cannot be decoded. Please, try a different image or contact developers.</string>
|
<string name="image_decoding_exception_desc">The image cannot be decoded. Please, try a different image or contact developers.</string>
|
||||||
<string name="video_decoding_exception_desc">The video cannot be decoded. Please, try a different video or contact developers.</string>
|
<string name="video_decoding_exception_desc">The video cannot be decoded. Please, try a different video or contact developers.</string>
|
||||||
<string name="you_are_observer">you are observer</string>
|
<string name="you_are_observer">you are observer</string>
|
||||||
<string name="observer_cant_send_message_title">You can\'t send messages!</string>
|
|
||||||
<string name="observer_cant_send_message_desc">Please contact group admin.</string>
|
|
||||||
<string name="files_and_media_prohibited">Files and media prohibited!</string>
|
<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="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="compose_send_direct_message_to_connect">Send direct message to connect</string>
|
||||||
|
@ -508,6 +506,19 @@
|
||||||
<string name="report_compose_reason_header_illegal">Report content: only group moderators will see it.</string>
|
<string name="report_compose_reason_header_illegal">Report content: only group moderators will see it.</string>
|
||||||
<string name="report_compose_reason_header_other">Report other: only group moderators will see it.</string>
|
<string name="report_compose_reason_header_other">Report other: only group moderators will see it.</string>
|
||||||
|
|
||||||
|
<string name="cant_send_message_alert_title">You can\'t send messages!</string>
|
||||||
|
<string name="cant_send_message_contact_not_ready">contact not ready</string>
|
||||||
|
<string name="cant_send_message_contact_deleted">contact deleted</string>
|
||||||
|
<string name="cant_send_message_contact_not_synchronized">not synchronized</string>
|
||||||
|
<string name="cant_send_message_contact_disabled">contact disabled</string>
|
||||||
|
<string name="observer_cant_send_message_title">you are observer</string>
|
||||||
|
<string name="observer_cant_send_message_desc">Please contact group admin.</string>
|
||||||
|
<string name="cant_send_message_rejected">request to join rejected</string>
|
||||||
|
<string name="cant_send_message_group_deleted">group is deleted</string>
|
||||||
|
<string name="cant_send_message_mem_removed">removed from group</string>
|
||||||
|
<string name="cant_send_message_you_left">you left</string>
|
||||||
|
<string name="cant_send_message_generic">can\'t send messages</string>
|
||||||
|
|
||||||
<!-- Images - chat.simplex.app.views.chat.item.CIImageView.kt -->
|
<!-- Images - chat.simplex.app.views.chat.item.CIImageView.kt -->
|
||||||
<string name="image_descr">Image</string>
|
<string name="image_descr">Image</string>
|
||||||
<string name="icon_descr_waiting_for_image">Waiting for image</string>
|
<string name="icon_descr_waiting_for_image">Waiting for image</string>
|
||||||
|
|
|
@ -44,10 +44,10 @@ import kotlin.text.substring
|
||||||
actual fun PlatformTextField(
|
actual fun PlatformTextField(
|
||||||
composeState: MutableState<ComposeState>,
|
composeState: MutableState<ComposeState>,
|
||||||
sendMsgEnabled: Boolean,
|
sendMsgEnabled: Boolean,
|
||||||
|
disabledText: String?,
|
||||||
sendMsgButtonDisabled: Boolean,
|
sendMsgButtonDisabled: Boolean,
|
||||||
textStyle: MutableState<TextStyle>,
|
textStyle: MutableState<TextStyle>,
|
||||||
showDeleteTextButton: MutableState<Boolean>,
|
showDeleteTextButton: MutableState<Boolean>,
|
||||||
userIsObserver: Boolean,
|
|
||||||
placeholder: String,
|
placeholder: String,
|
||||||
showVoiceButton: Boolean,
|
showVoiceButton: Boolean,
|
||||||
onMessageChange: (ComposeMessage) -> Unit,
|
onMessageChange: (ComposeMessage) -> Unit,
|
||||||
|
@ -203,16 +203,16 @@ actual fun PlatformTextField(
|
||||||
)
|
)
|
||||||
showDeleteTextButton.value = cs.message.text.split("\n").size >= 4 && !cs.inProgress
|
showDeleteTextButton.value = cs.message.text.split("\n").size >= 4 && !cs.inProgress
|
||||||
if (composeState.value.preview is ComposePreview.VoicePreview) {
|
if (composeState.value.preview is ComposePreview.VoicePreview) {
|
||||||
ComposeOverlay(MR.strings.voice_message_send_text, textStyle, padding)
|
ComposeOverlay(generalGetString(MR.strings.voice_message_send_text), textStyle, padding)
|
||||||
} else if (userIsObserver) {
|
} else if (disabledText != null) {
|
||||||
ComposeOverlay(MR.strings.you_are_observer, textStyle, padding)
|
ComposeOverlay(disabledText, textStyle, padding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ComposeOverlay(textId: StringResource, textStyle: MutableState<TextStyle>, padding: PaddingValues) {
|
private fun ComposeOverlay(text: String, textStyle: MutableState<TextStyle>, padding: PaddingValues) {
|
||||||
Text(
|
Text(
|
||||||
generalGetString(textId),
|
text,
|
||||||
Modifier.padding(padding),
|
Modifier.padding(padding),
|
||||||
color = MaterialTheme.colors.secondary,
|
color = MaterialTheme.colors.secondary,
|
||||||
style = textStyle.value.copy(fontStyle = FontStyle.Italic)
|
style = textStyle.value.copy(fontStyle = FontStyle.Italic)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue