share address

This commit is contained in:
Evgeny @ SimpleX Chat
2026-07-15 07:21:42 +00:00
parent 48b7523827
commit 9d37a6efc3
8 changed files with 79 additions and 4 deletions
@@ -1174,6 +1174,13 @@ object ChatController {
return null
}
suspend fun apiShareMyAddress(rh: Long?, toChatType: ChatType, toChatId: Long, toScope: GroupChatScope?, sendAsGroup: Boolean): MsgContent? {
val r = sendCmd(rh, CC.ApiShareMyAddress(toChatType, toChatId, toScope, sendAsGroup))
if (r is API.Result && r.res is CR.ChatMsgContent) return r.res.msgContent
apiErrorAlert("apiShareMyAddress", generalGetString(MR.strings.error_sharing_address), r)
return null
}
suspend fun apiPlanForwardChatItems(rh: Long?, fromChatType: ChatType, fromChatId: Long, fromScope: GroupChatScope?, chatItemIds: List<Long>): CR.ForwardPlan? {
val r = sendCmd(rh, CC.ApiPlanForwardChatItems(fromChatType, fromChatId, fromScope, chatItemIds))
if (r is API.Result && r.res is CR.ForwardPlan) return r.res
@@ -3809,6 +3816,7 @@ sealed class CC {
class ApiPlanForwardChatItems(val fromChatType: ChatType, val fromChatId: Long, val fromScope: GroupChatScope?, val chatItemIds: List<Long>): CC()
class ApiForwardChatItems(val toChatType: ChatType, val toChatId: Long, val toScope: GroupChatScope?, val sendAsGroup: Boolean, val fromChatType: ChatType, val fromChatId: Long, val fromScope: GroupChatScope?, val itemIds: List<Long>, val ttl: Int?): CC()
class ApiShareChatMsgContent(val shareChatType: ChatType, val shareChatId: Long, val toChatType: ChatType, val toChatId: Long, val toScope: GroupChatScope?, val sendAsGroup: Boolean): CC()
class ApiShareMyAddress(val toChatType: ChatType, val toChatId: Long, val toScope: GroupChatScope?, val sendAsGroup: Boolean): CC()
class ApiNewGroup(val userId: Long, val incognito: Boolean, val groupProfile: GroupProfile): CC()
class ApiNewPublicGroup(val userId: Long, val incognito: Boolean, val relayIds: List<Long>, val groupProfile: GroupProfile): CC()
class ApiGetGroupRelays(val groupId: Long): CC()
@@ -4015,6 +4023,7 @@ sealed class CC {
is ApiShareChatMsgContent -> {
"/_share chat content ${chatRef(shareChatType, shareChatId, null)} ${chatRef(toChatType, toChatId, toScope)}${if (sendAsGroup) "(as_group=on)" else ""}"
}
is ApiShareMyAddress -> "/_share address ${chatRef(toChatType, toChatId, toScope)}${if (sendAsGroup) "(as_group=on)" else ""}"
is ApiPlanForwardChatItems -> {
"/_forward plan ${chatRef(fromChatType, fromChatId, fromScope)} ${chatItemIds.joinToString(",")}"
}
@@ -4206,6 +4215,7 @@ sealed class CC {
is ApiGetReactionMembers -> "apiGetReactionMembers"
is ApiForwardChatItems -> "apiForwardChatItems"
is ApiShareChatMsgContent -> "apiShareChatMsgContent"
is ApiShareMyAddress -> "apiShareMyAddress"
is ApiPlanForwardChatItems -> "apiPlanForwardChatItems"
is ApiNewGroup -> "apiNewGroup"
is ApiNewPublicGroup -> "apiNewPublicGroup"
@@ -526,6 +526,7 @@ fun ComposeView(
is SharedContent.Text -> emptyList()
is SharedContent.Forward -> emptyList()
is SharedContent.ChatLink -> emptyList()
is SharedContent.MyAddress -> emptyList()
}
// When sharing a file and pasting it in SimpleX itself, the file shouldn't be deleted before sending or before leaving the chat after sharing
chatModel.filesToDelete.removeAll { file ->
@@ -1543,6 +1544,22 @@ fun ComposeView(
}
}
}
is SharedContent.MyAddress -> {
val cInfo = chat.chatInfo
val sendAsGroup = cInfo.sendAsGroup
withBGApi {
val mc = chatModel.controller.apiShareMyAddress(
chat.remoteHostId,
cInfo.chatType, cInfo.apiId,
cInfo.groupChatScope(), sendAsGroup
)
if (mc is MsgContent.MCChat) {
composeState.value = composeState.value.copy(
preview = ComposePreview.ChatLinkPreview(mc.chatLink, mc.ownerSig)
)
}
}
}
null -> {}
}
chatModel.sharedContent.value = null
@@ -51,7 +51,7 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) {
}
}
}
is SharedContent.ChatLink -> {
is SharedContent.ChatLink, is SharedContent.MyAddress -> {
hasSimplexLink = true
}
null -> {}
@@ -99,9 +99,10 @@ private fun ShareListToolbar(chatModel: ChatModel, stopped: Boolean, onSearchVal
}
val users by remember { derivedStateOf { chatModel.users.filter { u -> u.user.activeUser || !u.user.hidden } } }
val navButton: @Composable RowScope.() -> Unit = {
val sharedContent = remember { chatModel.sharedContent }.value
when {
showSearch -> NavigationButtonBack(hideSearchOnBack)
(users.size > 1 || chatModel.remoteHosts.isNotEmpty()) && remember { chatModel.sharedContent }.value !is SharedContent.Forward && remember { chatModel.sharedContent }.value !is SharedContent.ChatLink -> {
(users.size > 1 || chatModel.remoteHosts.isNotEmpty()) && sharedContent !is SharedContent.Forward && sharedContent !is SharedContent.ChatLink && sharedContent !is SharedContent.MyAddress -> {
val allRead = users
.filter { u -> !u.user.activeUser && !u.user.hidden }
.all { u -> u.unreadCount == 0 }
@@ -127,7 +128,6 @@ private fun ShareListToolbar(chatModel: ChatModel, stopped: Boolean, onSearchVal
}
}
else -> NavigationButtonBack(onButtonClicked = {
val sharedContent = chatModel.sharedContent.value
// Drop shared content
chatModel.sharedContent.value = null
if (sharedContent is SharedContent.Forward) {
@@ -150,6 +150,7 @@ private fun ShareListToolbar(chatModel: ChatModel, stopped: Boolean, onSearchVal
is SharedContent.File -> stringResource(MR.strings.share_file)
is SharedContent.Forward -> if (v.chatItems.size > 1) stringResource(MR.strings.forward_multiple) else stringResource(MR.strings.forward_message)
is SharedContent.ChatLink -> stringResource(MR.strings.share_channel)
is SharedContent.MyAddress -> stringResource(MR.strings.share_address)
null -> stringResource(MR.strings.share_message)
},
color = MaterialTheme.colors.onBackground,
@@ -196,7 +197,7 @@ private fun ShareList(
val oneHandUI = remember { appPrefs.oneHandUI.state }
val chats by remember(search) {
derivedStateOf {
val sorted = chatModel.chats.value.toList().filter { it.chatInfo.ready && it.chatInfo.sendMsgEnabled && !(chatModel.sharedContent.value is SharedContent.ChatLink && it.chatInfo is ChatInfo.Local) }.sortedByDescending { it.chatInfo is ChatInfo.Local }
val sorted = chatModel.chats.value.toList().filter { it.chatInfo.ready && it.chatInfo.sendMsgEnabled && !((chatModel.sharedContent.value is SharedContent.ChatLink || chatModel.sharedContent.value is SharedContent.MyAddress) && it.chatInfo is ChatInfo.Local) }.sortedByDescending { it.chatInfo is ChatInfo.Local }
filteredChats(mutableStateOf(false), mutableStateOf<Set<String>>(emptySet()), search, sorted)
}
}
@@ -16,6 +16,7 @@ sealed class SharedContent {
data class File(val text: String, val uri: URI): SharedContent()
data class Forward(val chatItems: List<ChatItem>, val fromChatInfo: ChatInfo): SharedContent()
data class ChatLink(val groupInfo: GroupInfo): SharedContent()
object MyAddress: SharedContent()
}
enum class AnimatedViewState {
@@ -354,6 +354,14 @@ private fun UserAddressLayout(
share(userAddress.connLinkContact.simplexChatUri(short = showShortLink.value))
}
}
ShareViaChatButton {
val shareViaChat = {
chatModel.sharedContent.value = SharedContent.MyAddress
chatModel.chatId.value = null
ModalManager.closeAllModalsEverywhere()
}
if (userAddress.shouldBeUpgraded) showAddShortLinkAlert { shareViaChat() } else shareViaChat()
}
// ShareViaEmailButton { sendEmail(userAddress) }
BusinessAddressToggle(addressSettingsState) { saveAddressSettings(addressSettingsState.value, savedAddressSettingsState) }
AddressSettingsButton(user, userAddress, shareViaProfile, setProfileAddress, saveAddressSettings)
@@ -438,6 +446,17 @@ private fun AddShortLinkButton(text: String, onClick: () -> Unit) {
)
}
@Composable
private fun ShareViaChatButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(MR.images.ic_forward),
stringResource(MR.strings.share_via_chat),
onClick,
iconColor = MaterialTheme.colors.primary,
textColor = MaterialTheme.colors.primary,
)
}
@Composable
private fun CreateOneTimeLinkButton() {
val closeAll = { ModalManager.start.closeModals() }
@@ -574,6 +574,7 @@
<string name="forward_message">Forward message…</string>
<string name="forward_multiple">Forward messages…</string>
<string name="share_channel">Share channel…</string>
<string name="share_address">Share address…</string>
<string name="cannot_share_message_alert_title">Cannot send message</string>
<string name="cannot_share_message_alert_text">Selected chat preferences prohibit this message.</string>
<string name="share_via_chat">Share via chat</string>
@@ -586,6 +587,7 @@
<string name="chat_link_from_owner">(from owner)</string>
<string name="chat_link_signed">(signed)</string>
<string name="error_sharing_channel">Error sharing channel</string>
<string name="error_sharing_address">Error sharing address</string>
<string name="owner_verification_passed">Link signature verified.</string>
<string name="owner_verification_failed">⚠️ Signature verification failed: %s.</string>
+2
View File
@@ -398,6 +398,7 @@ data ChatCommand
| APIPlanForwardChatItems {fromChatRef :: ChatRef, chatItemIds :: NonEmpty ChatItemId}
| APIForwardChatItems {toChatRef :: ChatRef, sendAsGroup :: ShowGroupAsSender, fromChatRef :: ChatRef, chatItemIds :: NonEmpty ChatItemId, ttl :: Maybe Int}
| APIShareChatMsgContent {shareChatRef :: ChatRef, toSendRef :: SendRef}
| APIShareMyAddress {toSendRef :: SendRef}
| APIUserRead UserId
| UserRead
| APIChatRead {chatRef :: ChatRef}
@@ -563,6 +564,7 @@ data ChatCommand
| ForwardGroupMessage {toChatName :: ChatName, fromGroupName :: GroupName, fromMemberName_ :: Maybe ContactName, forwardedMsg :: Text}
| ForwardLocalMessage {toChatName :: ChatName, forwardedMsg :: Text}
| SharePublicGroup {shareGroupName :: GroupName, toChatName :: ChatName}
| ShareMyAddress {toChatName :: ChatName}
| SendMessage SendName Text
| SendMemberContactMessage GroupName ContactName Text
| AcceptMemberContact ContactName
+23
View File
@@ -1168,6 +1168,15 @@ processChatCommand cxt nm = \case
| asGroup = (CBChannel, smpEncode pgId)
| otherwise = (CBGroup, smpEncode (pgId, memberId))
APIShareChatMsgContent _ _ -> throwCmdError "sharing is only supported for public groups"
APIShareMyAddress _ -> withUser $ \user -> do
UserContactLink {connLinkContact = CCLink _ sl_, addressSettings} <- withFastStore (`getUserAddress` user)
case sl_ of
Nothing -> throwCmdError "your address has no short link to share"
Just connLink -> do
let business = businessAddress addressSettings
profile = userProfileDirect user Nothing Nothing True
text = safeDecodeUtf8 $ strEncode connLink
pure $ CRChatMsgContent user MCChat {text, chatLink = MCLContact {connLink, profile, business}, ownerSig = Nothing}
APIUserRead userId -> withUserId userId $ \user -> withFastStore' (`setUserChatsRead` user) >> ok user
UserRead -> withUser $ \User {userId} -> processChatCommand cxt nm $ APIUserRead userId
APIChatRead chatRef@(ChatRef cType chatId scope_) -> withUser $ \_ -> case cType of
@@ -2447,6 +2456,18 @@ processChatCommand cxt nm = \case
CRChatMsgContent _ mc ->
processChatCommand cxt nm $ APISendMessages sendRef False Nothing False [composedMessage Nothing mc]
r -> pure r
ShareMyAddress toChatName -> withUser $ \user -> do
toChatRef <- getChatRef user toChatName
sendRef <- case toChatRef of
ChatRef CTDirect ctId _ -> pure $ SRDirect ctId
ChatRef CTGroup gId scope_ -> do
gInfo <- withFastStore $ \db -> getGroupInfo db cxt user gId
pure $ SRGroup gId scope_ (useRelays' gInfo)
_ -> throwCmdError "unsupported share target"
processChatCommand cxt nm (APIShareMyAddress sendRef) >>= \case
CRChatMsgContent _ mc ->
processChatCommand cxt nm $ APISendMessages sendRef False Nothing False [composedMessage Nothing mc]
r -> pure r
SendMessage sendName msg -> withUser $ \user -> do
let mc = MCText msg
case sendName of
@@ -5353,6 +5374,7 @@ chatCommandP =
"/_forward plan " *> (APIPlanForwardChatItems <$> chatRefP <*> _strP),
"/_forward " *> (APIForwardChatItems <$> chatRefP <*> (" as_group=" *> onOffP <|> pure False) <* A.space <*> chatRefP <*> _strP <*> sendMessageTTLP),
"/_share chat content " *> (APIShareChatMsgContent <$> chatRefP <* A.space <*> sendRefP),
"/_share address " *> (APIShareMyAddress <$> sendRefP),
"/_read user " *> (APIUserRead <$> A.decimal),
"/read user" $> UserRead,
"/_read chat " *> (APIChatRead <$> chatRefP),
@@ -5550,6 +5572,7 @@ chatCommandP =
ForwardGroupMessage <$> chatNameP <* " <- #" <*> displayNameP <*> pure Nothing <* A.space <*> msgTextP,
ForwardLocalMessage <$> chatNameP <* " <- * " <*> msgTextP,
"/share chat #" *> (SharePublicGroup <$> displayNameP <* A.space <*> chatNameP),
"/share address " *> (ShareMyAddress <$> chatNameP),
SendMessage <$> sendNameP <* A.space <*> msgTextP,
"@#" *> (SendMemberContactMessage <$> displayNameP <* A.space <* char_ '@' <*> displayNameP <* A.space <*> msgTextP),
"/accept_member_contact @" *> (AcceptMemberContact <$> displayNameP),