mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-28 20:29:53 +00:00
Merge branch 'short-links' into f/short-links-kotlin-user-picker
This commit is contained in:
commit
86953de2f4
10 changed files with 94 additions and 76 deletions
|
@ -1533,9 +1533,10 @@ func acceptContactRequest(incognito: Bool, contactRequestId: Int64) async {
|
||||||
NetworkModel.shared.setContactNetworkStatus(contact, .connected)
|
NetworkModel.shared.setContactNetworkStatus(contact, .connected)
|
||||||
}
|
}
|
||||||
if contact.sndReady {
|
if contact.sndReady {
|
||||||
|
let chatId = chat.id
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
dismissAllSheets(animated: true) {
|
dismissAllSheets(animated: true) {
|
||||||
ItemsModel.shared.loadOpenChat(chat.id)
|
ItemsModel.shared.loadOpenChat(chatId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -707,6 +707,7 @@ struct ComposeView: View {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.chatModel.updateContact(contact)
|
self.chatModel.updateContact(contact)
|
||||||
clearState()
|
clearState()
|
||||||
|
NetworkModel.shared.setContactNetworkStatus(contact, .connected)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AlertManager.shared.showAlertMsg(title: "Empty message!")
|
AlertManager.shared.showAlertMsg(title: "Empty message!")
|
||||||
|
@ -744,6 +745,7 @@ struct ComposeView: View {
|
||||||
if let contact = await apiConnectPreparedContact(contactId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) {
|
if let contact = await apiConnectPreparedContact(contactId: chat.chatInfo.apiId, incognito: incognitoGroupDefault.get(), msg: mc) {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.chatModel.updateContact(contact)
|
self.chatModel.updateContact(contact)
|
||||||
|
NetworkModel.shared.setContactNetworkStatus(contact, .connected)
|
||||||
clearState()
|
clearState()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -455,7 +455,7 @@ struct ChatPreviewView: View {
|
||||||
let size = dynamicSize(userFont).incognitoSize
|
let size = dynamicSize(userFont).incognitoSize
|
||||||
switch chat.chatInfo {
|
switch chat.chatInfo {
|
||||||
case let .direct(contact):
|
case let .direct(contact):
|
||||||
if contact.active && contact.activeConn != nil {
|
if contact.active, let status = contact.activeConn?.connStatus, status == .ready || status == .sndReady {
|
||||||
NetworkStatusView(contact: contact, size: size)
|
NetworkStatusView(contact: contact, size: size)
|
||||||
} else {
|
} else {
|
||||||
incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
|
incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
|
||||||
|
|
|
@ -1735,8 +1735,8 @@ public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
|
||||||
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 nextSendGrpInv: Bool { get { contactGroupMemberId != nil && !contactGrpInvSent } }
|
public var nextSendGrpInv: Bool { get { contactGroupMemberId != nil && !contactGrpInvSent } }
|
||||||
public var nextConnectPrepared: Bool { preparedContact != nil && activeConn == nil }
|
public var nextConnectPrepared: Bool { preparedContact != nil && (activeConn == nil || activeConn?.connStatus == .prepared) }
|
||||||
public var nextAcceptContactRequest: Bool { contactRequestId != nil && activeConn == nil }
|
public var nextAcceptContactRequest: Bool { contactRequestId != nil && (activeConn == nil || activeConn?.connStatus == .new) }
|
||||||
public var sendMsgToConnect: Bool { nextSendGrpInv || nextConnectPrepared }
|
public var sendMsgToConnect: Bool { nextSendGrpInv || nextConnectPrepared }
|
||||||
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 } }
|
||||||
|
@ -1833,7 +1833,7 @@ public struct Connection: Decodable, Hashable {
|
||||||
public var connId: Int64
|
public var connId: Int64
|
||||||
public var agentConnId: String
|
public var agentConnId: String
|
||||||
public var peerChatVRange: VersionRange
|
public var peerChatVRange: VersionRange
|
||||||
var connStatus: ConnStatus
|
public var connStatus: ConnStatus
|
||||||
public var connLevel: Int
|
public var connLevel: Int
|
||||||
public var viaGroupLink: Bool
|
public var viaGroupLink: Bool
|
||||||
public var customUserProfileId: Int64?
|
public var customUserProfileId: Int64?
|
||||||
|
|
|
@ -456,7 +456,7 @@ data ChatCommand
|
||||||
| APIChangePreparedGroupUser GroupId UserId
|
| APIChangePreparedGroupUser GroupId UserId
|
||||||
| APIConnectPreparedContact {contactId :: ContactId, incognito :: IncognitoEnabled, msgContent_ :: Maybe MsgContent}
|
| APIConnectPreparedContact {contactId :: ContactId, incognito :: IncognitoEnabled, msgContent_ :: Maybe MsgContent}
|
||||||
| APIConnectPreparedGroup GroupId IncognitoEnabled (Maybe MsgContent)
|
| APIConnectPreparedGroup GroupId IncognitoEnabled (Maybe MsgContent)
|
||||||
| APIConnect UserId IncognitoEnabled (Maybe ACreatedConnLink)
|
| APIConnect UserId IncognitoEnabled ACreatedConnLink
|
||||||
| Connect IncognitoEnabled (Maybe AConnectionLink)
|
| Connect IncognitoEnabled (Maybe AConnectionLink)
|
||||||
| APIConnectContactViaAddress UserId IncognitoEnabled ContactId
|
| APIConnectContactViaAddress UserId IncognitoEnabled ContactId
|
||||||
| ConnectSimplex IncognitoEnabled -- UserId (not used in UI)
|
| ConnectSimplex IncognitoEnabled -- UserId (not used in UI)
|
||||||
|
|
|
@ -1157,7 +1157,6 @@ processChatCommand' vr = \case
|
||||||
when (shortLinkDataSet && incognito) $ throwCmdError "incognito not allowed for address with short link data"
|
when (shortLinkDataSet && incognito) $ throwCmdError "incognito not allowed for address with short link data"
|
||||||
withUserContactLock "acceptContact" uclId $ do
|
withUserContactLock "acceptContact" uclId $ do
|
||||||
cReq <- withFastStore $ \db -> getContactRequest db user connReqId
|
cReq <- withFastStore $ \db -> getContactRequest db user connReqId
|
||||||
-- TODO [short links] accept async, move to continuation on JOIN?
|
|
||||||
(ct, conn@Connection {connId}, sqSecured) <- acceptContactRequest user cReq incognito
|
(ct, conn@Connection {connId}, sqSecured) <- acceptContactRequest user cReq incognito
|
||||||
let contactUsed = isNothing gLinkInfo_
|
let contactUsed = isNothing gLinkInfo_
|
||||||
ct' <- withStore' $ \db -> do
|
ct' <- withStore' $ \db -> do
|
||||||
|
@ -1795,18 +1794,16 @@ processChatCommand' vr = \case
|
||||||
Contact {preparedContact} <- withFastStore $ \db -> getContact db vr user contactId
|
Contact {preparedContact} <- withFastStore $ \db -> getContact db vr user contactId
|
||||||
case preparedContact of
|
case preparedContact of
|
||||||
Nothing -> throwCmdError "contact doesn't have link to connect"
|
Nothing -> throwCmdError "contact doesn't have link to connect"
|
||||||
Just PreparedContact {connLinkToConnect = ACCL SCMInvitation ccLink} ->
|
Just PreparedContact {connLinkToConnect = ACCL SCMInvitation ccLink} -> do
|
||||||
connectViaInvitation user incognito ccLink (Just contactId) >>= \case
|
(_, customUserProfile) <- connectViaInvitation user incognito ccLink (Just contactId)
|
||||||
CRSentConfirmation {customUserProfile} -> do
|
-- get updated contact with connection
|
||||||
-- get updated contact with connection
|
ct' <- withFastStore $ \db -> getContact db vr user contactId
|
||||||
ct' <- withFastStore $ \db -> getContact db vr user contactId
|
forM_ msgContent_ $ \mc -> do
|
||||||
forM_ msgContent_ $ \mc -> do
|
let evt = XMsgNew $ MCSimple (extMsgContent mc Nothing)
|
||||||
let evt = XMsgNew $ MCSimple (extMsgContent mc Nothing)
|
(msg, _) <- sendDirectContactMessage user ct' evt
|
||||||
(msg, _) <- sendDirectContactMessage user ct' evt
|
ci <- saveSndChatItem user (CDDirectSnd ct') msg (CISndMsgContent mc)
|
||||||
ci <- saveSndChatItem user (CDDirectSnd ct') msg (CISndMsgContent mc)
|
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct') ci]
|
||||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct') ci]
|
pure $ CRStartedConnectionToContact user ct' customUserProfile
|
||||||
pure $ CRStartedConnectionToContact user ct' customUserProfile
|
|
||||||
cr -> pure cr
|
|
||||||
Just PreparedContact {connLinkToConnect = ACCL SCMContact ccLink, welcomeSharedMsgId} -> do
|
Just PreparedContact {connLinkToConnect = ACCL SCMContact ccLink, welcomeSharedMsgId} -> do
|
||||||
-- TODO [short links] reuse welcomeSharedMsgId
|
-- TODO [short links] reuse welcomeSharedMsgId
|
||||||
msg_ <- forM msgContent_ $ \mc -> (,mc) <$> getSharedMsgId
|
msg_ <- forM msgContent_ $ \mc -> (,mc) <$> getSharedMsgId
|
||||||
|
@ -1831,11 +1828,12 @@ processChatCommand' vr = \case
|
||||||
gInfo' <- withFastStore $ \db -> getGroupInfo db vr user groupId
|
gInfo' <- withFastStore $ \db -> getGroupInfo db vr user groupId
|
||||||
pure $ CRStartedConnectionToGroup user gInfo' customUserProfile
|
pure $ CRStartedConnectionToGroup user gInfo' customUserProfile
|
||||||
cr -> pure cr
|
cr -> pure cr
|
||||||
APIConnect userId incognito (Just (ACCL SCMInvitation ccLink)) -> withUserId userId $ \user ->
|
APIConnect userId incognito acl -> withUserId userId $ \user -> case acl of
|
||||||
connectViaInvitation user incognito ccLink Nothing
|
ACCL SCMInvitation ccLink -> do
|
||||||
APIConnect userId incognito (Just (ACCL SCMContact ccLink)) -> withUserId userId $ \user ->
|
(dbConnId, incognitoProfile) <- connectViaInvitation user incognito ccLink Nothing
|
||||||
connectViaContact user incognito ccLink Nothing Nothing Nothing
|
pcc <- withFastStore $ \db -> getPendingContactConnection db userId dbConnId
|
||||||
APIConnect _ _ Nothing -> throwChatError CEInvalidConnReq
|
pure $ CRSentConfirmation user pcc incognitoProfile
|
||||||
|
ACCL SCMContact ccLink -> connectViaContact user incognito ccLink Nothing Nothing Nothing
|
||||||
Connect incognito (Just cLink@(ACL m cLink')) -> withUser $ \user -> do
|
Connect incognito (Just cLink@(ACL m cLink')) -> withUser $ \user -> do
|
||||||
(ccLink, plan) <- connectPlan user cLink `catchChatError` \e -> case cLink' of CLFull cReq -> pure (ACCL m (CCLink cReq Nothing), CPInvitationLink (ILPOk Nothing)); _ -> throwError e
|
(ccLink, plan) <- connectPlan user cLink `catchChatError` \e -> case cLink' of CLFull cReq -> pure (ACCL m (CCLink cReq Nothing), CPInvitationLink (ILPOk Nothing)); _ -> throwError e
|
||||||
connectWithPlan user incognito ccLink plan
|
connectWithPlan user incognito ccLink plan
|
||||||
|
@ -2869,9 +2867,9 @@ processChatCommand' vr = \case
|
||||||
CTGroup -> withFastStore $ \db -> getGroupChatItemIdByText' db user cId msg
|
CTGroup -> withFastStore $ \db -> getGroupChatItemIdByText' db user cId msg
|
||||||
CTLocal -> withFastStore $ \db -> getLocalChatItemIdByText' db user cId msg
|
CTLocal -> withFastStore $ \db -> getLocalChatItemIdByText' db user cId msg
|
||||||
_ -> throwCmdError "not supported"
|
_ -> throwCmdError "not supported"
|
||||||
connectViaInvitation :: User -> IncognitoEnabled -> CreatedLinkInvitation -> Maybe ContactId -> CM ChatResponse
|
connectViaInvitation :: User -> IncognitoEnabled -> CreatedLinkInvitation -> Maybe ContactId -> CM (Int64, Maybe Profile)
|
||||||
connectViaInvitation user@User {userId} incognito (CCLink cReq@(CRInvitationUri crData e2e) sLnk_) contactId_ =
|
connectViaInvitation user@User {userId} incognito (CCLink cReq@(CRInvitationUri crData e2e) sLnk_) contactId_ =
|
||||||
withInvitationLock "connect" (strEncode cReq) . procCmd $ do
|
withInvitationLock "connect" (strEncode cReq) $ do
|
||||||
subMode <- chatReadVar subscriptionMode
|
subMode <- chatReadVar subscriptionMode
|
||||||
-- [incognito] generate profile to send
|
-- [incognito] generate profile to send
|
||||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||||
|
@ -2884,24 +2882,22 @@ processChatCommand' vr = \case
|
||||||
dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend
|
dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend
|
||||||
withFastStore' (\db -> getConnectionEntityByConnReq db vr user cReqs) >>= \case
|
withFastStore' (\db -> getConnectionEntityByConnReq db vr user cReqs) >>= \case
|
||||||
Nothing -> joinNewConn chatV dm
|
Nothing -> joinNewConn chatV dm
|
||||||
Just (RcvDirectMsgConnection conn@Connection {connId, connStatus, contactConnInitiated} _ct_)
|
Just (RcvDirectMsgConnection conn@Connection {connId = dbConnId, connStatus, contactConnInitiated} _ct_)
|
||||||
| connStatus == ConnNew && contactConnInitiated -> joinNewConn chatV dm -- own connection link
|
| connStatus == ConnNew && contactConnInitiated -> joinNewConn chatV dm -- own connection link
|
||||||
| connStatus == ConnPrepared -> do
|
| connStatus == ConnPrepared -> joinPreparedConn dbConnId (aConnId conn) dm -- retrying join after error
|
||||||
-- retrying join after error
|
|
||||||
pcc <- withFastStore $ \db -> getPendingContactConnection db userId connId
|
|
||||||
joinPreparedConn (aConnId conn) pcc dm
|
|
||||||
Just ent -> throwCmdError $ "connection is not RcvDirectMsgConnection: " <> show (connEntityInfo ent)
|
Just ent -> throwCmdError $ "connection is not RcvDirectMsgConnection: " <> show (connEntityInfo ent)
|
||||||
where
|
where
|
||||||
joinNewConn chatV dm = do
|
joinNewConn chatV dm = do
|
||||||
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup'
|
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup'
|
||||||
let ccLink = CCLink cReq $ serverShortLink <$> sLnk_
|
let ccLink = CCLink cReq $ serverShortLink <$> sLnk_
|
||||||
pcc <- withFastStore' $ \db -> createDirectConnection db user connId ccLink contactId_ ConnPrepared (incognitoProfile $> profileToSend) subMode chatV pqSup'
|
createdAt <- liftIO getCurrentTime
|
||||||
joinPreparedConn connId pcc dm
|
(dbConnId, _) <- withFastStore' $ \db -> createDirectConnection_ db userId connId ccLink contactId_ ConnPrepared (incognitoProfile $> profileToSend) subMode chatV pqSup' createdAt
|
||||||
joinPreparedConn connId pcc@PendingContactConnection {pccConnId} dm = do
|
joinPreparedConn dbConnId connId dm
|
||||||
|
joinPreparedConn dbConnId connId dm = do
|
||||||
(sqSecured, _serviceId) <- withAgent $ \a -> joinConnection a (aUserId user) connId True cReq dm pqSup' subMode
|
(sqSecured, _serviceId) <- withAgent $ \a -> joinConnection a (aUserId user) connId True cReq dm pqSup' subMode
|
||||||
let newStatus = if sqSecured then ConnSndReady else ConnJoined
|
let newStatus = if sqSecured then ConnSndReady else ConnJoined
|
||||||
withFastStore' $ \db -> updateConnectionStatusFromTo db pccConnId ConnPrepared newStatus
|
withFastStore' $ \db -> updateConnectionStatusFromTo db dbConnId ConnPrepared newStatus
|
||||||
pure $ CRSentConfirmation user pcc {pccConnStatus = newStatus} incognitoProfile
|
pure (dbConnId, incognitoProfile)
|
||||||
cReqs =
|
cReqs =
|
||||||
( CRInvitationUri crData {crScheme = SSSimplex} e2e,
|
( CRInvitationUri crData {crScheme = SSSimplex} e2e,
|
||||||
CRInvitationUri crData {crScheme = simplexChat} e2e
|
CRInvitationUri crData {crScheme = simplexChat} e2e
|
||||||
|
@ -3363,7 +3359,7 @@ processChatCommand' vr = \case
|
||||||
case plan of
|
case plan of
|
||||||
CPContactAddress (CAPContactViaAddress Contact {contactId}) ->
|
CPContactAddress (CAPContactViaAddress Contact {contactId}) ->
|
||||||
processChatCommand $ APIConnectContactViaAddress userId incognito contactId
|
processChatCommand $ APIConnectContactViaAddress userId incognito contactId
|
||||||
_ -> processChatCommand $ APIConnect userId incognito (Just ccLink)
|
_ -> processChatCommand $ APIConnect userId incognito ccLink
|
||||||
| otherwise = pure $ CRConnectionPlan user ccLink plan
|
| otherwise = pure $ CRConnectionPlan user ccLink plan
|
||||||
invitationRequestPlan :: User -> ConnReqInvitation -> Maybe ContactShortLinkData -> CM ConnectionPlan
|
invitationRequestPlan :: User -> ConnReqInvitation -> Maybe ContactShortLinkData -> CM ConnectionPlan
|
||||||
invitationRequestPlan user cReq contactSLinkData_ = do
|
invitationRequestPlan user cReq contactSLinkData_ = do
|
||||||
|
@ -4480,7 +4476,7 @@ chatCommandP =
|
||||||
"/_connect contact @" *> (APIConnectPreparedContact <$> A.decimal <*> incognitoOnOffP <*> optional (A.space *> msgContentP)),
|
"/_connect contact @" *> (APIConnectPreparedContact <$> A.decimal <*> incognitoOnOffP <*> optional (A.space *> msgContentP)),
|
||||||
"/_connect group #" *> (APIConnectPreparedGroup <$> A.decimal <*> incognitoOnOffP <*> optional (A.space *> msgContentP)),
|
"/_connect group #" *> (APIConnectPreparedGroup <$> A.decimal <*> incognitoOnOffP <*> optional (A.space *> msgContentP)),
|
||||||
"/_connect " *> (APIAddContact <$> A.decimal <*> incognitoOnOffP),
|
"/_connect " *> (APIAddContact <$> A.decimal <*> incognitoOnOffP),
|
||||||
"/_connect " *> (APIConnect <$> A.decimal <*> incognitoOnOffP <* A.space <*> connLinkP_),
|
"/_connect " *> (APIConnect <$> A.decimal <*> incognitoOnOffP <* A.space <*> connLinkP),
|
||||||
"/_set incognito :" *> (APISetConnectionIncognito <$> A.decimal <* A.space <*> onOffP),
|
"/_set incognito :" *> (APISetConnectionIncognito <$> A.decimal <* A.space <*> onOffP),
|
||||||
"/_set conn user :" *> (APIChangeConnectionUser <$> A.decimal <* A.space <*> A.decimal),
|
"/_set conn user :" *> (APIChangeConnectionUser <$> A.decimal <* A.space <*> A.decimal),
|
||||||
("/connect" <|> "/c") *> (AddContact <$> incognitoP),
|
("/connect" <|> "/c") *> (AddContact <$> incognitoP),
|
||||||
|
@ -4598,8 +4594,6 @@ chatCommandP =
|
||||||
cReq <- strP
|
cReq <- strP
|
||||||
sLink_ <- optional (A.space *> strP)
|
sLink_ <- optional (A.space *> strP)
|
||||||
pure $ CCLink cReq sLink_
|
pure $ CCLink cReq sLink_
|
||||||
connLinkP_ =
|
|
||||||
((Just <$> connLinkP) <|> A.takeTill (== ' ') $> Nothing)
|
|
||||||
incognitoP = (A.space *> ("incognito" <|> "i")) $> True <|> pure False
|
incognitoP = (A.space *> ("incognito" <|> "i")) $> True <|> pure False
|
||||||
incognitoOnOffP = (A.space *> "incognito=" *> onOffP) <|> pure False
|
incognitoOnOffP = (A.space *> "incognito=" *> onOffP) <|> pure False
|
||||||
imagePrefix = (<>) <$> "data:" <*> ("image/png;base64," <|> "image/jpg;base64,")
|
imagePrefix = (<>) <$> "data:" <*> ("image/png;base64," <|> "image/jpg;base64,")
|
||||||
|
|
|
@ -869,6 +869,14 @@ getRcvFilePath fileId fPath_ fn keepHandle = case fPath_ of
|
||||||
liftIO $ B.hPut h "" >> hFlush h
|
liftIO $ B.hPut h "" >> hFlush h
|
||||||
| otherwise = liftIO $ B.writeFile fPath ""
|
| otherwise = liftIO $ B.writeFile fPath ""
|
||||||
|
|
||||||
|
-- TODO [short links]
|
||||||
|
-- Please note:
|
||||||
|
-- - the connection here is created as ConnNew, even though when joining it is created as ConnPrepared.
|
||||||
|
-- (changing it is risky, as there may be existing "prepared" connections that were not accepted in ConnNew status).
|
||||||
|
-- - after accepted, the status is changed by this func caller to ConnSndReady if it is sndSecure, and not changed otherwise - joined changed to ConnJoined in this case.
|
||||||
|
-- - xContactId is set on the contact at the first acceptance attempt, not after accept success, which prevents profile updates after such attempt.
|
||||||
|
-- It may be reasonable to set it when contact is first prepared, but then we can't use it to ignore requests after acceptance,
|
||||||
|
-- and it may lead to race conditions with XInfo events.
|
||||||
acceptContactRequest :: User -> UserContactRequest -> IncognitoEnabled -> CM (Contact, Connection, SndQueueSecured)
|
acceptContactRequest :: User -> UserContactRequest -> IncognitoEnabled -> CM (Contact, Connection, SndQueueSecured)
|
||||||
acceptContactRequest user@User {userId} UserContactRequest {agentInvitationId = AgentInvId invId, contactId_, cReqChatVRange, localDisplayName = cName, profileId, profile = cp, userContactLinkId, xContactId, pqSupport} incognito = do
|
acceptContactRequest user@User {userId} UserContactRequest {agentInvitationId = AgentInvId invId, contactId_, cReqChatVRange, localDisplayName = cName, profileId, profile = cp, userContactLinkId, xContactId, pqSupport} incognito = do
|
||||||
subMode <- chatReadVar subscriptionMode
|
subMode <- chatReadVar subscriptionMode
|
||||||
|
|
|
@ -23,6 +23,7 @@ module Simplex.Chat.Store.Direct
|
||||||
-- * Contacts and connections functions
|
-- * Contacts and connections functions
|
||||||
getPendingContactConnection,
|
getPendingContactConnection,
|
||||||
deletePendingContactConnection,
|
deletePendingContactConnection,
|
||||||
|
createDirectConnection_,
|
||||||
createDirectConnection,
|
createDirectConnection,
|
||||||
createIncognitoProfile,
|
createIncognitoProfile,
|
||||||
createConnReqConnection,
|
createConnReqConnection,
|
||||||
|
@ -63,7 +64,6 @@ module Simplex.Chat.Store.Direct
|
||||||
deleteContactRequest,
|
deleteContactRequest,
|
||||||
createContactFromRequest,
|
createContactFromRequest,
|
||||||
createAcceptedContactConn,
|
createAcceptedContactConn,
|
||||||
createAcceptedContact,
|
|
||||||
updateContactAccepted,
|
updateContactAccepted,
|
||||||
getUserByContactRequestId,
|
getUserByContactRequestId,
|
||||||
getPendingContactConnections,
|
getPendingContactConnections,
|
||||||
|
@ -235,8 +235,13 @@ getContactByConnReqHash db vr user@User {userId} cReqHash = do
|
||||||
mapM (addDirectChatTags db) ct_
|
mapM (addDirectChatTags db) ct_
|
||||||
|
|
||||||
createDirectConnection :: DB.Connection -> User -> ConnId -> CreatedLinkInvitation -> Maybe ContactId -> ConnStatus -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> IO PendingContactConnection
|
createDirectConnection :: DB.Connection -> User -> ConnId -> CreatedLinkInvitation -> Maybe ContactId -> ConnStatus -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> IO PendingContactConnection
|
||||||
createDirectConnection db User {userId} acId ccLink@(CCLink cReq shortLinkInv) contactId_ pccConnStatus incognitoProfile subMode chatV pqSup = do
|
createDirectConnection db User {userId} acId ccLink contactId_ pccConnStatus incognitoProfile subMode chatV pqSup = do
|
||||||
createdAt <- getCurrentTime
|
createdAt <- getCurrentTime
|
||||||
|
(pccConnId, customUserProfileId) <- createDirectConnection_ db userId acId ccLink contactId_ pccConnStatus incognitoProfile subMode chatV pqSup createdAt
|
||||||
|
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = False, viaUserContactLink = Nothing, groupLinkId = Nothing, customUserProfileId, connLinkInv = Just ccLink, localAlias = "", createdAt, updatedAt = createdAt}
|
||||||
|
|
||||||
|
createDirectConnection_ :: DB.Connection -> UserId -> ConnId -> CreatedLinkInvitation -> Maybe ContactId -> ConnStatus -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> UTCTime -> IO (Int64, Maybe Int64)
|
||||||
|
createDirectConnection_ db userId acId (CCLink cReq shortLinkInv) contactId_ pccConnStatus incognitoProfile subMode chatV pqSup createdAt = do
|
||||||
customUserProfileId <- mapM (createIncognitoProfile_ db userId createdAt) incognitoProfile
|
customUserProfileId <- mapM (createIncognitoProfile_ db userId createdAt) incognitoProfile
|
||||||
let contactConnInitiated = pccConnStatus == ConnNew
|
let contactConnInitiated = pccConnStatus == ConnNew
|
||||||
DB.execute
|
DB.execute
|
||||||
|
@ -250,8 +255,8 @@ createDirectConnection db User {userId} acId ccLink@(CCLink cReq shortLinkInv) c
|
||||||
( (userId, acId, cReq, shortLinkInv, pccConnStatus, ConnContact, contactId_, BI contactConnInitiated, customUserProfileId)
|
( (userId, acId, cReq, shortLinkInv, pccConnStatus, ConnContact, contactId_, BI contactConnInitiated, customUserProfileId)
|
||||||
:. (createdAt, createdAt, BI (subMode == SMOnlyCreate), chatV, pqSup, pqSup)
|
:. (createdAt, createdAt, BI (subMode == SMOnlyCreate), chatV, pqSup, pqSup)
|
||||||
)
|
)
|
||||||
pccConnId <- insertedRowId db
|
dbConnId <- insertedRowId db
|
||||||
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = False, viaUserContactLink = Nothing, groupLinkId = Nothing, customUserProfileId, connLinkInv = Just ccLink, localAlias = "", createdAt, updatedAt = createdAt}
|
pure (dbConnId, customUserProfileId)
|
||||||
|
|
||||||
createIncognitoProfile :: DB.Connection -> User -> Profile -> IO Int64
|
createIncognitoProfile :: DB.Connection -> User -> Profile -> IO Int64
|
||||||
createIncognitoProfile db User {userId} p = do
|
createIncognitoProfile db User {userId} p = do
|
||||||
|
@ -777,37 +782,6 @@ createAcceptedContactConn db User {userId} uclId contactId agentConnId connChatV
|
||||||
ExistingIncognito LocalProfile {profileId = pId} -> pure pId
|
ExistingIncognito LocalProfile {profileId = pId} -> pure pId
|
||||||
createConnection_ db userId ConnContact (Just contactId) agentConnId ConnNew connChatVersion cReqChatVRange Nothing (Just uclId) customUserProfileId 0 currentTs subMode pqSup
|
createConnection_ db userId ConnContact (Just contactId) agentConnId ConnNew connChatVersion cReqChatVRange Nothing (Just uclId) customUserProfileId 0 currentTs subMode pqSup
|
||||||
|
|
||||||
createAcceptedContact :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ConnId -> VersionChat -> VersionRangeChat -> Profile -> Maybe XContactId -> PQSupport -> Maybe IncognitoProfile -> SubscriptionMode -> ExceptT StoreError IO (Contact, Connection)
|
|
||||||
createAcceptedContact
|
|
||||||
db
|
|
||||||
vr
|
|
||||||
user@User {userId}
|
|
||||||
uclId
|
|
||||||
agentConnId
|
|
||||||
connChatVersion
|
|
||||||
cReqChatVRange
|
|
||||||
Profile {displayName, fullName, image, contactLink, preferences}
|
|
||||||
xContactId
|
|
||||||
pqSup
|
|
||||||
incognitoProfile
|
|
||||||
subMode = do
|
|
||||||
currentTs <- liftIO getCurrentTime
|
|
||||||
let userPreferences = fromMaybe emptyChatPrefs $ incognitoProfile >> preferences
|
|
||||||
contactId <- ExceptT . withLocalDisplayName db userId displayName $ \ldn -> do
|
|
||||||
DB.execute
|
|
||||||
db
|
|
||||||
"INSERT INTO contact_profiles (display_name, full_name, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
|
|
||||||
(displayName, fullName, image, contactLink, userId, preferences, currentTs, currentTs)
|
|
||||||
profileId <- insertedRowId db
|
|
||||||
DB.execute
|
|
||||||
db
|
|
||||||
"INSERT INTO contacts (user_id, local_display_name, contact_profile_id, enable_ntfs, user_preferences, created_at, updated_at, chat_ts, xcontact_id, contact_used) VALUES (?,?,?,?,?,?,?,?,?,?)"
|
|
||||||
(userId, ldn, profileId, BI True, userPreferences, currentTs, currentTs, currentTs, xContactId, BI True)
|
|
||||||
Right <$> insertedRowId db
|
|
||||||
conn <- liftIO $ createAcceptedContactConn db user uclId contactId agentConnId connChatVersion cReqChatVRange pqSup incognitoProfile subMode currentTs
|
|
||||||
ct <- getContact db vr user contactId
|
|
||||||
pure (ct, conn)
|
|
||||||
|
|
||||||
updateContactAccepted :: DB.Connection -> User -> Contact -> Bool -> IO ()
|
updateContactAccepted :: DB.Connection -> User -> Contact -> Bool -> IO ()
|
||||||
updateContactAccepted db User {userId} Contact {contactId} contactUsed =
|
updateContactAccepted db User {userId} Contact {contactId} contactUsed =
|
||||||
DB.execute
|
DB.execute
|
||||||
|
|
|
@ -99,7 +99,6 @@ import Data.Type.Equality
|
||||||
import Data.Word (Word32)
|
import Data.Word (Word32)
|
||||||
import Simplex.Chat.Messages
|
import Simplex.Chat.Messages
|
||||||
import Simplex.Chat.Messages.CIContent
|
import Simplex.Chat.Messages.CIContent
|
||||||
import Simplex.Chat.Protocol
|
|
||||||
import Simplex.Chat.Store.Direct
|
import Simplex.Chat.Store.Direct
|
||||||
import Simplex.Chat.Store.Messages
|
import Simplex.Chat.Store.Messages
|
||||||
import Simplex.Chat.Store.Profiles
|
import Simplex.Chat.Store.Profiles
|
||||||
|
|
|
@ -118,6 +118,8 @@ chatProfileTests = do
|
||||||
it "prepare contact with image in profile" testShortLinkInvitationImage
|
it "prepare contact with image in profile" testShortLinkInvitationImage
|
||||||
it "prepare contact with a long name in profile" testShortLinkInvitationLongName
|
it "prepare contact with a long name in profile" testShortLinkInvitationLongName
|
||||||
it "prepare contact using address short link data and connect" testShortLinkAddressPrepareContact
|
it "prepare contact using address short link data and connect" testShortLinkAddressPrepareContact
|
||||||
|
it "prepare contact via invitation and connect after it is deleted" testShortLinkDeletedInvitation
|
||||||
|
it "prepare contact via address and connect after it is deleted" testShortLinkDeletedAddress
|
||||||
it "prepare business chat using address short link data and connect" testShortLinkAddressPrepareBusiness
|
it "prepare business chat using address short link data and connect" testShortLinkAddressPrepareBusiness
|
||||||
it "prepare group using group short link data and connect" testShortLinkPrepareGroup
|
it "prepare group using group short link data and connect" testShortLinkPrepareGroup
|
||||||
it "prepare group using group short link data and connect, host rejects" testShortLinkPrepareGroupReject
|
it "prepare group using group short link data and connect, host rejects" testShortLinkPrepareGroupReject
|
||||||
|
@ -3008,6 +3010,44 @@ testShortLinkAddressPrepareContact =
|
||||||
(alice <## "bob (Bob): contact is connected")
|
(alice <## "bob (Bob): contact is connected")
|
||||||
alice <##> bob
|
alice <##> bob
|
||||||
|
|
||||||
|
testShortLinkDeletedInvitation :: HasCallStack => TestParams -> IO ()
|
||||||
|
testShortLinkDeletedInvitation =
|
||||||
|
testChat2 aliceProfile bobProfile $
|
||||||
|
\alice bob -> do
|
||||||
|
alice ##> "/_connect 1"
|
||||||
|
(shortLink, fullLink) <- getInvitations alice
|
||||||
|
bob ##> ("/_connect plan 1 " <> shortLink)
|
||||||
|
bob <## "invitation link: ok to connect"
|
||||||
|
contactSLinkData <- getTermLine bob
|
||||||
|
bob ##> ("/_prepare contact 1 " <> fullLink <> " " <> shortLink <> " " <> contactSLinkData)
|
||||||
|
bob <## "alice: contact is prepared"
|
||||||
|
alice @@@ [(":1","")]
|
||||||
|
alice ##> "/_delete :1"
|
||||||
|
alice <## "connection :1 deleted"
|
||||||
|
bob ##> "/_connect contact @2"
|
||||||
|
bob <##. "error: connection authorization failed"
|
||||||
|
bob ##> "/_connect contact @2"
|
||||||
|
bob <##. "error: connection authorization failed"
|
||||||
|
|
||||||
|
testShortLinkDeletedAddress :: HasCallStack => TestParams -> IO ()
|
||||||
|
testShortLinkDeletedAddress =
|
||||||
|
testChat2 aliceProfile bobProfile $
|
||||||
|
\alice bob -> do
|
||||||
|
alice ##> "/ad"
|
||||||
|
(shortLink, fullLink) <- getContactLinks alice True
|
||||||
|
bob ##> ("/_connect plan 1 " <> shortLink)
|
||||||
|
bob <## "contact address: ok to connect"
|
||||||
|
contactSLinkData <- getTermLine bob
|
||||||
|
bob ##> ("/_prepare contact 1 " <> fullLink <> " " <> shortLink <> " " <> contactSLinkData)
|
||||||
|
bob <## "alice: contact is prepared"
|
||||||
|
alice ##> "/da"
|
||||||
|
alice <## "Your chat address is deleted - accepted contacts will remain connected."
|
||||||
|
alice <## "To create a new chat address use /ad"
|
||||||
|
bob ##> "/_connect contact @2"
|
||||||
|
bob <##. "error: connection authorization failed"
|
||||||
|
bob ##> "/_connect contact @2"
|
||||||
|
bob <##. "error: connection authorization failed"
|
||||||
|
|
||||||
testShortLinkAddressPrepareBusiness :: HasCallStack => TestParams -> IO ()
|
testShortLinkAddressPrepareBusiness :: HasCallStack => TestParams -> IO ()
|
||||||
testShortLinkAddressPrepareBusiness =
|
testShortLinkAddressPrepareBusiness =
|
||||||
testChat3 businessProfile aliceProfile {fullName = "Alice @ Biz"} bobProfile $
|
testChat3 businessProfile aliceProfile {fullName = "Alice @ Biz"} bobProfile $
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue