android: connect with contact via address (for preset simplex contact) (#3330)

This commit is contained in:
spaced4ndy 2023-11-10 10:16:28 +04:00 committed by GitHub
parent f49ded5ae5
commit c0be36737d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 165 additions and 48 deletions

View File

@ -147,12 +147,13 @@ object ChatModel {
val currentCInfo = chats[i].chatInfo val currentCInfo = chats[i].chatInfo
var newCInfo = cInfo var newCInfo = cInfo
if (currentCInfo is ChatInfo.Direct && newCInfo is ChatInfo.Direct) { if (currentCInfo is ChatInfo.Direct && newCInfo is ChatInfo.Direct) {
val currentStats = currentCInfo.contact.activeConn.connectionStats val currentStats = currentCInfo.contact.activeConn?.connectionStats
val newStats = newCInfo.contact.activeConn.connectionStats val newConn = newCInfo.contact.activeConn
if (currentStats != null && newStats == null) { val newStats = newConn?.connectionStats
if (currentStats != null && newConn != null && newStats == null) {
newCInfo = newCInfo.copy( newCInfo = newCInfo.copy(
contact = newCInfo.contact.copy( contact = newCInfo.contact.copy(
activeConn = newCInfo.contact.activeConn.copy( activeConn = newConn.copy(
connectionStats = currentStats connectionStats = currentStats
) )
) )
@ -168,7 +169,7 @@ object ChatModel {
fun updateContact(contact: Contact) = updateChat(ChatInfo.Direct(contact), addMissing = contact.directOrUsed) fun updateContact(contact: Contact) = updateChat(ChatInfo.Direct(contact), addMissing = contact.directOrUsed)
fun updateContactConnectionStats(contact: Contact, connectionStats: ConnectionStats) { fun updateContactConnectionStats(contact: Contact, connectionStats: ConnectionStats) {
val updatedConn = contact.activeConn.copy(connectionStats = connectionStats) val updatedConn = contact.activeConn?.copy(connectionStats = connectionStats)
val updatedContact = contact.copy(activeConn = updatedConn) val updatedContact = contact.copy(activeConn = updatedConn)
updateContact(updatedContact) updateContact(updatedContact)
} }
@ -570,11 +571,19 @@ object ChatModel {
} }
fun setContactNetworkStatus(contact: Contact, status: NetworkStatus) { fun setContactNetworkStatus(contact: Contact, status: NetworkStatus) {
networkStatuses[contact.activeConn.agentConnId] = status val conn = contact.activeConn
if (conn != null) {
networkStatuses[conn.agentConnId] = status
}
} }
fun contactNetworkStatus(contact: Contact): NetworkStatus = fun contactNetworkStatus(contact: Contact): NetworkStatus {
networkStatuses[contact.activeConn.agentConnId] ?: NetworkStatus.Unknown() val conn = contact.activeConn
return if (conn != null)
networkStatuses[conn.agentConnId] ?: NetworkStatus.Unknown()
else
NetworkStatus.Unknown()
}
fun addTerminalItem(item: TerminalItem) { fun addTerminalItem(item: TerminalItem) {
if (terminalItems.size >= 500) { if (terminalItems.size >= 500) {
@ -891,7 +900,7 @@ data class Contact(
val contactId: Long, val contactId: Long,
override val localDisplayName: String, override val localDisplayName: String,
val profile: LocalProfile, val profile: LocalProfile,
val activeConn: Connection, val activeConn: Connection? = null,
val viaGroup: Long? = null, val viaGroup: Long? = null,
val contactUsed: Boolean, val contactUsed: Boolean,
val contactStatus: ContactStatus, val contactStatus: ContactStatus,
@ -906,10 +915,10 @@ data class Contact(
override val chatType get() = ChatType.Direct override val chatType get() = ChatType.Direct
override val id get() = "@$contactId" override val id get() = "@$contactId"
override val apiId get() = contactId override val apiId get() = contactId
override val ready get() = activeConn.connStatus == ConnStatus.Ready override val ready get() = activeConn?.connStatus == ConnStatus.Ready
val active get() = contactStatus == ContactStatus.Active val active get() = contactStatus == ContactStatus.Active
override val sendMsgEnabled get() = override val sendMsgEnabled get() =
(ready && active && !(activeConn.connectionStats?.ratchetSyncSendProhibited ?: false)) (ready && active && !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?: false))
|| nextSendGrpInv || nextSendGrpInv
val nextSendGrpInv get() = contactGroupMemberId != null && !contactGrpInvSent val nextSendGrpInv get() = contactGroupMemberId != null && !contactGrpInvSent
override val ntfsEnabled get() = chatSettings.enableNtfs == MsgFilter.All override val ntfsEnabled get() = chatSettings.enableNtfs == MsgFilter.All
@ -927,13 +936,17 @@ data class Contact(
override val image get() = profile.image override val image get() = profile.image
val contactLink: String? = profile.contactLink val contactLink: String? = profile.contactLink
override val localAlias get() = profile.localAlias override val localAlias get() = profile.localAlias
val verified get() = activeConn.connectionCode != null val verified get() = activeConn?.connectionCode != null
val directOrUsed: Boolean get() = val directOrUsed: Boolean get() =
(activeConn.connLevel == 0 && !activeConn.viaGroupLink) || contactUsed if (activeConn != null) {
(activeConn.connLevel == 0 && !activeConn.viaGroupLink) || contactUsed
} else {
true
}
val contactConnIncognito = val contactConnIncognito =
activeConn.customUserProfileId != null activeConn?.customUserProfileId != null
fun allowsFeature(feature: ChatFeature): Boolean = when (feature) { fun allowsFeature(feature: ChatFeature): Boolean = when (feature) {
ChatFeature.TimedMessages -> mergedPreferences.timedMessages.contactPreference.allow != FeatureAllowed.NO ChatFeature.TimedMessages -> mergedPreferences.timedMessages.contactPreference.allow != FeatureAllowed.NO

View File

@ -907,6 +907,23 @@ object ChatController {
} }
} }
suspend fun apiConnectContactViaAddress(incognito: Boolean, contactId: Long): Contact? {
val userId = chatModel.currentUser.value?.userId ?: run {
Log.e(TAG, "apiConnectContactViaAddress: no current user")
return null
}
val r = sendCmd(CC.ApiConnectContactViaAddress(userId, incognito, contactId))
when {
r is CR.SentInvitationToContact -> return r.contact
else -> {
if (!(networkErrorAlert(r))) {
apiErrorAlert("apiConnectContactViaAddress", generalGetString(MR.strings.connection_error), r)
}
return null
}
}
}
suspend fun apiDeleteChat(type: ChatType, id: Long, notify: Boolean? = null): Boolean { suspend fun apiDeleteChat(type: ChatType, id: Long, notify: Boolean? = null): Boolean {
val r = sendCmd(CC.ApiDeleteChat(type, id, notify)) val r = sendCmd(CC.ApiDeleteChat(type, id, notify))
when { when {
@ -1413,8 +1430,11 @@ object ChatController {
is CR.ContactConnected -> { is CR.ContactConnected -> {
if (active(r.user) && r.contact.directOrUsed) { if (active(r.user) && r.contact.directOrUsed) {
chatModel.updateContact(r.contact) chatModel.updateContact(r.contact)
chatModel.dismissConnReqView(r.contact.activeConn.id) val conn = r.contact.activeConn
chatModel.removeChat(r.contact.activeConn.id) if (conn != null) {
chatModel.dismissConnReqView(conn.id)
chatModel.removeChat(conn.id)
}
} }
if (r.contact.directOrUsed) { if (r.contact.directOrUsed) {
ntfManager.notifyContactConnected(r.user, r.contact) ntfManager.notifyContactConnected(r.user, r.contact)
@ -1424,8 +1444,11 @@ object ChatController {
is CR.ContactConnecting -> { is CR.ContactConnecting -> {
if (active(r.user) && r.contact.directOrUsed) { if (active(r.user) && r.contact.directOrUsed) {
chatModel.updateContact(r.contact) chatModel.updateContact(r.contact)
chatModel.dismissConnReqView(r.contact.activeConn.id) val conn = r.contact.activeConn
chatModel.removeChat(r.contact.activeConn.id) if (conn != null) {
chatModel.dismissConnReqView(conn.id)
chatModel.removeChat(conn.id)
}
} }
} }
is CR.ReceivedContactRequest -> { is CR.ReceivedContactRequest -> {
@ -1556,9 +1579,10 @@ object ChatController {
if (!active(r.user)) return if (!active(r.user)) return
chatModel.updateGroup(r.groupInfo) chatModel.updateGroup(r.groupInfo)
if (r.hostContact != null) { val conn = r.hostContact?.activeConn
chatModel.dismissConnReqView(r.hostContact.activeConn.id) if (conn != null) {
chatModel.removeChat(r.hostContact.activeConn.id) chatModel.dismissConnReqView(conn.id)
chatModel.removeChat(conn.id)
} }
} }
is CR.GroupLinkConnecting -> { is CR.GroupLinkConnecting -> {
@ -1946,6 +1970,7 @@ sealed class CC {
class ApiSetConnectionIncognito(val connId: Long, val incognito: Boolean): CC() class ApiSetConnectionIncognito(val connId: Long, val incognito: Boolean): CC()
class APIConnectPlan(val userId: Long, val connReq: String): CC() class APIConnectPlan(val userId: Long, val connReq: String): CC()
class APIConnect(val userId: Long, val incognito: Boolean, val connReq: String): CC() class APIConnect(val userId: Long, val incognito: Boolean, val connReq: String): CC()
class ApiConnectContactViaAddress(val userId: Long, val incognito: Boolean, val contactId: Long): CC()
class ApiDeleteChat(val type: ChatType, val id: Long, val notify: Boolean?): CC() class ApiDeleteChat(val type: ChatType, val id: Long, val notify: Boolean?): CC()
class ApiClearChat(val type: ChatType, val id: Long): CC() class ApiClearChat(val type: ChatType, val id: Long): CC()
class ApiListContacts(val userId: Long): CC() class ApiListContacts(val userId: Long): CC()
@ -2057,6 +2082,7 @@ sealed class CC {
is ApiSetConnectionIncognito -> "/_set incognito :$connId ${onOff(incognito)}" is ApiSetConnectionIncognito -> "/_set incognito :$connId ${onOff(incognito)}"
is APIConnectPlan -> "/_connect plan $userId $connReq" is APIConnectPlan -> "/_connect plan $userId $connReq"
is APIConnect -> "/_connect $userId incognito=${onOff(incognito)} $connReq" is APIConnect -> "/_connect $userId incognito=${onOff(incognito)} $connReq"
is ApiConnectContactViaAddress -> "/_connect contact $userId incognito=${onOff(incognito)} $contactId"
is ApiDeleteChat -> if (notify != null) { is ApiDeleteChat -> if (notify != null) {
"/_delete ${chatRef(type, id)} notify=${onOff(notify)}" "/_delete ${chatRef(type, id)} notify=${onOff(notify)}"
} else { } else {
@ -2164,6 +2190,7 @@ sealed class CC {
is ApiSetConnectionIncognito -> "apiSetConnectionIncognito" is ApiSetConnectionIncognito -> "apiSetConnectionIncognito"
is APIConnectPlan -> "apiConnectPlan" is APIConnectPlan -> "apiConnectPlan"
is APIConnect -> "apiConnect" is APIConnect -> "apiConnect"
is ApiConnectContactViaAddress -> "apiConnectContactViaAddress"
is ApiDeleteChat -> "apiDeleteChat" is ApiDeleteChat -> "apiDeleteChat"
is ApiClearChat -> "apiClearChat" is ApiClearChat -> "apiClearChat"
is ApiListContacts -> "apiListContacts" is ApiListContacts -> "apiListContacts"
@ -3379,6 +3406,7 @@ sealed class CR {
@Serializable @SerialName("connectionPlan") class CRConnectionPlan(val user: UserRef, val connectionPlan: ConnectionPlan): CR() @Serializable @SerialName("connectionPlan") class CRConnectionPlan(val user: UserRef, val connectionPlan: ConnectionPlan): CR()
@Serializable @SerialName("sentConfirmation") class SentConfirmation(val user: UserRef): CR() @Serializable @SerialName("sentConfirmation") class SentConfirmation(val user: UserRef): CR()
@Serializable @SerialName("sentInvitation") class SentInvitation(val user: UserRef): CR() @Serializable @SerialName("sentInvitation") class SentInvitation(val user: UserRef): CR()
@Serializable @SerialName("sentInvitationToContact") class SentInvitationToContact(val user: UserRef, val contact: Contact, val customUserProfile: Profile?): CR()
@Serializable @SerialName("contactAlreadyExists") class ContactAlreadyExists(val user: UserRef, val contact: Contact): CR() @Serializable @SerialName("contactAlreadyExists") class ContactAlreadyExists(val user: UserRef, val contact: Contact): CR()
@Serializable @SerialName("contactRequestAlreadyAccepted") class ContactRequestAlreadyAccepted(val user: UserRef, val contact: Contact): CR() @Serializable @SerialName("contactRequestAlreadyAccepted") class ContactRequestAlreadyAccepted(val user: UserRef, val contact: Contact): CR()
@Serializable @SerialName("contactDeleted") class ContactDeleted(val user: UserRef, val contact: Contact): CR() @Serializable @SerialName("contactDeleted") class ContactDeleted(val user: UserRef, val contact: Contact): CR()
@ -3517,6 +3545,7 @@ sealed class CR {
is CRConnectionPlan -> "connectionPlan" is CRConnectionPlan -> "connectionPlan"
is SentConfirmation -> "sentConfirmation" is SentConfirmation -> "sentConfirmation"
is SentInvitation -> "sentInvitation" is SentInvitation -> "sentInvitation"
is SentInvitationToContact -> "sentInvitationToContact"
is ContactAlreadyExists -> "contactAlreadyExists" is ContactAlreadyExists -> "contactAlreadyExists"
is ContactRequestAlreadyAccepted -> "contactRequestAlreadyAccepted" is ContactRequestAlreadyAccepted -> "contactRequestAlreadyAccepted"
is ContactDeleted -> "contactDeleted" is ContactDeleted -> "contactDeleted"
@ -3650,6 +3679,7 @@ sealed class CR {
is CRConnectionPlan -> withUser(user, json.encodeToString(connectionPlan)) is CRConnectionPlan -> withUser(user, json.encodeToString(connectionPlan))
is SentConfirmation -> withUser(user, noDetails()) is SentConfirmation -> withUser(user, noDetails())
is SentInvitation -> withUser(user, noDetails()) is SentInvitation -> withUser(user, noDetails())
is SentInvitationToContact -> withUser(user, json.encodeToString(contact))
is ContactAlreadyExists -> withUser(user, json.encodeToString(contact)) is ContactAlreadyExists -> withUser(user, json.encodeToString(contact))
is ContactRequestAlreadyAccepted -> withUser(user, json.encodeToString(contact)) is ContactRequestAlreadyAccepted -> withUser(user, json.encodeToString(contact))
is ContactDeleted -> withUser(user, json.encodeToString(contact)) is ContactDeleted -> withUser(user, json.encodeToString(contact))
@ -3785,6 +3815,7 @@ sealed class ContactAddressPlan {
@Serializable @SerialName("connectingConfirmReconnect") object ConnectingConfirmReconnect: ContactAddressPlan() @Serializable @SerialName("connectingConfirmReconnect") object ConnectingConfirmReconnect: ContactAddressPlan()
@Serializable @SerialName("connectingProhibit") class ConnectingProhibit(val contact: Contact): ContactAddressPlan() @Serializable @SerialName("connectingProhibit") class ConnectingProhibit(val contact: Contact): ContactAddressPlan()
@Serializable @SerialName("known") class Known(val contact: Contact): ContactAddressPlan() @Serializable @SerialName("known") class Known(val contact: Contact): ContactAddressPlan()
@Serializable @SerialName("contactViaAddress") class ContactViaAddress(val contact: Contact): ContactAddressPlan()
} }
@Serializable @Serializable

View File

@ -150,7 +150,7 @@ fun ChatInfoView(
val (verified, existingCode) = r val (verified, existingCode) = r
chatModel.updateContact( chatModel.updateContact(
ct.copy( ct.copy(
activeConn = ct.activeConn.copy( activeConn = ct.activeConn?.copy(
connectionCode = if (verified) SecurityCode(existingCode, Clock.System.now()) else null connectionCode = if (verified) SecurityCode(existingCode, Clock.System.now()) else null
) )
) )

View File

@ -57,7 +57,7 @@ fun CIRcvDecryptionError(
if (cInfo is ChatInfo.Direct) { if (cInfo is ChatInfo.Direct) {
val modelCInfo = findModelChat(cInfo.id)?.chatInfo val modelCInfo = findModelChat(cInfo.id)?.chatInfo
if (modelCInfo is ChatInfo.Direct) { if (modelCInfo is ChatInfo.Direct) {
val modelContactStats = modelCInfo.contact.activeConn.connectionStats val modelContactStats = modelCInfo.contact.activeConn?.connectionStats
if (modelContactStats != null) { if (modelContactStats != null) {
if (modelContactStats.ratchetSyncAllowed) { if (modelContactStats.ratchetSyncAllowed) {
DecryptionErrorItemFixButton( DecryptionErrorItemFixButton(

View File

@ -30,6 +30,7 @@ import chat.simplex.common.views.newchat.*
import chat.simplex.res.MR import chat.simplex.res.MR
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import java.net.URI
@Composable @Composable
fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) { fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
@ -61,8 +62,8 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
val contactNetworkStatus = chatModel.contactNetworkStatus(chat.chatInfo.contact) val contactNetworkStatus = chatModel.contactNetworkStatus(chat.chatInfo.contact)
ChatListNavLinkLayout( ChatListNavLinkLayout(
chatLinkPreview = { ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, contactNetworkStatus, stopped, linkMode, inProgress = false, progressByTimeout = false) }, chatLinkPreview = { ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, contactNetworkStatus, stopped, linkMode, inProgress = false, progressByTimeout = false) },
click = { directChatAction(chat.chatInfo, chatModel) }, click = { directChatAction(chat.chatInfo.contact, chatModel) },
dropdownMenuItems = { ContactMenuItems(chat, chatModel, showMenu, showMarkRead) }, dropdownMenuItems = { ContactMenuItems(chat, chat.chatInfo.contact, chatModel, showMenu, showMarkRead) },
showMenu, showMenu,
stopped, stopped,
selectedChat selectedChat
@ -118,8 +119,11 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
} }
} }
fun directChatAction(chatInfo: ChatInfo, chatModel: ChatModel) { fun directChatAction(contact: Contact, chatModel: ChatModel) {
withBGApi { openChat(chatInfo, chatModel) } when {
contact.activeConn == null && contact.profile.contactLink != null -> askCurrentOrIncognitoProfileConnectContactViaAddress(chatModel, contact, close = null, openChat = true)
else -> withBGApi { openChat(ChatInfo.Direct(contact), chatModel) }
}
} }
fun groupChatAction(groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState<Boolean>? = null) { fun groupChatAction(groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState<Boolean>? = null) {
@ -192,15 +196,17 @@ suspend fun setGroupMembers(groupInfo: GroupInfo, chatModel: ChatModel) {
} }
@Composable @Composable
fun ContactMenuItems(chat: Chat, chatModel: ChatModel, showMenu: MutableState<Boolean>, showMarkRead: Boolean) { fun ContactMenuItems(chat: Chat, contact: Contact, chatModel: ChatModel, showMenu: MutableState<Boolean>, showMarkRead: Boolean) {
if (showMarkRead) { if (contact.activeConn != null) {
MarkReadChatAction(chat, chatModel, showMenu) if (showMarkRead) {
} else { MarkReadChatAction(chat, chatModel, showMenu)
MarkUnreadChatAction(chat, chatModel, showMenu) } else {
MarkUnreadChatAction(chat, chatModel, showMenu)
}
ToggleFavoritesChatAction(chat, chatModel, chat.chatInfo.chatSettings?.favorite == true, showMenu)
ToggleNotificationsChatAction(chat, chatModel, chat.chatInfo.ntfsEnabled, showMenu)
ClearChatAction(chat, chatModel, showMenu)
} }
ToggleFavoritesChatAction(chat, chatModel, chat.chatInfo.chatSettings?.favorite == true, showMenu)
ToggleNotificationsChatAction(chat, chatModel, chat.chatInfo.ntfsEnabled, showMenu)
ClearChatAction(chat, chatModel, showMenu)
DeleteContactAction(chat, chatModel, showMenu) DeleteContactAction(chat, chatModel, showMenu)
} }
@ -591,6 +597,63 @@ fun pendingContactAlertDialog(chatInfo: ChatInfo, chatModel: ChatModel) {
) )
} }
fun askCurrentOrIncognitoProfileConnectContactViaAddress(
chatModel: ChatModel,
contact: Contact,
close: (() -> Unit)?,
openChat: Boolean
) {
AlertManager.shared.showAlertDialogButtonsColumn(
title = String.format(generalGetString(MR.strings.connect_with_contact_name_question), contact.chatViewName),
buttons = {
Column {
SectionItemView({
AlertManager.shared.hideAlert()
withApi {
close?.invoke()
val ok = connectContactViaAddress(chatModel, contact.contactId, incognito = false)
if (ok && openChat) {
openDirectChat(contact.contactId, chatModel)
}
}
}) {
Text(generalGetString(MR.strings.connect_use_current_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
SectionItemView({
AlertManager.shared.hideAlert()
withApi {
close?.invoke()
val ok = connectContactViaAddress(chatModel, contact.contactId, incognito = true)
if (ok && openChat) {
openDirectChat(contact.contactId, chatModel)
}
}
}) {
Text(generalGetString(MR.strings.connect_use_new_incognito_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
SectionItemView({
AlertManager.shared.hideAlert()
}) {
Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
}
}
)
}
suspend fun connectContactViaAddress(chatModel: ChatModel, contactId: Long, incognito: Boolean): Boolean {
val contact = chatModel.controller.apiConnectContactViaAddress(incognito, contactId)
if (contact != null) {
chatModel.updateContact(contact)
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.connection_request_sent),
text = generalGetString(MR.strings.you_will_be_connected_when_your_connection_request_is_accepted)
)
return true
}
return false
}
fun acceptGroupInvitationAlertDialog(groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState<Boolean>? = null) { fun acceptGroupInvitationAlertDialog(groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState<Boolean>? = null) {
AlertManager.shared.showAlertDialog( AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.join_group_question), title = generalGetString(MR.strings.join_group_question),

View File

@ -146,11 +146,6 @@ fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerf
@Composable @Composable
private fun OnboardingButtons(openNewChatSheet: () -> Unit) { private fun OnboardingButtons(openNewChatSheet: () -> Unit) {
Column(Modifier.fillMaxSize().padding(DEFAULT_PADDING), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.Bottom) { Column(Modifier.fillMaxSize().padding(DEFAULT_PADDING), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.Bottom) {
val uriHandler = LocalUriHandler.current
ConnectButton(generalGetString(MR.strings.chat_with_developers)) {
uriHandler.openVerifiedSimplexUri(simplexTeamUri)
}
Spacer(Modifier.height(DEFAULT_PADDING))
ConnectButton(generalGetString(MR.strings.tap_to_start_new_chat), openNewChatSheet) ConnectButton(generalGetString(MR.strings.tap_to_start_new_chat), openNewChatSheet)
val color = MaterialTheme.colors.primaryVariant val color = MaterialTheme.colors.primaryVariant
Canvas(modifier = Modifier.width(40.dp).height(10.dp), onDraw = { Canvas(modifier = Modifier.width(40.dp).height(10.dp), onDraw = {

View File

@ -185,10 +185,14 @@ fun ChatPreviewView(
} else { } else {
when (cInfo) { when (cInfo) {
is ChatInfo.Direct -> is ChatInfo.Direct ->
if (cInfo.contact.nextSendGrpInv) { if (cInfo.contact.activeConn == null && cInfo.contact.profile.contactLink != null) {
Text(stringResource(MR.strings.member_contact_send_direct_message), color = MaterialTheme.colors.secondary) Text(stringResource(MR.strings.contact_tap_to_connect), color = MaterialTheme.colors.primary)
} else if (!cInfo.ready && cInfo.contact.active) { } else if (!cInfo.ready && cInfo.contact.activeConn != null) {
Text(stringResource(MR.strings.contact_connection_pending), color = MaterialTheme.colors.secondary) if (cInfo.contact.nextSendGrpInv) {
Text(stringResource(MR.strings.member_contact_send_direct_message), color = MaterialTheme.colors.secondary)
} else if (cInfo.contact.active) {
Text(stringResource(MR.strings.contact_connection_pending), color = MaterialTheme.colors.secondary)
}
} }
is ChatInfo.Group -> is ChatInfo.Group ->
when (cInfo.groupInfo.membership.memberStatus) { when (cInfo.groupInfo.membership.memberStatus) {
@ -215,7 +219,7 @@ fun ChatPreviewView(
@Composable @Composable
fun chatStatusImage() { fun chatStatusImage() {
if (cInfo is ChatInfo.Direct) { if (cInfo is ChatInfo.Direct) {
if (cInfo.contact.active) { if (cInfo.contact.active && cInfo.contact.activeConn != null) {
val descr = contactNetworkStatus?.statusString val descr = contactNetworkStatus?.statusString
when (contactNetworkStatus) { when (contactNetworkStatus) {
is NetworkStatus.Connected -> is NetworkStatus.Connected ->

View File

@ -20,7 +20,7 @@ fun ShareListNavLinkView(chat: Chat, chatModel: ChatModel) {
is ChatInfo.Direct -> is ChatInfo.Direct ->
ShareListNavLinkLayout( ShareListNavLinkLayout(
chatLinkPreview = { SharePreviewView(chat) }, chatLinkPreview = { SharePreviewView(chat) },
click = { directChatAction(chat.chatInfo, chatModel) }, click = { directChatAction(chat.chatInfo.contact, chatModel) },
stopped stopped
) )
is ChatInfo.Group -> is ChatInfo.Group ->

View File

@ -19,8 +19,7 @@ import androidx.compose.ui.unit.dp
import chat.simplex.common.model.* import chat.simplex.common.model.*
import chat.simplex.common.platform.TAG import chat.simplex.common.platform.TAG
import chat.simplex.common.ui.theme.* import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chatlist.openDirectChat import chat.simplex.common.views.chatlist.*
import chat.simplex.common.views.chatlist.openGroupChat
import chat.simplex.common.views.helpers.* import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.* import chat.simplex.common.views.usersettings.*
import chat.simplex.res.MR import chat.simplex.res.MR
@ -171,6 +170,16 @@ suspend fun planAndConnect(
String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), contact.displayName) String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), contact.displayName)
) )
} }
is ContactAddressPlan.ContactViaAddress -> {
Log.d(TAG, "planAndConnect, .ContactAddress, .ContactViaAddress, incognito=$incognito")
val contact = connectionPlan.contactAddressPlan.contact
if (incognito != null) {
close?.invoke()
connectContactViaAddress(chatModel, contact.contactId, incognito)
} else {
askCurrentOrIncognitoProfileConnectContactViaAddress(chatModel, contact, close, openChat = false)
}
}
} }
is ConnectionPlan.GroupLink -> when (connectionPlan.groupLinkPlan) { is ConnectionPlan.GroupLink -> when (connectionPlan.groupLinkPlan) {
GroupLinkPlan.Ok -> { GroupLinkPlan.Ok -> {

View File

@ -289,6 +289,8 @@
<string name="chat_with_developers">Chat with the developers</string> <string name="chat_with_developers">Chat with the developers</string>
<string name="you_have_no_chats">You have no chats</string> <string name="you_have_no_chats">You have no chats</string>
<string name="no_filtered_chats">No filtered chats</string> <string name="no_filtered_chats">No filtered chats</string>
<string name="contact_tap_to_connect">Tap to Connect</string>
<string name="connect_with_contact_name_question">Connect with %1$s?</string>
<!-- ChatView.kt --> <!-- ChatView.kt -->
<string name="no_selected_chat">No selected chat</string> <string name="no_selected_chat">No selected chat</string>