mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-12 02:14:57 +00:00
core, ui: better error on failed channel creation (#6825)
This commit is contained in:
@@ -651,6 +651,12 @@ data RelayConnectionResult = RelayConnectionResult
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data AddRelayResult = AddRelayResult
|
||||
{ relay :: UserChatRelay,
|
||||
relayError :: Maybe ChatError
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data RelayTestStep
|
||||
= RTSGetLink
|
||||
| RTSDecodeLink
|
||||
@@ -721,6 +727,7 @@ data ChatResponse
|
||||
| CRWelcome {user :: User}
|
||||
| CRGroupCreated {user :: User, groupInfo :: GroupInfo}
|
||||
| CRPublicGroupCreated {user :: User, groupInfo :: GroupInfo, groupLink :: GroupLink, groupRelays :: [GroupRelay]}
|
||||
| CRPublicGroupCreationFailed {user :: User, addRelayResults :: [AddRelayResult]}
|
||||
| CRGroupRelays {user :: User, groupInfo :: GroupInfo, groupRelays :: [GroupRelay]}
|
||||
| CRGroupMembers {user :: User, group :: Group}
|
||||
| CRMemberSupportChats {user :: User, groupInfo :: GroupInfo, members :: [GroupMember]}
|
||||
@@ -1713,6 +1720,8 @@ $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "TE") ''TerminalEvent)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''RelayConnectionResult)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''AddRelayResult)
|
||||
|
||||
$(JQ.deriveJSON (enumJSON $ dropPrefix "RTS") ''RelayTestStep)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''RelayTestFailure)
|
||||
|
||||
@@ -2479,13 +2479,29 @@ processChatCommand vr nm = \case
|
||||
APINewPublicGroup userId incognito relayIds groupProfile -> withUserId userId $ \user -> do
|
||||
(gProfile', memberId, groupKeys, setupLink) <- prepareGroupLink user
|
||||
gInfo <- newGroup user incognito gProfile' True memberId (Just groupKeys) (Just 1)
|
||||
(gLink, groupRelays) <- setupLink gInfo `catchAllErrors` \e -> do
|
||||
(gLink, results) <- setupLink gInfo `catchAllErrors` \e -> do
|
||||
deleteInProgressGroup user gInfo
|
||||
throwError e
|
||||
createNewGroupItems user gInfo
|
||||
pure $ CRPublicGroupCreated user gInfo gLink groupRelays
|
||||
case partitionEithers (map snd results) of
|
||||
([], groupRelays) -> do
|
||||
createNewGroupItems user gInfo
|
||||
pure $ CRPublicGroupCreated user gInfo gLink groupRelays
|
||||
(errors@(e : _), _) -> do
|
||||
deleteInProgressGroup user gInfo
|
||||
-- If all errors are temporary (network, timeout, host), throw to allow retry
|
||||
if all isTempErr errors
|
||||
then throwError e
|
||||
else do
|
||||
let relayResults = map toRelayResult results
|
||||
toRelayResult (r, Left e) = AddRelayResult r (Just e)
|
||||
toRelayResult (r, Right _) = AddRelayResult r Nothing
|
||||
pure $ CRPublicGroupCreationFailed user relayResults
|
||||
where
|
||||
prepareGroupLink :: User -> CM (GroupProfile, MemberId, GroupKeys, GroupInfo -> CM (GroupLink, [GroupRelay]))
|
||||
isTempErr :: ChatError -> Bool
|
||||
isTempErr = \case
|
||||
ChatErrorAgent {agentError = e} -> temporaryOrHostError e
|
||||
_ -> False
|
||||
prepareGroupLink :: User -> CM (GroupProfile, MemberId, GroupKeys, GroupInfo -> CM (GroupLink, [(UserChatRelay, Either ChatError GroupRelay)]))
|
||||
prepareGroupLink user = do
|
||||
gVar <- asks random
|
||||
groupLinkId <- GroupLinkId <$> drgRandomBytes 16
|
||||
@@ -2514,8 +2530,8 @@ processChatCommand vr nm = \case
|
||||
subRole <- asks $ channelSubscriberRole . config
|
||||
gLink <- withFastStore $ \db -> createGroupLink db gVar user gInfo connId ccLink' groupLinkId subRole subMode
|
||||
relays <- withFastStore $ \db -> mapM (getChatRelayById db user) (L.toList relayIds)
|
||||
groupRelays <- addRelays user gInfo sLnk relays
|
||||
pure (gLink, groupRelays)
|
||||
results <- addRelays user gInfo sLnk relays
|
||||
pure (gLink, results)
|
||||
pure (groupProfile', memberId, groupKeys, setupLink)
|
||||
NewPublicGroup incognito relayIds gProfile -> withUser $ \User {userId} ->
|
||||
processChatCommand vr nm $ APINewPublicGroup userId incognito relayIds gProfile
|
||||
@@ -3862,44 +3878,43 @@ processChatCommand vr nm = \case
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct) ci]
|
||||
forM_ (timed_ >>= timedDeleteAt') $
|
||||
startProximateTimedItemThread user (ChatRef CTDirect contactId Nothing, chatItemId' ci)
|
||||
addRelays :: User -> GroupInfo -> ShortLinkContact -> [UserChatRelay] -> CM [GroupRelay]
|
||||
addRelays :: User -> GroupInfo -> ShortLinkContact -> [UserChatRelay] -> CM [(UserChatRelay, Either ChatError GroupRelay)]
|
||||
addRelays user gInfo@GroupInfo {membership} groupSLink relays =
|
||||
mapConcurrently addRelay relays
|
||||
where
|
||||
addRelay :: UserChatRelay -> CM GroupRelay
|
||||
addRelay :: UserChatRelay -> CM (UserChatRelay, Either ChatError GroupRelay)
|
||||
addRelay relay@UserChatRelay {address} = do
|
||||
-- TODO [relays] owner: track and reuse relay profiles
|
||||
-- TODO - single profile linked to relay configuration record (chat_relays)
|
||||
-- TODO - update when fetching link data from relay address
|
||||
(FixedLinkData {linkConnReq = cReq}, _cData) <- getShortLinkConnReq nm user address
|
||||
lift (withAgent' $ \a -> connRequestPQSupport a PQSupportOff cReq) >>= \case
|
||||
Nothing -> throwChatError CEInvalidConnReq
|
||||
Just (agentV, _) -> do
|
||||
let chatV = agentToChatVersion agentV
|
||||
gVar <- asks random
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
|
||||
(relayMember, conn, groupRelay) <- withFastStore $ \db -> do
|
||||
relayMember <- createRelayForOwner db vr gVar user gInfo relay
|
||||
groupRelay <- createGroupRelayRecord db gInfo relayMember relay
|
||||
conn <- createRelayConnection db vr user (groupMemberId' relayMember) connId ConnPrepared chatV subMode
|
||||
pure (relayMember, conn, groupRelay)
|
||||
let GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
allowSimplexLinks = groupFeatureUserAllowed SGFSimplexLinks gInfo
|
||||
membershipProfile = redactedMemberProfile allowSimplexLinks $ fromLocalProfile $ memberProfile membership
|
||||
GroupMember {memberId = relayMemberId} = relayMember
|
||||
relayInv = GroupRelayInvitation {
|
||||
fromMember = MemberIdRole userMemberId userRole,
|
||||
fromMemberProfile = membershipProfile,
|
||||
relayMemberId,
|
||||
groupLink = groupSLink
|
||||
}
|
||||
dm <- encodeConnInfo $ XGrpRelayInv relayInv
|
||||
(sqSecured, _serviceId) <- withAgent $ \a -> joinConnection a nm (aUserId user) (aConnId conn) True cReq dm PQSupportOff subMode
|
||||
let newConnStatus = if sqSecured then ConnSndReady else ConnJoined
|
||||
withFastStore' $ \db -> do
|
||||
void $ updateConnectionStatusFromTo db conn ConnPrepared newConnStatus
|
||||
updateRelayStatusFromTo db groupRelay RSNew RSInvited
|
||||
r <- tryAllErrors $ do
|
||||
(FixedLinkData {linkConnReq = cReq}, _cData) <- getShortLinkConnReq nm user address
|
||||
lift (withAgent' $ \a -> connRequestPQSupport a PQSupportOff cReq) >>= \case
|
||||
Nothing -> throwChatError CEInvalidConnReq
|
||||
Just (agentV, _) -> do
|
||||
let chatV = agentToChatVersion agentV
|
||||
gVar <- asks random
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
|
||||
(relayMember, conn, groupRelay) <- withFastStore $ \db -> do
|
||||
relayMember <- createRelayForOwner db vr gVar user gInfo relay
|
||||
groupRelay <- createGroupRelayRecord db gInfo relayMember relay
|
||||
conn <- createRelayConnection db vr user (groupMemberId' relayMember) connId ConnPrepared chatV subMode
|
||||
pure (relayMember, conn, groupRelay)
|
||||
let GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
allowSimplexLinks = groupFeatureUserAllowed SGFSimplexLinks gInfo
|
||||
membershipProfile = redactedMemberProfile allowSimplexLinks $ fromLocalProfile $ memberProfile membership
|
||||
GroupMember {memberId = relayMemberId} = relayMember
|
||||
relayInv = GroupRelayInvitation {
|
||||
fromMember = MemberIdRole userMemberId userRole,
|
||||
fromMemberProfile = membershipProfile,
|
||||
relayMemberId,
|
||||
groupLink = groupSLink
|
||||
}
|
||||
dm <- encodeConnInfo $ XGrpRelayInv relayInv
|
||||
(sqSecured, _serviceId) <- withAgent $ \a -> joinConnection a nm (aUserId user) (aConnId conn) True cReq dm PQSupportOff subMode
|
||||
let newConnStatus = if sqSecured then ConnSndReady else ConnJoined
|
||||
withFastStore' $ \db -> do
|
||||
void $ updateConnectionStatusFromTo db conn ConnPrepared newConnStatus
|
||||
updateRelayStatusFromTo db groupRelay RSNew RSInvited
|
||||
pure (relay, r)
|
||||
privateGetUser :: UserId -> CM User
|
||||
privateGetUser userId =
|
||||
tryAllErrors (withStore (`getUser` userId)) >>= \case
|
||||
|
||||
@@ -7131,6 +7131,10 @@ Query: UPDATE groups SET relay_own_status = ?, updated_at = ? WHERE group_id = ?
|
||||
Plan:
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query: UPDATE groups SET relay_request_err_reason = ?, updated_at = ? WHERE group_id = ?
|
||||
Plan:
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query: UPDATE groups SET request_shared_msg_id = ? WHERE group_id = ?
|
||||
Plan:
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
@@ -180,6 +180,7 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
CRContactRequestRejected u UserContactRequest {localDisplayName = c} _ct_ -> ttyUser u [ttyContact c <> ": contact request rejected"]
|
||||
CRGroupCreated u g -> ttyUser u $ viewGroupCreated g testView
|
||||
CRPublicGroupCreated u g _groupLink _relays -> ttyUser u $ viewGroupCreated g testView
|
||||
CRPublicGroupCreationFailed u results -> ttyUser u $ viewPublicGroupCreationFailed results
|
||||
CRGroupRelays u g relays -> ttyUser u $ viewGroupRelays g relays
|
||||
CRGroupMembers u g -> ttyUser u $ viewGroupMembers g
|
||||
CRMemberSupportChats u g ms -> ttyUser u $ viewMemberSupportChats g ms
|
||||
@@ -1238,6 +1239,14 @@ viewGroupCreated g testView =
|
||||
where
|
||||
relaysInstruction = "wait for selected relay(s) to join, then you can invite members via group link"
|
||||
|
||||
viewPublicGroupCreationFailed :: [AddRelayResult] -> [StyledString]
|
||||
viewPublicGroupCreationFailed results =
|
||||
["channel not created, results:"]
|
||||
<> map showRelayResult results
|
||||
where
|
||||
showRelayResult (AddRelayResult UserChatRelay {chatRelayId = DBEntityId i} err_) =
|
||||
" relay " <> sShow i <> ": " <> maybe "ok" (plain . tshow) err_
|
||||
|
||||
viewCannotResendInvitation :: GroupInfo -> ContactName -> [StyledString]
|
||||
viewCannotResendInvitation g c =
|
||||
[ ttyContact c <> " is already invited to group " <> ttyGroup' g,
|
||||
|
||||
Reference in New Issue
Block a user