mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-14 23:25:33 +00:00
core: allow to add short link to existing contact link (#5940)
This commit is contained in:
@@ -80,6 +80,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case apiGroupLinkMemberRole(groupId: Int64, memberRole: GroupMemberRole)
|
||||
case apiDeleteGroupLink(groupId: Int64)
|
||||
case apiGetGroupLink(groupId: Int64)
|
||||
case apiAddShortLinkGroupLink(groupId: Int64)
|
||||
case apiCreateMemberContact(groupId: Int64, groupMemberId: Int64)
|
||||
case apiSendMemberContactInvitation(contactId: Int64, msg: MsgContent)
|
||||
case apiTestProtoServer(userId: Int64, server: String)
|
||||
@@ -134,6 +135,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case apiCreateMyAddress(userId: Int64, short: Bool)
|
||||
case apiDeleteMyAddress(userId: Int64)
|
||||
case apiShowMyAddress(userId: Int64)
|
||||
case apiAddShortLinkMyAddress(userId: Int64)
|
||||
case apiSetProfileAddress(userId: Int64, on: Bool)
|
||||
case apiAddressAutoAccept(userId: Int64, autoAccept: AutoAccept?)
|
||||
case apiAcceptContact(incognito: Bool, contactReqId: Int64)
|
||||
@@ -262,6 +264,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case let .apiGroupLinkMemberRole(groupId, memberRole): return "/_set link role #\(groupId) \(memberRole)"
|
||||
case let .apiDeleteGroupLink(groupId): return "/_delete link #\(groupId)"
|
||||
case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)"
|
||||
case let .apiAddShortLinkGroupLink(groupId): return "/_short link #\(groupId)"
|
||||
case let .apiCreateMemberContact(groupId, groupMemberId): return "/_create member contact #\(groupId) \(groupMemberId)"
|
||||
case let .apiSendMemberContactInvitation(contactId, mc): return "/_invite member contact @\(contactId) \(mc.cmdString)"
|
||||
case let .apiTestProtoServer(userId, server): return "/_server test \(userId) \(server)"
|
||||
@@ -326,6 +329,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case let .apiCreateMyAddress(userId, short): return "/_address \(userId) short=\(onOff(short))"
|
||||
case let .apiDeleteMyAddress(userId): return "/_delete_address \(userId)"
|
||||
case let .apiShowMyAddress(userId): return "/_show_address \(userId)"
|
||||
case let .apiAddShortLinkMyAddress(userId): return "/_short_link_address \(userId)"
|
||||
case let .apiSetProfileAddress(userId, on): return "/_profile_address \(userId) \(onOff(on))"
|
||||
case let .apiAddressAutoAccept(userId, autoAccept): return "/_auto_accept \(userId) \(AutoAccept.cmdString(autoAccept))"
|
||||
case let .apiAcceptContact(incognito, contactReqId): return "/_accept incognito=\(onOff(incognito)) \(contactReqId)"
|
||||
@@ -438,6 +442,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case .apiGroupLinkMemberRole: return "apiGroupLinkMemberRole"
|
||||
case .apiDeleteGroupLink: return "apiDeleteGroupLink"
|
||||
case .apiGetGroupLink: return "apiGetGroupLink"
|
||||
case .apiAddShortLinkGroupLink: return "apiAddShortLinkGroupLink"
|
||||
case .apiCreateMemberContact: return "apiCreateMemberContact"
|
||||
case .apiSendMemberContactInvitation: return "apiSendMemberContactInvitation"
|
||||
case .apiTestProtoServer: return "apiTestProtoServer"
|
||||
@@ -491,6 +496,7 @@ enum ChatCommand: ChatCmdProtocol {
|
||||
case .apiCreateMyAddress: return "apiCreateMyAddress"
|
||||
case .apiDeleteMyAddress: return "apiDeleteMyAddress"
|
||||
case .apiShowMyAddress: return "apiShowMyAddress"
|
||||
case .apiAddShortLinkMyAddress: return "apiAddShortLinkMyAddress"
|
||||
case .apiSetProfileAddress: return "apiSetProfileAddress"
|
||||
case .apiAddressAutoAccept: return "apiAddressAutoAccept"
|
||||
case .apiAcceptContact: return "apiAcceptContact"
|
||||
|
||||
@@ -1210,6 +1210,13 @@ private func userAddressResponse(_ r: APIResult<ChatResponse1>) throws -> UserCo
|
||||
}
|
||||
}
|
||||
|
||||
func apiAddShortLinkMyAddress() async throws -> UserContactLink {
|
||||
let userId = try currentUserId("apiAddShortLinkMyAddress")
|
||||
let r: ChatResponse1 = try await chatSendCmd(.apiAddShortLinkMyAddress(userId: userId))
|
||||
if case let .userContactLink(_, contactLink) = r { return contactLink }
|
||||
throw r.unexpected
|
||||
}
|
||||
|
||||
func userAddressAutoAccept(_ autoAccept: AutoAccept?) async throws -> UserContactLink? {
|
||||
let userId = try currentUserId("userAddressAutoAccept")
|
||||
let r: APIResult<ChatResponse1> = await chatApiSendCmd(.apiAddressAutoAccept(userId: userId, autoAccept: autoAccept))
|
||||
@@ -1736,6 +1743,12 @@ func apiGetGroupLink(_ groupId: Int64) throws -> (CreatedConnLink, GroupMemberRo
|
||||
}
|
||||
}
|
||||
|
||||
func apiAddShortLinkGroupLink(_ groupId: Int64) async throws -> (CreatedConnLink, GroupMemberRole) {
|
||||
let r: ChatResponse2 = try await chatSendCmd(.apiAddShortLinkGroupLink(groupId: groupId))
|
||||
if case let .groupLink(_, _, connLink, memberRole) = r { return (connLink, memberRole) }
|
||||
throw r.unexpected
|
||||
}
|
||||
|
||||
func apiCreateMemberContact(_ groupId: Int64, _ groupMemberId: Int64) async throws -> Contact {
|
||||
let r: ChatResponse2 = try await chatSendCmd(.apiCreateMemberContact(groupId: groupId, groupMemberId: groupMemberId))
|
||||
if case let .newMemberContact(_, contact, _, _) = r { return contact }
|
||||
|
||||
@@ -35,16 +35,23 @@ struct GroupLinkView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if creatingGroup {
|
||||
groupLinkView()
|
||||
.navigationBarBackButtonHidden()
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button ("Continue") { linkCreatedCb?() }
|
||||
ZStack {
|
||||
if creatingGroup {
|
||||
groupLinkView()
|
||||
.navigationBarBackButtonHidden()
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button ("Continue") { linkCreatedCb?() }
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
groupLinkView()
|
||||
} else {
|
||||
groupLinkView()
|
||||
}
|
||||
if creatingLink {
|
||||
ProgressView()
|
||||
.scaleEffect(2)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +86,14 @@ struct GroupLinkView: View {
|
||||
Label("Share link", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
|
||||
if (groupLink.connShortLink == nil && UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS)) {
|
||||
Button {
|
||||
addShortLink()
|
||||
} label: {
|
||||
Label("Add short link", systemImage: "plus")
|
||||
}
|
||||
}
|
||||
|
||||
if !creatingGroup {
|
||||
Button(role: .destructive) { alert = .deleteLink } label: {
|
||||
Label("Delete link", systemImage: "trash")
|
||||
@@ -89,11 +104,6 @@ struct GroupLinkView: View {
|
||||
Label("Create link", systemImage: "link.badge.plus")
|
||||
}
|
||||
.disabled(creatingLink)
|
||||
if creatingLink {
|
||||
ProgressView()
|
||||
.scaleEffect(2)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
if let groupLink, groupLink.connShortLink != nil {
|
||||
@@ -160,6 +170,26 @@ struct GroupLinkView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func addShortLink() {
|
||||
Task {
|
||||
do {
|
||||
creatingLink = true
|
||||
let link = try await apiAddShortLinkGroupLink(groupId)
|
||||
await MainActor.run {
|
||||
creatingLink = false
|
||||
(groupLink, groupLinkMemberRole) = link
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("apiAddShortLinkGroupLink: \(responseError(error))")
|
||||
await MainActor.run {
|
||||
creatingLink = false
|
||||
let a = getErrorAlert(error, "Error adding short link")
|
||||
alert = .error(title: a.title, error: a.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GroupLinkView_Previews: PreviewProvider {
|
||||
|
||||
@@ -153,6 +153,9 @@ struct UserAddressView: View {
|
||||
}
|
||||
}
|
||||
addressSettingsButton(userAddress)
|
||||
if (userAddress.connLinkContact.connShortLink == nil && UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS)) {
|
||||
addShortLinkButton()
|
||||
}
|
||||
} header: {
|
||||
ToggleShortLinkHeader(text: Text("For social media"), link: userAddress.connLinkContact, short: $showShortLink)
|
||||
} footer: {
|
||||
@@ -209,6 +212,30 @@ struct UserAddressView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func addShortLinkButton() -> some View {
|
||||
Button {
|
||||
addShortLink()
|
||||
} label: {
|
||||
Label("Add short link", systemImage: "plus")
|
||||
}
|
||||
}
|
||||
|
||||
private func addShortLink() {
|
||||
progressIndicator = true
|
||||
Task {
|
||||
do {
|
||||
let userAddress = try await apiAddShortLinkMyAddress()
|
||||
await MainActor.run {
|
||||
chatModel.userAddress = userAddress
|
||||
}
|
||||
await MainActor.run { progressIndicator = false }
|
||||
} catch let error {
|
||||
logger.error("apiAddShortLinkMyAddress: \(responseError(error))")
|
||||
await MainActor.run { progressIndicator = false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func createOneTimeLinkButton() -> some View {
|
||||
NavigationLink {
|
||||
NewChatView(selection: .invite)
|
||||
|
||||
+21
@@ -1570,6 +1570,14 @@ object ChatController {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiAddShortLinkMyAddress(rh: Long?): UserContactLinkRec? {
|
||||
val userId = kotlin.runCatching { currentUserId("apiAddShortLinkMyAddress") }.getOrElse { return null }
|
||||
val r = sendCmd(rh, CC.ApiAddShortLinkMyAddress(userId))
|
||||
if (r is API.Result && r.res is CR.UserContactLink) return r.res.contactLink
|
||||
Log.e(TAG, "apiAddShortLinkMyAddress bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun userAddressAutoAccept(rh: Long?, autoAccept: AutoAccept?): UserContactLinkRec? {
|
||||
val userId = kotlin.runCatching { currentUserId("userAddressAutoAccept") }.getOrElse { return null }
|
||||
val r = sendCmd(rh, CC.ApiAddressAutoAccept(userId, autoAccept))
|
||||
@@ -2016,6 +2024,13 @@ object ChatController {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiAddShortLinkGroupLink(rh: Long?, groupId: Long): Pair<CreatedConnLink, GroupMemberRole>? {
|
||||
val r = sendCmd(rh, CC.APIAddShortLinkGroupLink(groupId))
|
||||
if (r is API.Result && r.res is CR.GroupLink) return r.res.connLinkContact to r.res.memberRole
|
||||
Log.e(TAG, "apiAddShortLinkGroupLink bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiCreateMemberContact(rh: Long?, groupId: Long, groupMemberId: Long): Contact? {
|
||||
val r = sendCmd(rh, CC.APICreateMemberContact(groupId, groupMemberId))
|
||||
if (r is API.Result && r.res is CR.NewMemberContact) return r.res.contact
|
||||
@@ -3368,6 +3383,7 @@ sealed class CC {
|
||||
class APIGroupLinkMemberRole(val groupId: Long, val memberRole: GroupMemberRole): CC()
|
||||
class APIDeleteGroupLink(val groupId: Long): CC()
|
||||
class APIGetGroupLink(val groupId: Long): CC()
|
||||
class APIAddShortLinkGroupLink(val groupId: Long): CC()
|
||||
class APICreateMemberContact(val groupId: Long, val groupMemberId: Long): CC()
|
||||
class APISendMemberContactInvitation(val contactId: Long, val mc: MsgContent): CC()
|
||||
class APITestProtoServer(val userId: Long, val server: String): CC()
|
||||
@@ -3422,6 +3438,7 @@ sealed class CC {
|
||||
class ApiCreateMyAddress(val userId: Long, val short: Boolean): CC()
|
||||
class ApiDeleteMyAddress(val userId: Long): CC()
|
||||
class ApiShowMyAddress(val userId: Long): CC()
|
||||
class ApiAddShortLinkMyAddress(val userId: Long): CC()
|
||||
class ApiSetProfileAddress(val userId: Long, val on: Boolean): CC()
|
||||
class ApiAddressAutoAccept(val userId: Long, val autoAccept: AutoAccept?): CC()
|
||||
class ApiGetCallInvitations: CC()
|
||||
@@ -3555,6 +3572,7 @@ sealed class CC {
|
||||
is APIGroupLinkMemberRole -> "/_set link role #$groupId ${memberRole.name.lowercase()}"
|
||||
is APIDeleteGroupLink -> "/_delete link #$groupId"
|
||||
is APIGetGroupLink -> "/_get link #$groupId"
|
||||
is APIAddShortLinkGroupLink -> "/_short link #$groupId"
|
||||
is APICreateMemberContact -> "/_create member contact #$groupId $groupMemberId"
|
||||
is APISendMemberContactInvitation -> "/_invite member contact @$contactId ${mc.cmdString}"
|
||||
is APITestProtoServer -> "/_server test $userId $server"
|
||||
@@ -3609,6 +3627,7 @@ sealed class CC {
|
||||
is ApiCreateMyAddress -> "/_address $userId short=${onOff(short)}"
|
||||
is ApiDeleteMyAddress -> "/_delete_address $userId"
|
||||
is ApiShowMyAddress -> "/_show_address $userId"
|
||||
is ApiAddShortLinkMyAddress -> "/_short_link_address $userId"
|
||||
is ApiSetProfileAddress -> "/_profile_address $userId ${onOff(on)}"
|
||||
is ApiAddressAutoAccept -> "/_auto_accept $userId ${AutoAccept.cmdString(autoAccept)}"
|
||||
is ApiAcceptContact -> "/_accept incognito=${onOff(incognito)} $contactReqId"
|
||||
@@ -3720,6 +3739,7 @@ sealed class CC {
|
||||
is APIGroupLinkMemberRole -> "apiGroupLinkMemberRole"
|
||||
is APIDeleteGroupLink -> "apiDeleteGroupLink"
|
||||
is APIGetGroupLink -> "apiGetGroupLink"
|
||||
is APIAddShortLinkGroupLink -> "apiAddShortLinkGroupLink"
|
||||
is APICreateMemberContact -> "apiCreateMemberContact"
|
||||
is APISendMemberContactInvitation -> "apiSendMemberContactInvitation"
|
||||
is APITestProtoServer -> "testProtoServer"
|
||||
@@ -3774,6 +3794,7 @@ sealed class CC {
|
||||
is ApiCreateMyAddress -> "apiCreateMyAddress"
|
||||
is ApiDeleteMyAddress -> "apiDeleteMyAddress"
|
||||
is ApiShowMyAddress -> "apiShowMyAddress"
|
||||
is ApiAddShortLinkMyAddress -> "apiAddShortLinkMyAddress"
|
||||
is ApiSetProfileAddress -> "apiSetProfileAddress"
|
||||
is ApiAddressAutoAccept -> "apiAddressAutoAccept"
|
||||
is ApiAcceptContact -> "apiAcceptContact"
|
||||
|
||||
+28
@@ -49,6 +49,18 @@ fun GroupLinkView(
|
||||
creatingLink = false
|
||||
}
|
||||
}
|
||||
fun addShortLink() {
|
||||
creatingLink = true
|
||||
withBGApi {
|
||||
val link = chatModel.controller.apiAddShortLinkGroupLink(rhId, groupInfo.groupId)
|
||||
if (link != null) {
|
||||
groupLink = link.first
|
||||
groupLinkMemberRole.value = link.second
|
||||
onGroupLinkUpdated?.invoke(link)
|
||||
}
|
||||
creatingLink = false
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
if (groupLink == null && !creatingLink) {
|
||||
createLink()
|
||||
@@ -60,6 +72,7 @@ fun GroupLinkView(
|
||||
groupLinkMemberRole,
|
||||
creatingLink,
|
||||
createLink = ::createLink,
|
||||
addShortLink = ::addShortLink,
|
||||
updateLink = {
|
||||
val role = groupLinkMemberRole.value
|
||||
if (role != null) {
|
||||
@@ -105,6 +118,7 @@ fun GroupLinkLayout(
|
||||
groupLinkMemberRole: MutableState<GroupMemberRole?>,
|
||||
creatingLink: Boolean,
|
||||
createLink: () -> Unit,
|
||||
addShortLink: () -> Unit,
|
||||
updateLink: () -> Unit,
|
||||
deleteLink: () -> Unit,
|
||||
creatingGroup: Boolean = false,
|
||||
@@ -182,12 +196,26 @@ fun GroupLinkLayout(
|
||||
)
|
||||
}
|
||||
}
|
||||
if (groupLink.connShortLink == null && appPreferences.privacyShortLinks.get()) {
|
||||
AddShortLinkButton(addShortLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddShortLinkButton(onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_add),
|
||||
stringResource(MR.strings.add_short_link),
|
||||
onClick,
|
||||
iconColor = MaterialTheme.colors.primary,
|
||||
textColor = MaterialTheme.colors.primary,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState<GroupMemberRole?>, enabled: Boolean = true) {
|
||||
Row(
|
||||
|
||||
+30
@@ -43,6 +43,7 @@ fun UserAddressView(
|
||||
KeyChangeEffect(user.value?.remoteHostId, user.value?.userId) {
|
||||
close()
|
||||
}
|
||||
|
||||
fun setProfileAddress(on: Boolean) {
|
||||
progressIndicator = true
|
||||
withBGApi {
|
||||
@@ -81,6 +82,17 @@ fun UserAddressView(
|
||||
}
|
||||
}
|
||||
|
||||
fun addShortLink() {
|
||||
withBGApi {
|
||||
progressIndicator = true
|
||||
val userAddress = chatModel.controller.apiAddShortLinkMyAddress(user.value?.remoteHostId)
|
||||
if (userAddress != null) {
|
||||
chatModel.userAddress.value = userAddress
|
||||
}
|
||||
progressIndicator = false
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(autoCreateAddress) {
|
||||
if (chatModel.userAddress.value == null && autoCreateAddress) {
|
||||
createAddress()
|
||||
@@ -95,6 +107,7 @@ fun UserAddressView(
|
||||
userAddress = userAddress.value,
|
||||
shareViaProfile,
|
||||
createAddress = { createAddress() },
|
||||
addShortLink = { addShortLink() },
|
||||
learnMore = {
|
||||
ModalManager.start.showModal {
|
||||
UserAddressLearnMore()
|
||||
@@ -169,6 +182,7 @@ private fun UserAddressLayout(
|
||||
userAddress: UserContactLinkRec?,
|
||||
shareViaProfile: MutableState<Boolean>,
|
||||
createAddress: () -> Unit,
|
||||
addShortLink: () -> Unit,
|
||||
learnMore: () -> Unit,
|
||||
share: (String) -> Unit,
|
||||
sendEmail: (UserContactLinkRec) -> Unit,
|
||||
@@ -211,6 +225,9 @@ private fun UserAddressLayout(
|
||||
// ShareViaEmailButton { sendEmail(userAddress) }
|
||||
BusinessAddressToggle(autoAcceptState) { saveAas(autoAcceptState.value, autoAcceptStateSaved) }
|
||||
AddressSettingsButton(user, userAddress, shareViaProfile, setProfileAddress, saveAas)
|
||||
if (userAddress.connLinkContact.connShortLink == null && appPreferences.privacyShortLinks.get()) {
|
||||
AddShortLinkButton(addShortLink)
|
||||
}
|
||||
|
||||
if (autoAcceptState.value.business) {
|
||||
SectionTextFooter(stringResource(MR.strings.add_your_team_members_to_conversations))
|
||||
@@ -248,6 +265,17 @@ private fun CreateAddressButton(onClick: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddShortLinkButton(onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_add),
|
||||
stringResource(MR.strings.add_short_link),
|
||||
onClick,
|
||||
iconColor = MaterialTheme.colors.primary,
|
||||
textColor = MaterialTheme.colors.primary,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateOneTimeLinkButton() {
|
||||
val closeAll = { ModalManager.start.closeModals() }
|
||||
@@ -559,6 +587,7 @@ fun PreviewUserAddressLayoutNoAddress() {
|
||||
user = User.sampleData,
|
||||
userAddress = null,
|
||||
createAddress = {},
|
||||
addShortLink = {},
|
||||
share = { _ -> },
|
||||
deleteAddress = {},
|
||||
saveAas = { _, _ -> },
|
||||
@@ -592,6 +621,7 @@ fun PreviewUserAddressLayoutAddressCreated() {
|
||||
user = User.sampleData,
|
||||
userAddress = UserContactLinkRec(CreatedConnLink("https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D", null)),
|
||||
createAddress = {},
|
||||
addShortLink = {},
|
||||
share = { _ -> },
|
||||
deleteAddress = {},
|
||||
saveAas = { _, _ -> },
|
||||
|
||||
@@ -1058,6 +1058,7 @@
|
||||
<string name="address_settings">Address settings</string>
|
||||
<string name="business_address">Business address</string>
|
||||
<string name="add_your_team_members_to_conversations">Add your team members to the conversations.</string>
|
||||
<string name="add_short_link">Add short link</string>
|
||||
|
||||
<!-- CreateSimpleXAddress.kt -->
|
||||
<string name="continue_to_next_step">Continue</string>
|
||||
|
||||
@@ -373,6 +373,7 @@ data ChatCommand
|
||||
| APIGroupLinkMemberRole GroupId GroupMemberRole
|
||||
| APIDeleteGroupLink GroupId
|
||||
| APIGetGroupLink GroupId
|
||||
| APIAddShortLinkGroupLink GroupId
|
||||
| APICreateMemberContact GroupId GroupMemberId
|
||||
| APISendMemberContactInvitation {contactId :: ContactId, msgContent_ :: Maybe MsgContent}
|
||||
| GetUserProtoServers AProtocolType
|
||||
@@ -461,6 +462,7 @@ data ChatCommand
|
||||
| DeleteMyAddress
|
||||
| APIShowMyAddress UserId
|
||||
| ShowMyAddress
|
||||
| APIAddShortLinkMyAddress UserId
|
||||
| APISetProfileAddress UserId Bool
|
||||
| SetProfileAddress Bool
|
||||
| APIAddressAutoAccept UserId (Maybe AutoAccept)
|
||||
|
||||
@@ -1796,9 +1796,9 @@ processChatCommand' vr = \case
|
||||
CreateMyAddress short -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APICreateMyAddress userId short
|
||||
APIDeleteMyAddress userId -> withUserId userId $ \user@User {profile = p} -> do
|
||||
conns <- withFastStore $ \db -> getUserAddressConnections db vr user
|
||||
conn <- withFastStore $ \db -> getUserAddressConnection db vr user
|
||||
withChatLock "deleteMyAddress" $ do
|
||||
deleteAgentConnectionsAsync $ map aConnId conns
|
||||
deleteAgentConnectionAsync $ aConnId conn
|
||||
withFastStore' (`deleteUserAddress` user)
|
||||
let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing}
|
||||
r <- updateProfile_ user p' $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing
|
||||
@@ -1812,6 +1812,17 @@ processChatCommand' vr = \case
|
||||
CRUserContactLink user <$> withFastStore (`getUserAddress` user)
|
||||
ShowMyAddress -> withUser' $ \User {userId} ->
|
||||
processChatCommand $ APIShowMyAddress userId
|
||||
APIAddShortLinkMyAddress userId -> withUserId' userId $ \user -> do
|
||||
(ucl@UserContactLink {connLinkContact = CCLink connFullLink sLnk_}, conn) <-
|
||||
withFastStore $ \db -> (,) <$> getUserAddress db user <*> getUserAddressConnection db vr user
|
||||
when (isJust sLnk_) $ throwCmdError "address already has short link"
|
||||
sLnk <- withAgent $ \a -> setContactShortLink a (aConnId conn) ""
|
||||
case entityId conn of
|
||||
Just uclId -> do
|
||||
withFastStore' $ \db -> setUserContactLinkShortLink db uclId sLnk
|
||||
let ucl' = (ucl :: UserContactLink) {connLinkContact = CCLink connFullLink (Just sLnk)}
|
||||
pure $ CRUserContactLink user ucl'
|
||||
Nothing -> throwChatError $ CEException "no user contact link id"
|
||||
APISetProfileAddress userId False -> withUserId userId $ \user@User {profile = p} -> do
|
||||
let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing}
|
||||
updateProfile_ user p' $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing
|
||||
@@ -2403,6 +2414,17 @@ processChatCommand' vr = \case
|
||||
gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId
|
||||
(_, groupLink, mRole) <- withFastStore $ \db -> getGroupLink db user gInfo
|
||||
pure $ CRGroupLink user gInfo groupLink mRole
|
||||
APIAddShortLinkGroupLink groupId -> withUser $ \user -> do
|
||||
(gInfo, (uclId, _gLink@(CCLink connFullLink sLnk_), mRole), conn) <- withFastStore $ \db -> do
|
||||
gInfo <- getGroupInfo db vr user groupId
|
||||
gLink <- getGroupLink db user gInfo
|
||||
conn <- getGroupLinkConnection db vr user gInfo
|
||||
pure (gInfo, gLink, conn)
|
||||
when (isJust sLnk_) $ throwCmdError "group link already has short link"
|
||||
sLnk <- withAgent $ \a -> setContactShortLink a (aConnId conn) ""
|
||||
withFastStore' $ \db -> setUserContactLinkShortLink db uclId sLnk
|
||||
let groupLink' = CCLink connFullLink (Just sLnk)
|
||||
pure $ CRGroupLink user gInfo groupLink' mRole
|
||||
APICreateMemberContact gId gMemberId -> withUser $ \user -> do
|
||||
(g, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId
|
||||
assertUserGroupRole g GRAuthor
|
||||
@@ -4222,6 +4244,7 @@ chatCommandP =
|
||||
"/_set link role #" *> (APIGroupLinkMemberRole <$> A.decimal <*> memberRole),
|
||||
"/_delete link #" *> (APIDeleteGroupLink <$> A.decimal),
|
||||
"/_get link #" *> (APIGetGroupLink <$> A.decimal),
|
||||
"/_short link #" *> (APIAddShortLinkGroupLink <$> A.decimal),
|
||||
"/create link #" *> (CreateGroupLink <$> displayNameP <*> (memberRole <|> pure GRMember) <*> shortP),
|
||||
"/set link role #" *> (GroupLinkMemberRole <$> displayNameP <*> memberRole),
|
||||
"/delete link #" *> (DeleteGroupLink <$> displayNameP),
|
||||
@@ -4277,6 +4300,7 @@ chatCommandP =
|
||||
("/delete_address" <|> "/da") $> DeleteMyAddress,
|
||||
"/_show_address " *> (APIShowMyAddress <$> A.decimal),
|
||||
("/show_address" <|> "/sa") $> ShowMyAddress,
|
||||
"/_short_link_address " *> (APIAddShortLinkMyAddress <$> A.decimal),
|
||||
"/_profile_address " *> (APISetProfileAddress <$> A.decimal <* A.space <*> onOffP),
|
||||
("/profile_address " <|> "/pa ") *> (SetProfileAddress <$> onOffP),
|
||||
"/_auto_accept " *> (APIAddressAutoAccept <$> A.decimal <* A.space <*> autoAcceptP),
|
||||
|
||||
@@ -43,7 +43,7 @@ module Simplex.Chat.Store.Profiles
|
||||
setUserProfileContactLink,
|
||||
getUserContactProfiles,
|
||||
createUserContactLink,
|
||||
getUserAddressConnections,
|
||||
getUserAddressConnection,
|
||||
getUserContactLinks,
|
||||
deleteUserAddress,
|
||||
getUserAddress,
|
||||
@@ -51,6 +51,7 @@ module Simplex.Chat.Store.Profiles
|
||||
getGroupLinkInfo,
|
||||
getUserContactLinkByConnReq,
|
||||
getUserContactLinkViaShortLink,
|
||||
setUserContactLinkShortLink,
|
||||
getContactWithoutConnViaAddress,
|
||||
updateUserAddressAutoAccept,
|
||||
getProtocolServers,
|
||||
@@ -363,26 +364,21 @@ createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) subMo
|
||||
userContactLinkId <- insertedRowId db
|
||||
void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId ConnNew initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff
|
||||
|
||||
getUserAddressConnections :: DB.Connection -> VersionRangeChat -> User -> ExceptT StoreError IO [Connection]
|
||||
getUserAddressConnections db vr User {userId} = do
|
||||
cs <- liftIO getUserAddressConnections_
|
||||
if null cs then throwError SEUserContactLinkNotFound else pure cs
|
||||
where
|
||||
getUserAddressConnections_ :: IO [Connection]
|
||||
getUserAddressConnections_ =
|
||||
map (toConnection vr)
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM connections c
|
||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|
||||
|]
|
||||
(userId, userId)
|
||||
getUserAddressConnection :: DB.Connection -> VersionRangeChat -> User -> ExceptT StoreError IO Connection
|
||||
getUserAddressConnection db vr User {userId} = do
|
||||
ExceptT . firstRow (toConnection vr) SEUserContactLinkNotFound $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM connections c
|
||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|
||||
|]
|
||||
(userId, userId)
|
||||
|
||||
getUserContactLinks :: DB.Connection -> VersionRangeChat -> User -> IO [(Connection, UserContact)]
|
||||
getUserContactLinks db vr User {userId} =
|
||||
@@ -531,6 +527,17 @@ userContactLinkQuery =
|
||||
FROM user_contact_links
|
||||
|]
|
||||
|
||||
setUserContactLinkShortLink :: DB.Connection -> Int64 -> ShortLinkContact -> IO ()
|
||||
setUserContactLinkShortLink db userContactLinkId shortLink =
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE user_contact_links
|
||||
SET short_link_contact = ?
|
||||
WHERE user_contact_link_id = ?
|
||||
|]
|
||||
(shortLink, userContactLinkId)
|
||||
|
||||
getContactWithoutConnViaAddress :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe Contact)
|
||||
getContactWithoutConnViaAddress db vr user@User {userId} (cReqSchema1, cReqSchema2) = do
|
||||
ctId_ <-
|
||||
|
||||
@@ -501,19 +501,6 @@ Query:
|
||||
Plan:
|
||||
SEARCH messages USING INDEX idx_messages_group_id_shared_msg_id (group_id=? AND shared_msg_id=?)
|
||||
|
||||
Query:
|
||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM connections c
|
||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|
||||
|
||||
Plan:
|
||||
SEARCH uc USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
|
||||
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
|
||||
FROM chat_items
|
||||
@@ -2823,6 +2810,19 @@ Plan:
|
||||
SEARCH uc USING INDEX idx_user_contact_links_group_id (group_id=?)
|
||||
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM connections c
|
||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|
||||
|
||||
Plan:
|
||||
SEARCH uc USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
|
||||
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
|
||||
Reference in New Issue
Block a user