mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-22 21:52:28 +00:00
align and simplify core/UI/UXcanvas
This commit is contained in:
1 parent
10c456b057
commit
6bc2f4450e
26 files changed
+634
-460
No files matched your search
@@ -885,7 +885,7 @@ data ChatResponse
|
||||
| CRInvitation {user :: User, connLinkInvitation :: CreatedLinkInvitation, connection :: PendingContactConnection}
|
||||
| CRConnectionIncognitoUpdated {user :: User, toConnection :: PendingContactConnection, customUserProfile :: Maybe Profile}
|
||||
| CRConnectionUserChanged {user :: User, fromConnection :: PendingContactConnection, toConnection :: PendingContactConnection, newUser :: User}
|
||||
| CRConnectionPlan {user :: User, connLink :: Maybe ACreatedConnLink, planSimplexName :: Maybe SimplexNameInfo, otherSimplexName :: Maybe SimplexNameInfo, connectionPlan :: ConnectionPlan}
|
||||
| CRConnectionPlan {user :: User, connLink :: Maybe ACreatedConnLink, planSimplexName :: Maybe SimplexNameInfo, otherSimplexName :: Maybe SimplexNameInfo, connectionPlan :: ConnectionPlan, localChats :: [AChatInfo], offerLookup :: Bool}
|
||||
| CRNewPreparedChat {user :: User, chat :: AChat}
|
||||
| CRContactUserChanged {user :: User, fromContact :: Contact, newUser :: User, toContact :: Contact}
|
||||
| CRGroupUserChanged {user :: User, fromGroup :: GroupInfo, newUser :: User, toGroup :: GroupInfo}
|
||||
@@ -1180,7 +1180,7 @@ data InvitationLinkPlan
|
||||
deriving (Show)
|
||||
|
||||
data ContactAddressPlan
|
||||
= CAPOk {contactSLinkData_ :: Maybe ContactShortLinkData, ownerVerification :: Maybe OwnerVerification, addressChanged :: Bool, existingChat_ :: Maybe AChatInfo}
|
||||
= CAPOk {contactSLinkData_ :: Maybe ContactShortLinkData, ownerVerification :: Maybe OwnerVerification, addressChanged :: Bool}
|
||||
| CAPOwnLink
|
||||
| CAPConnectingConfirmReconnect
|
||||
| CAPConnectingProhibit {contact :: Contact}
|
||||
@@ -1189,7 +1189,7 @@ data ContactAddressPlan
|
||||
deriving (Show)
|
||||
|
||||
data GroupLinkPlan
|
||||
= GLPOk {groupSLinkInfo_ :: Maybe GroupShortLinkInfo, groupSLinkData_ :: Maybe GroupShortLinkData, ownerVerification :: Maybe OwnerVerification, addressChanged :: Bool, existingChat_ :: Maybe AChatInfo}
|
||||
= GLPOk {groupSLinkInfo_ :: Maybe GroupShortLinkInfo, groupSLinkData_ :: Maybe GroupShortLinkData, ownerVerification :: Maybe OwnerVerification, addressChanged :: Bool}
|
||||
| GLPOwnLink {groupInfo :: GroupInfo}
|
||||
| GLPConnectingConfirmReconnect
|
||||
| GLPConnectingProhibit {groupInfo_ :: Maybe GroupInfo}
|
||||
|
||||
@@ -40,7 +40,7 @@ import Data.Foldable (foldr')
|
||||
import Data.Functor (($>))
|
||||
import Data.Functor.Identity (Identity (..), runIdentity)
|
||||
import Data.Int (Int64)
|
||||
import Data.List (dropWhileEnd, find, foldl', isSuffixOf, partition, sortOn, zipWith4)
|
||||
import Data.List (dropWhileEnd, find, foldl', isSuffixOf, nubBy, partition, sortOn, zipWith4)
|
||||
import Data.List.NonEmpty (NonEmpty (..))
|
||||
import qualified Data.List.NonEmpty as L
|
||||
import Data.Map.Strict (Map)
|
||||
@@ -2177,7 +2177,8 @@ processChatCommand cxt nm = \case
|
||||
pure conn'
|
||||
APIConnectPlan userId (Just ct) resolveMode linkOwnerSig_ -> withUserId userId $ \user -> do
|
||||
(ccLink, planSimplexName, otherSimplexName, plan) <- connectPlan user ct resolveMode linkOwnerSig_ Nothing
|
||||
pure $ CRConnectionPlan user ccLink planSimplexName otherSimplexName plan
|
||||
(localChats, offerLookup) <- planLocalChats user ct plan
|
||||
pure $ CRConnectionPlan user ccLink planSimplexName otherSimplexName plan localChats offerLookup
|
||||
APIConnectPlan _ Nothing _ _ -> throwChatError CEInvalidConnReq
|
||||
APIPrepareContact userId accLink verifiedDomain contactSLinkData -> withUserId userId $ \user -> do
|
||||
let ContactShortLinkData {profile, message, business} = contactSLinkData
|
||||
@@ -2192,6 +2193,7 @@ processChatCommand cxt nm = \case
|
||||
groupProfile = businessGroupProfile profile groupPreferences
|
||||
gVar <- asks random
|
||||
(gInfo, hostMember_) <- withStore $ \db -> createPreparedGroup db gVar cxt user groupProfile True ccLink welcomeSharedMsgId False GRMember Nothing domain_
|
||||
forM_ domain_ $ nameChatsUpdated user NTContact
|
||||
hostMember <- maybe (throwCmdError "no host member") pure hostMember_
|
||||
void $ createChatItem user (CDGroupSnd gInfo Nothing) False CIChatBanner Nothing Nothing (Just epochStart)
|
||||
let cd = CDGroupRcv gInfo Nothing hostMember
|
||||
@@ -2205,6 +2207,7 @@ processChatCommand cxt nm = \case
|
||||
pure $ CRNewPreparedChat user $ AChat SCTGroup chat
|
||||
ACCL _ (CCLink cReq _) -> do
|
||||
ct <- withStore $ \db -> createPreparedContact db cxt user profile accLink welcomeSharedMsgId (True <$ domain_)
|
||||
forM_ domain_ $ nameChatsUpdated user NTContact
|
||||
void $ createChatItem user (CDDirectSnd ct) False CIChatBanner Nothing Nothing (Just epochStart)
|
||||
let cd = CDDirectRcv ct
|
||||
createItem sharedMsgId content = createChatItem user cd False content sharedMsgId Nothing Nothing
|
||||
@@ -2221,6 +2224,7 @@ processChatCommand cxt nm = \case
|
||||
domain_ = if verifiedDomain == (claimDomain <$> (publicGroup >>= publicGroupAccess >>= groupDomainClaim)) then verifiedDomain else Nothing
|
||||
welcomeSharedMsgId <- forM description $ \_ -> getSharedMsgId
|
||||
(gInfo, hostMember_) <- preparedGroupFromLink user ccLink direct groupSLinkData welcomeSharedMsgId domain_
|
||||
forM_ domain_ $ nameChatsUpdated user NTPublicGroup
|
||||
void $ createChatItem user (CDGroupSnd gInfo Nothing) False CIChatBanner Nothing Nothing (Just epochStart)
|
||||
let cd = maybe (CDChannelRcv gInfo Nothing) (CDGroupRcv gInfo Nothing) hostMember_
|
||||
cInfo = GroupChat gInfo Nothing
|
||||
@@ -2407,7 +2411,7 @@ processChatCommand cxt nm = \case
|
||||
ACTarget m (CTFullContact cReq) -> con m cReq
|
||||
ACTarget m (CTInv (CLFull cReq)) -> con m cReq
|
||||
_ -> throwError e
|
||||
connectWithPlan user incognito ccLink planSimplexName otherSimplexName plan
|
||||
connectWithPlan user incognito ccLink planSimplexName otherSimplexName plan =<< planLocalChats user ct plan
|
||||
Connect _ Nothing -> throwChatError CEInvalidConnReq
|
||||
APIVerifyContactDomain contactId -> withUser $ \user -> do
|
||||
ct@Contact {profile = LocalProfile {contactDomain}, preparedContact} <- withFastStore $ \db -> getContact db cxt user contactId
|
||||
@@ -2415,6 +2419,7 @@ processChatCommand cxt nm = \case
|
||||
domain <- maybe (throwCmdError "contact has no name to verify") pure contactDomain
|
||||
(verified, reason) <- verifyEntityDomain user nm NTContact domain connLink_
|
||||
ct' <- maybe (pure ct) (\v -> withFastStore' $ \db -> setContactDomainVerified db user ct v Nothing) verified
|
||||
when (verified == Just True) $ nameChatsUpdated user NTContact $ claimDomain domain
|
||||
pure $ CRContactDomainVerified user ct' reason
|
||||
APIVerifyGroupDomain groupId -> withUser $ \user -> do
|
||||
g@GroupInfo {groupProfile = GroupProfile {publicGroup}} <- withFastStore $ \db -> getGroupInfo db cxt user groupId
|
||||
@@ -2429,6 +2434,7 @@ processChatCommand cxt nm = \case
|
||||
Left (ChatErrorAgent {agentError = SMP _ (NAME SMP.NOT_FOUND)}) -> pure (False, Just "the name is not registered")
|
||||
Left e -> throwError e
|
||||
g' <- withFastStore' $ \db -> setGroupDomainVerified db user g verified Nothing
|
||||
when verified $ nameChatsUpdated user NTPublicGroup $ claimDomain claim
|
||||
pure $ CRGroupDomainVerified user g' reason
|
||||
APIConnectContactViaAddress userId incognito contactId -> withUserId userId $ \user -> do
|
||||
ct@Contact {profile = LocalProfile {contactLink}, groupDirectInv} <- withFastStore $ \db -> getContact db cxt user contactId
|
||||
@@ -2446,8 +2452,8 @@ processChatCommand cxt nm = \case
|
||||
toView $ CEvtChatInfoUpdated user (AChatInfo SCTDirect $ DirectChat ct')
|
||||
throwError e
|
||||
ConnectSimplex incognito -> withUser $ \user -> do
|
||||
plan <- contactRequestPlan user adminContactReq Nothing Nothing `catchAllErrors` const (pure $ CPContactAddress (CAPOk Nothing Nothing False Nothing) Nothing)
|
||||
connectWithPlan user incognito (Just (ACCL SCMContact (CCLink adminContactReq Nothing))) Nothing Nothing plan
|
||||
plan <- contactRequestPlan user adminContactReq Nothing Nothing `catchAllErrors` const (pure $ CPContactAddress (CAPOk Nothing Nothing False) Nothing)
|
||||
connectWithPlan user incognito (Just (ACCL SCMContact (CCLink adminContactReq Nothing))) Nothing Nothing plan ([], False)
|
||||
DeleteContact cName cdm -> withContactName cName $ \ctId -> APIDeleteChat (ChatRef CTDirect ctId Nothing) cdm
|
||||
ClearContact cName -> withContactName cName $ \chatId -> APIClearChat $ ChatRef CTDirect chatId Nothing
|
||||
APIListContacts userId -> withUserId userId $ \user ->
|
||||
@@ -4113,6 +4119,7 @@ processChatCommand cxt nm = \case
|
||||
gInfo' <- withStore $ \db -> do
|
||||
g <- updateGroupProfile db user gInfo p'
|
||||
if domainVerified then liftIO $ setGroupDomainVerified db user g True Nothing else pure g
|
||||
when domainVerified $ forM_ (publicGroup p' >>= publicGroupAccess >>= groupDomainClaim) $ nameChatsUpdated user NTPublicGroup . claimDomain
|
||||
msg <- case businessChat of
|
||||
Just BusinessChatInfo {businessId} -> do
|
||||
ms <- withStore' $ \db -> getGroupMembers db cxt user gInfo'
|
||||
@@ -4418,11 +4425,12 @@ processChatCommand cxt nm = \case
|
||||
pure (Just (ACCL SCMInvitation (CCLink cReq sLnk_)), Nothing, Nothing, plan)
|
||||
connectPlan user (ACTarget SCMContact ct) resolveMode sig_ nameReg_ = case ct of
|
||||
CTDomain d -> do
|
||||
channel_ <- knownGroupPlans $ CTName channelName
|
||||
contact_ <- knownContactPlans $ CTName contactName
|
||||
channel_ <- knownGroupPlans user $ CTName channelName
|
||||
contact_ <- knownContactPlans user $ CTName contactName
|
||||
let match_ = (channelName,) <$> channel_ <|> (contactName,) <$> contact_
|
||||
bothLocal = isJust channel_ && isJust contact_
|
||||
case match_ of
|
||||
Just (ni, ((l, p), fresh)) | resolveMode == PRMNever || (resolveMode == PRMUnknown && fresh) -> pure (Just l, Just ni, Nothing, p)
|
||||
Just (ni, ((l, p), fresh)) | resolveMode == PRMNever || (resolveMode == PRMUnknown && fresh && bothLocal) -> pure (Just l, Just ni, Nothing, p)
|
||||
Nothing | resolveMode == PRMNever -> throwChatError CENotResolvedLocally
|
||||
_ ->
|
||||
tryAllErrors (resolveNameRegistration user nm d) >>= \case
|
||||
@@ -4438,12 +4446,19 @@ processChatCommand cxt nm = \case
|
||||
NTPublicGroup -> newName contactName contact_
|
||||
NTContact -> newName channelName channel_
|
||||
newName ni known_ = case linkOrWarning ni of
|
||||
Right l' | (knownLinkOf . fst . fst =<< known_) /= Just l' -> Just ni
|
||||
Right l' | not (any (isLinkOf l' . fst . fst) known_) -> Just ni
|
||||
_ -> Nothing
|
||||
contactPlanOr fallback = namePlanWith Nothing contactName `catchAllErrors` \_ -> fallback
|
||||
unjoinable (_, _, _, p) = case p of
|
||||
CPGroupLink GLPNoRelays {} _ -> True
|
||||
CPGroupLink GLPUpdateRequired {} _ -> True
|
||||
_ -> False
|
||||
case match_ of
|
||||
Just (ni, _) -> namePlan ni
|
||||
Nothing -> case (linkOrWarning channelName, linkOrWarning contactName) of
|
||||
(Right _, Right _) -> namePlan channelName `catchAllErrors` \e -> (namePlanWith Nothing contactName `catchAllErrors` \_ -> throwError e)
|
||||
(Right _, Right _) ->
|
||||
(namePlan channelName >>= \r -> if unjoinable r then contactPlanOr (pure r) else pure r)
|
||||
`catchAllErrors` \e -> contactPlanOr (throwError e)
|
||||
(Right _, Left _) -> namePlan channelName
|
||||
(Left _, Right _) -> namePlan contactName
|
||||
(Left w, Left _) -> pure (Nothing, Nothing, Nothing, CPNameNotConnectable d w)
|
||||
@@ -4456,7 +4471,7 @@ processChatCommand cxt nm = \case
|
||||
pure (Just (ACCL SCMContact $ CCLink cReq Nothing), Nothing, Nothing, plan)
|
||||
CTShortContact nl ->
|
||||
(\(l, p) -> (l, simplexName_, Nothing, p)) <$> case ctType of
|
||||
CCTContact -> knownContactPlans nl' >>= shortLinkPlan confirmKnown resolvedPlan
|
||||
CCTContact -> knownContactPlans user nl' >>= shortLinkPlan confirmKnown resolvedPlan
|
||||
where
|
||||
confirmKnown expiresAt _ r@(_, p) = r <$ when (isJust simplexName_) (setKnownVerified expiresAt p)
|
||||
resolvedPlan expiresAt l' = do
|
||||
@@ -4498,10 +4513,12 @@ processChatCommand cxt nm = \case
|
||||
CTName SimplexNameInfo {nameType = NTContact} -> CCTContact
|
||||
CTName SimplexNameInfo {nameType = NTPublicGroup} -> CCTChannel
|
||||
con l' cReq = ACCL SCMContact $ CCLink cReq (Just l')
|
||||
setKnownVerified expiresAt = \case
|
||||
CPContactAddress (CAPKnown ct') _ -> void $ withFastStore' $ \db -> setContactDomainVerified db user ct' True expiresAt
|
||||
CPGroupLink (GLPKnown g _ _ _) _ -> void $ withFastStore' $ \db -> setGroupDomainVerified db user g True expiresAt
|
||||
_ -> pure ()
|
||||
setKnownVerified expiresAt p = do
|
||||
verified <- case p of
|
||||
CPContactAddress (CAPKnown ct') _ -> True <$ withFastStore' (\db -> setContactDomainVerified db user ct' True expiresAt)
|
||||
CPGroupLink (GLPKnown g _ _ _) _ -> True <$ withFastStore' (\db -> setGroupDomainVerified db user g True expiresAt)
|
||||
_ -> pure False
|
||||
when verified $ forM_ simplexName_ $ \SimplexNameInfo {nameType, nameDomain} -> nameChatsUpdated user nameType nameDomain
|
||||
shortLinkPlan ::
|
||||
(Maybe UTCTime -> ShortLinkContact -> (ACreatedConnLink, ConnectionPlan) -> CM (ACreatedConnLink, ConnectionPlan)) ->
|
||||
(Maybe UTCTime -> ShortLinkContact -> CM (ACreatedConnLink, ConnectionPlan)) ->
|
||||
@@ -4537,15 +4554,15 @@ processChatCommand cxt nm = \case
|
||||
Just r@(_, p) | knownChat p -> pure r
|
||||
_ -> throwError e
|
||||
linkPlan expiresAt l' = case known_ of
|
||||
Just r@(l, p)
|
||||
| knownLinkOf l == Just l' -> confirmKnown expiresAt l' r
|
||||
Just r@(l, _)
|
||||
| isLinkOf l' l -> confirmKnown expiresAt l' r
|
||||
| otherwise ->
|
||||
(second (setAddressChanged p) <$> resolvedPlan expiresAt l') `catchAllErrors` \case
|
||||
((\(l'', p) -> maybe r (l'',) $ setAddressChanged p) <$> resolvedPlan expiresAt l') `catchAllErrors` \case
|
||||
ChatError (CESimplexDomainNotReady _ SDEUnknownDomain) -> pure r
|
||||
e -> throwError e
|
||||
Nothing -> resolvedPlan expiresAt l'
|
||||
groupShortLinkPlan :: CM (Maybe ACreatedConnLink, ConnectionPlan)
|
||||
groupShortLinkPlan = knownGroupPlans nl' >>= shortLinkPlan confirmKnown resolvedGroupPlan
|
||||
groupShortLinkPlan = knownGroupPlans user nl' >>= shortLinkPlan confirmKnown resolvedGroupPlan
|
||||
where
|
||||
confirmKnown expiresAt l' = \case
|
||||
(_, CPGroupLink (GLPKnown g _ _ _) _) -> resolveKnownGroup expiresAt l' g
|
||||
@@ -4578,7 +4595,7 @@ processChatCommand cxt nm = \case
|
||||
_ -> pure plan0
|
||||
forM_ planDomain $ \nameDomain ->
|
||||
let domain_ = (\GroupProfile {publicGroup} -> claimDomain <$> (publicGroup >>= publicGroupAccess >>= groupDomainClaim)) =<< case plan of
|
||||
CPGroupLink (GLPOk _ (Just GroupShortLinkData {groupProfile}) _ _ _) _ -> Just groupProfile
|
||||
CPGroupLink (GLPOk _ (Just GroupShortLinkData {groupProfile}) _ _) _ -> Just groupProfile
|
||||
CPGroupLink (GLPKnown GroupInfo {groupProfile} _ _ _) _ -> Just groupProfile
|
||||
CPGroupLink (GLPOwnLink GroupInfo {groupProfile}) _ -> Just groupProfile
|
||||
CPGroupLink (GLPConnectingProhibit (Just GroupInfo {groupProfile})) _ -> Just groupProfile
|
||||
@@ -4598,54 +4615,79 @@ processChatCommand cxt nm = \case
|
||||
_ -> pure (g, False)
|
||||
pure (con l' cReq, CPGroupLink (GLPKnown g' updated ov (ListDef glOwners)) Nothing)
|
||||
where
|
||||
knownContactPlans :: ContactNameOrLink -> CM (Maybe ((ACreatedConnLink, ConnectionPlan), Bool))
|
||||
knownContactPlans nl' = withFastStore $ \db ->
|
||||
liftIO (getUserContactLinkViaTarget db user nl') >>= \case
|
||||
Just UserContactLink {connLinkContact} -> pure $ Just ((ACCL SCMContact connLinkContact, CPContactAddress CAPOwnLink Nothing), False)
|
||||
Nothing ->
|
||||
getContactToConnect db cxt user nl' >>= \case
|
||||
Just (ccl, ct')
|
||||
| contactDeleted ct' -> pure Nothing
|
||||
| otherwise -> do
|
||||
fresh <- liftIO $ resolvedRecently =<< getContactDomainResolution db user ct'
|
||||
pure $ Just ((ACCL SCMContact ccl, CPContactAddress (CAPKnown ct') Nothing), fresh)
|
||||
Nothing -> gPlan db =<< getGroupToConnect db cxt user nl'
|
||||
knownGroupPlans :: ContactNameOrLink -> CM (Maybe ((ACreatedConnLink, ConnectionPlan), Bool))
|
||||
knownGroupPlans nl' = withFastStore $ \db ->
|
||||
liftIO (getGroupInfoViaUserTarget db cxt user nl') >>= \case
|
||||
Just (ccl, g) -> pure $ Just ((ACCL SCMContact ccl, CPGroupLink (GLPOwnLink g) Nothing), False)
|
||||
Nothing -> gPlan db =<< getGroupToConnect db cxt user nl'
|
||||
gPlan db = \case
|
||||
Just (ccl, g) | not (memberRemoved (membership g)) -> do
|
||||
fresh <- liftIO $ resolvedRecently =<< getGroupDomainResolution db user g
|
||||
pure $ Just ((ACCL SCMContact ccl, CPGroupLink (GLPKnown g False Nothing (ListDef [])) Nothing), fresh)
|
||||
_ -> pure Nothing
|
||||
resolvedRecently :: Maybe (UTCTime, Maybe UTCTime) -> IO Bool
|
||||
resolvedRecently = \case
|
||||
Just (resolvedAt, expiresAt_) -> do
|
||||
now <- getCurrentTime
|
||||
pure $ diffUTCTime now resolvedAt < nominalDay && maybe True (now <) expiresAt_
|
||||
Nothing -> pure False
|
||||
knownChat = \case
|
||||
CPContactAddress (CAPKnown _) _ -> True
|
||||
CPGroupLink GLPKnown {} _ -> True
|
||||
_ -> False
|
||||
knownLinkOf :: ACreatedConnLink -> Maybe ShortLinkContact
|
||||
knownLinkOf = \case
|
||||
ACCL SCMContact (CCLink _ sl_) -> sl_
|
||||
isLinkOf :: ShortLinkContact -> ACreatedConnLink -> Bool
|
||||
isLinkOf l' = \case
|
||||
ACCL SCMContact (CCLink _ (Just sl)) -> sameShortLinkContact sl l'
|
||||
_ -> False
|
||||
knownContactPlans :: User -> ContactNameOrLink -> CM (Maybe ((ACreatedConnLink, ConnectionPlan), Bool))
|
||||
knownContactPlans user nl' = withFastStore $ \db ->
|
||||
liftIO (getUserContactLinkViaTarget db user nl') >>= \case
|
||||
Just UserContactLink {connLinkContact} -> pure $ Just ((ACCL SCMContact connLinkContact, CPContactAddress CAPOwnLink Nothing), False)
|
||||
Nothing ->
|
||||
getContactToConnect db cxt user nl' >>= \case
|
||||
Just (ccl, ct')
|
||||
| contactDeleted ct' -> pure Nothing
|
||||
| otherwise -> do
|
||||
fresh <- liftIO $ resolvedRecently =<< getContactDomainResolution db user ct'
|
||||
pure $ Just ((ACCL SCMContact ccl, CPContactAddress (CAPKnown ct') Nothing), fresh)
|
||||
Nothing -> gPlan user db =<< getGroupToConnect db cxt user nl'
|
||||
knownGroupPlans :: User -> ContactNameOrLink -> CM (Maybe ((ACreatedConnLink, ConnectionPlan), Bool))
|
||||
knownGroupPlans user nl' = withFastStore $ \db ->
|
||||
liftIO (getGroupInfoViaUserTarget db cxt user nl') >>= \case
|
||||
Just (ccl, g) -> pure $ Just ((ACCL SCMContact ccl, CPGroupLink (GLPOwnLink g) Nothing), False)
|
||||
Nothing -> gPlan user db =<< getGroupToConnect db cxt user nl'
|
||||
gPlan user db = \case
|
||||
Just (ccl, g) | not (memberRemoved (membership g)) -> do
|
||||
fresh <- liftIO $ resolvedRecently =<< getGroupDomainResolution db user g
|
||||
pure $ Just ((ACCL SCMContact ccl, CPGroupLink (GLPKnown g False Nothing (ListDef [])) Nothing), fresh)
|
||||
_ -> pure Nothing
|
||||
resolvedRecently :: Maybe (UTCTime, Maybe UTCTime) -> IO Bool
|
||||
resolvedRecently = \case
|
||||
Just (resolvedAt, expiresAt_) -> do
|
||||
now <- getCurrentTime
|
||||
pure $ diffUTCTime now resolvedAt < nominalDay && maybe True (now <) expiresAt_
|
||||
Nothing -> pure False
|
||||
-- the chats a plan is about, the planned one first, and whether looking the name up can show more than them
|
||||
planLocalChats :: User -> AConnectTarget -> ConnectionPlan -> CM ([AChatInfo], Bool)
|
||||
planLocalChats user ct plan = do
|
||||
found <- nameLookups
|
||||
let chats = nubBy (\(AChatInfo _ c) (AChatInfo _ c') -> chatInfoToRef c == chatInfoToRef c') $ mapMaybe planChat $ plan : map (snd . fst) (catMaybes found)
|
||||
pure (chats, not $ all (maybe False snd) found)
|
||||
where
|
||||
nameLookups :: CM [Maybe ((ACreatedConnLink, ConnectionPlan), Bool)]
|
||||
nameLookups = case ct of
|
||||
ACTarget SCMContact (CTDomain d) -> sequence [knownGroupPlans user $ CTName $ SimplexNameInfo NTPublicGroup d, knownContactPlans user $ CTName $ SimplexNameInfo NTContact d]
|
||||
ACTarget SCMContact (CTShortContact nl@(CTName SimplexNameInfo {nameType = NTContact})) -> (: []) <$> knownContactPlans user nl
|
||||
ACTarget SCMContact (CTShortContact nl@(CTName SimplexNameInfo {nameType = NTPublicGroup})) -> (: []) <$> knownGroupPlans user nl
|
||||
_ -> pure []
|
||||
planChat = \case
|
||||
CPInvitationLink (ILPConnecting (Just ct')) -> directChat ct'
|
||||
CPInvitationLink (ILPKnown ct') -> directChat ct'
|
||||
CPContactAddress (CAPConnectingProhibit ct') _ -> directChat ct'
|
||||
CPContactAddress (CAPKnown ct') _ -> directChat ct'
|
||||
CPContactAddress (CAPContactViaAddress ct') _ -> directChat ct'
|
||||
CPGroupLink (GLPOwnLink g) _ -> groupChat g
|
||||
CPGroupLink (GLPConnectingProhibit (Just g)) _ -> groupChat g
|
||||
CPGroupLink (GLPKnown g _ _ _) _ -> groupChat g
|
||||
_ -> Nothing
|
||||
connectWithPlan :: User -> IncognitoEnabled -> Maybe ACreatedConnLink -> Maybe SimplexNameInfo -> Maybe SimplexNameInfo -> ConnectionPlan -> CM ChatResponse
|
||||
connectWithPlan user@User {userId} incognito ccLink_ planSimplexName otherSimplexName plan
|
||||
directChat = Just . AChatInfo SCTDirect . DirectChat
|
||||
groupChat g = Just $ AChatInfo SCTGroup $ GroupChat g Nothing
|
||||
connectWithPlan :: User -> IncognitoEnabled -> Maybe ACreatedConnLink -> Maybe SimplexNameInfo -> Maybe SimplexNameInfo -> ConnectionPlan -> ([AChatInfo], Bool) -> CM ChatResponse
|
||||
connectWithPlan user@User {userId} incognito ccLink_ planSimplexName otherSimplexName plan (localChats, offerLookup)
|
||||
| Just ccLink <- ccLink_, connectionPlanProceed plan = do
|
||||
case plan of CPError e -> eToView e; _ -> pure ()
|
||||
case plan of
|
||||
CPContactAddress (CAPContactViaAddress Contact {contactId}) _ ->
|
||||
processChatCommand cxt nm $ APIConnectContactViaAddress userId incognito contactId
|
||||
CPContactAddress (CAPOk (Just sld) _ _ _) _ | isJust vName -> connectContactViaName ccLink sld
|
||||
CPGroupLink (GLPOk (Just GroupShortLinkInfo {direct = False}) (Just gld) _ _ _) _
|
||||
CPContactAddress (CAPOk (Just sld) _ _) _ | isJust vName -> connectContactViaName ccLink sld
|
||||
CPGroupLink (GLPOk (Just GroupShortLinkInfo {direct = False}) (Just gld) _ _) _
|
||||
| ACCL SCMContact ccl <- ccLink -> joinChannelViaRelays ccl gld
|
||||
_ -> processChatCommand cxt nm $ APIConnect userId incognito $ Just ccLink
|
||||
| otherwise = pure $ CRConnectionPlan user ccLink_ planSimplexName otherSimplexName plan
|
||||
| otherwise = pure $ CRConnectionPlan user ccLink_ planSimplexName otherSimplexName plan localChats offerLookup
|
||||
where
|
||||
vName = nameDomain <$> planSimplexName
|
||||
joinChannelViaRelays :: CreatedLinkContact -> GroupShortLinkData -> CM ChatResponse
|
||||
@@ -4709,13 +4751,13 @@ processChatCommand cxt nm = \case
|
||||
Nothing ->
|
||||
withFastStore' (\db -> getContactWithoutConnViaAddress db cxt user cReqSchemas) >>= \case
|
||||
Just ct | not (contactDeleted ct) -> plan $ CAPContactViaAddress ct
|
||||
_ -> plan $ CAPOk cld ov False Nothing
|
||||
_ -> plan $ CAPOk cld ov False
|
||||
Just (RcvDirectMsgConnection Connection {connStatus} Nothing)
|
||||
| connStatus == ConnPrepared -> plan $ CAPOk cld ov False Nothing
|
||||
| connStatus == ConnPrepared -> plan $ CAPOk cld ov False
|
||||
| otherwise -> plan CAPConnectingConfirmReconnect
|
||||
Just (RcvDirectMsgConnection _ (Just ct))
|
||||
| not (contactReady ct) && contactActive ct -> plan $ CAPConnectingProhibit ct
|
||||
| contactDeleted ct -> plan $ CAPOk cld ov False Nothing
|
||||
| contactDeleted ct -> plan $ CAPOk cld ov False
|
||||
| otherwise -> plan $ CAPKnown ct
|
||||
-- TODO [short links] RcvGroupMsgConnection branch is deprecated? (old group link protocol?)
|
||||
Just (RcvGroupMsgConnection _ gInfo _) -> groupPlan gInfo Nothing Nothing Nothing []
|
||||
@@ -4731,12 +4773,12 @@ processChatCommand cxt nm = \case
|
||||
connEnt_ <- withFastStore' $ \db -> getContactConnEntityByConnReqHash db cxt user cReqHashes
|
||||
gInfo_ <- withFastStore' $ \db -> getGroupInfoByGroupLinkHash db cxt user cReqHashes
|
||||
case (gInfo_, connEnt_) of
|
||||
(Nothing, Nothing) -> plan $ GLPOk linkInfo gld ov False Nothing
|
||||
(Nothing, Nothing) -> plan $ GLPOk linkInfo gld ov False
|
||||
-- TODO [short links] RcvDirectMsgConnection branches are deprecated? (old group link protocol?)
|
||||
(Nothing, Just (RcvDirectMsgConnection _conn Nothing)) -> plan $ GLPConnectingConfirmReconnect
|
||||
(Nothing, Just (RcvDirectMsgConnection _ (Just ct)))
|
||||
| not (contactReady ct) && contactActive ct -> plan $ GLPConnectingProhibit gInfo_
|
||||
| otherwise -> plan $ GLPOk linkInfo gld ov False Nothing
|
||||
| otherwise -> plan $ GLPOk linkInfo gld ov False
|
||||
(Nothing, Just _) -> throwCmdError "found connection entity is not RcvDirectMsgConnection"
|
||||
(Just gInfo, _) -> groupPlan gInfo linkInfo gld ov glOwners
|
||||
groupPlan :: GroupInfo -> Maybe GroupShortLinkInfo -> Maybe GroupShortLinkData -> Maybe OwnerVerification -> [GroupLinkOwner] -> CM ConnectionPlan
|
||||
@@ -4745,7 +4787,7 @@ processChatCommand cxt nm = \case
|
||||
| not (memberActive membership) && not (memberRemoved membership) =
|
||||
plan $ GLPConnectingProhibit $ Just gInfo
|
||||
| memberActive membership = plan $ GLPKnown gInfo False ov (ListDef glOwners)
|
||||
| otherwise = plan $ GLPOk linkInfo gld ov False Nothing
|
||||
| otherwise = plan $ GLPOk linkInfo gld ov False
|
||||
where
|
||||
plan p = pure $ CPGroupLink p Nothing
|
||||
contactCReqSchemas :: ConnReqContact -> (ConnReqContact, ConnReqContact)
|
||||
@@ -5158,7 +5200,7 @@ nameExpiresAt = \case
|
||||
nameLinkOrWarning :: SystemSeconds -> SimplexNameInfo -> NameRegistration -> Either NameWarning ShortLinkContact
|
||||
nameLinkOrWarning now SimplexNameInfo {nameType, nameDomain = SimplexDomain {domain}} = \case
|
||||
NRRegistered {expires = Just expires, graceUntil}
|
||||
| expires < now -> Left $ NWExpired (roundedToUTCTime expires) (roundedToUTCTime <$> graceUntil)
|
||||
| expires < now -> Left $ NWExpired (roundedToUTCTime expires) (roundedToUTCTime <$> mfilter (now <=) graceUntil)
|
||||
NRRegistered {nameRecord = NameRecord {nrSimplexContact, nrSimplexChannel}} ->
|
||||
maybe (Left NWNoValidLink) Right $ case nameType of
|
||||
NTContact -> firstNameLink CCTContact nrSimplexContact
|
||||
@@ -5192,18 +5234,17 @@ setNameWarning w = \case
|
||||
NWReservedForCommunity -> Just w
|
||||
_ -> Nothing
|
||||
|
||||
setAddressChanged :: ConnectionPlan -> ConnectionPlan -> ConnectionPlan
|
||||
setAddressChanged known = \case
|
||||
CPContactAddress (CAPOk cld ov _ _) w_ -> CPContactAddress (CAPOk cld ov True existingChat_) w_
|
||||
CPGroupLink (GLPOk li gld ov _ _) w_ -> CPGroupLink (GLPOk li gld ov True existingChat_) w_
|
||||
p -> p
|
||||
where
|
||||
existingChat_ = case known of
|
||||
CPContactAddress (CAPKnown ct) _ -> Just $ AChatInfo SCTDirect $ DirectChat ct
|
||||
CPGroupLink (GLPKnown g _ _ _) _ -> groupChat g
|
||||
CPGroupLink (GLPOwnLink g) _ -> groupChat g
|
||||
_ -> Nothing
|
||||
groupChat g = Just $ AChatInfo SCTGroup $ GroupChat g Nothing
|
||||
setAddressChanged :: ConnectionPlan -> Maybe ConnectionPlan
|
||||
setAddressChanged = \case
|
||||
CPContactAddress (CAPOk cld ov _) w_ -> Just $ CPContactAddress (CAPOk cld ov True) w_
|
||||
CPGroupLink (GLPOk li gld ov _) w_ -> Just $ CPGroupLink (GLPOk li gld ov True) w_
|
||||
CPContactAddress CAPConnectingConfirmReconnect _ -> Nothing
|
||||
CPContactAddress CAPConnectingProhibit {} _ -> Nothing
|
||||
CPGroupLink GLPConnectingConfirmReconnect _ -> Nothing
|
||||
CPGroupLink GLPConnectingProhibit {} _ -> Nothing
|
||||
CPGroupLink GLPNoRelays {} _ -> Nothing
|
||||
CPGroupLink GLPUpdateRequired {} _ -> Nothing
|
||||
p -> Just p
|
||||
|
||||
verifyEntityDomain :: User -> NetworkRequestMode -> SimplexNameType -> SimplexDomainClaim -> Maybe AConnShortLink -> CM (Maybe Bool, Maybe Text)
|
||||
verifyEntityDomain user nm nameType SimplexDomainClaim {domain = StrJSON domain, proof = proof_} connLink_ = case (proof_, connLink_) of
|
||||
|
||||
@@ -1619,14 +1619,16 @@ updateGroupFromLinkData :: User -> GroupInfo -> GroupShortLinkData -> Maybe Simp
|
||||
updateGroupFromLinkData user gInfo@GroupInfo {groupProfile = p, groupSummary = GroupSummary {publicMemberCount = localCount}} GroupShortLinkData {groupProfile, publicGroupData} resolvedDomain_ expiresAt
|
||||
| profileChanged || countChanged || verifyResolved = do
|
||||
cxt <- chatStoreCxt
|
||||
withStore $ \db -> do
|
||||
g'' <- withStore $ \db -> do
|
||||
g <- if profileChanged then updateGroupProfile db user gInfo groupProfile else pure gInfo
|
||||
g' <- case publicGroupData of
|
||||
Just PublicGroupData {publicMemberCount} | countChanged ->
|
||||
setPublicMemberCount db cxt user g publicMemberCount
|
||||
_ -> pure g
|
||||
g'' <- if verifyResolved then liftIO $ setGroupDomainVerified db user g' True expiresAt else pure g'
|
||||
pure (g'', profileChanged)
|
||||
if verifyResolved then liftIO $ setGroupDomainVerified db user g' True expiresAt else pure g'
|
||||
toView $ CEvtGroupUpdated user gInfo g'' Nothing Nothing
|
||||
when verifyResolved $ forM_ newClaim $ nameChatsUpdated user NTPublicGroup
|
||||
pure (g'', profileChanged)
|
||||
| otherwise = pure (gInfo, False)
|
||||
where
|
||||
profileChanged = p /= groupProfile
|
||||
@@ -1641,15 +1643,25 @@ updateContactFromLinkData :: User -> Contact -> Profile -> Maybe UTCTime -> CM C
|
||||
updateContactFromLinkData user ct@Contact {profile = profile@LocalProfile {contactDomain = prevClaim, contactDomainVerified}} linkProfile@Profile {contactDomain = newClaim} expiresAt
|
||||
| profileChanged || verifyChanged = do
|
||||
cxt <- chatStoreCxt
|
||||
withFastStore $ \db -> do
|
||||
ct'' <- withFastStore $ \db -> do
|
||||
ct' <- updateContactProfile db cxt user ct linkProfile
|
||||
if verifyChanged then liftIO $ setContactDomainVerified db user ct' True expiresAt else pure ct'
|
||||
toView $ CEvtContactUpdated user ct ct''
|
||||
when verifyChanged $ forM_ newClaim $ nameChatsUpdated user NTContact . claimDomain
|
||||
pure ct''
|
||||
| otherwise = pure ct
|
||||
where
|
||||
profileChanged = fromLocalProfile profile /= linkProfile
|
||||
claimChanged = (claimDomain <$> prevClaim) /= (claimDomain <$> newClaim)
|
||||
verifyChanged = contactDomainVerified /= Just True || claimChanged
|
||||
|
||||
nameChatsUpdated :: User -> SimplexNameType -> SimplexDomain -> CM ()
|
||||
nameChatsUpdated user nameType domain = do
|
||||
cxt <- chatStoreCxt
|
||||
(cts, gs) <- withStore' $ \db -> getNameChats db cxt user nameType domain
|
||||
forM_ cts $ \ct -> toView $ CEvtContactUpdated user ct ct
|
||||
forM_ gs $ \g -> toView $ CEvtGroupUpdated user g g Nothing Nothing
|
||||
|
||||
-- TODO [relays] owner: set owners on updating link data (multi-owner)
|
||||
groupLinkData :: GroupInfoKeys -> GroupLink -> [GroupRelay] -> (UserConnLinkData 'CMContact, CRClientData)
|
||||
groupLinkData (GIK gInfo@GroupInfo {groupProfile, groupSummary = GroupSummary {publicMemberCount}, membership = GroupMember {memberId}} gks) GroupLink {groupLinkId} groupRelays =
|
||||
|
||||
@@ -47,6 +47,7 @@ module Simplex.Chat.Store.Groups
|
||||
getGroupInfoByGroupLinkHash,
|
||||
updateGroupProfile,
|
||||
setGroupDomainVerified,
|
||||
getNameChats,
|
||||
getGroupDomainResolution,
|
||||
updateGroupPreferences,
|
||||
updateGroupProfileFromMember,
|
||||
@@ -2755,6 +2756,20 @@ setGroupDomainVerified db User {userId} g@GroupInfo {groupId, businessChat} veri
|
||||
(BI verified, currentTs, expiresAt, userId, groupId)
|
||||
pure g {groupDomainVerified = Just verified}
|
||||
|
||||
getNameChats :: DB.Connection -> StoreCxt -> User -> SimplexNameType -> SimplexDomain -> IO ([Contact], [GroupInfo])
|
||||
getNameChats db cxt user@User {userId} nameType domain = do
|
||||
cts <- case nameType of
|
||||
NTContact -> ids "SELECT ct.contact_id FROM contacts ct JOIN contact_profiles cp ON cp.contact_profile_id = ct.contact_profile_id WHERE ct.user_id = ? AND cp.contact_domain = ? AND ct.deleted = 0" >>= fmap rights . mapM (runExceptT . getContact db cxt user)
|
||||
NTPublicGroup -> pure []
|
||||
gs <- ids (groupsQuery <> businessCond) >>= fmap rights . mapM (runExceptT . getGroupInfo db cxt user)
|
||||
pure (cts, gs)
|
||||
where
|
||||
ids q = map fromOnly <$> DB.query db q (userId, domain)
|
||||
groupsQuery = "SELECT g.group_id FROM groups g JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id WHERE g.user_id = ? AND gp.group_domain = ?"
|
||||
businessCond = case nameType of
|
||||
NTContact -> " AND g.business_chat IS NOT NULL"
|
||||
NTPublicGroup -> " AND g.business_chat IS NULL"
|
||||
|
||||
getGroupDomainResolution :: DB.Connection -> User -> GroupInfo -> IO (Maybe (UTCTime, Maybe UTCTime))
|
||||
getGroupDomainResolution db User {userId} GroupInfo {groupId} =
|
||||
maybeFirstRow id $
|
||||
|
||||
@@ -217,7 +217,7 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, showFullLinks, te
|
||||
CRInvitation u ccLink _ -> ttyUser u $ viewConnReqInvitation showFullLinks ccLink
|
||||
CRConnectionIncognitoUpdated u c customUserProfile -> ttyUser u $ viewConnectionIncognitoUpdated c customUserProfile testView
|
||||
CRConnectionUserChanged u c c' nu -> ttyUser u $ viewConnectionUserChanged showFullLinks u c nu c'
|
||||
CRConnectionPlan u connLink planSimplexName otherSimplexName connectionPlan -> ttyUser u $ viewConnectionPlan cfg connLink connectionPlan <> otherSimplexNameNote otherSimplexName <> viewNameWarning planSimplexName connectionPlan
|
||||
CRConnectionPlan u connLink planSimplexName otherSimplexName connectionPlan _ _ -> ttyUser u $ viewConnectionPlan cfg connLink connectionPlan <> otherSimplexNameNote otherSimplexName <> viewNameWarning planSimplexName connectionPlan
|
||||
CRNewPreparedChat u (AChat _ (Chat cInfo _ _)) -> ttyUser u $ case cInfo of
|
||||
DirectChat ct -> [ttyContact' ct <> ": contact is prepared"]
|
||||
GroupChat g _ -> [ttyGroup' g <> ": group is prepared"]
|
||||
@@ -2278,7 +2278,7 @@ viewConnectionPlan ChatConfig {logLevel, testView} _connLink = \case
|
||||
| business -> ("business address: " <>)
|
||||
_ -> ("invitation link: " <>)
|
||||
CPContactAddress cap _ -> case cap of
|
||||
CAPOk contactSLinkData ov addressChanged _ -> [addrOrBiz contactSLinkData ("ok to connect" <> (if addressChanged then ", address changed" else ""))] <> viewSigVerification ov <> [viewJSON contactSLinkData | testView]
|
||||
CAPOk contactSLinkData ov addressChanged -> [addrOrBiz contactSLinkData ("ok to connect" <> (if addressChanged then ", address changed" else ""))] <> viewSigVerification ov <> [viewJSON contactSLinkData | testView]
|
||||
CAPOwnLink -> [ctAddr "own address"]
|
||||
CAPConnectingConfirmReconnect -> [ctAddr "connecting, allowed to reconnect"]
|
||||
CAPConnectingProhibit ct -> [ctAddr ("connecting to contact " <> ttyContact' ct)]
|
||||
@@ -2296,7 +2296,7 @@ viewConnectionPlan ChatConfig {logLevel, testView} _connLink = \case
|
||||
| business -> ("business address: " <>)
|
||||
_ -> ("contact address: " <>)
|
||||
CPGroupLink glp _ -> case glp of
|
||||
GLPOk groupSLinkInfo_ groupSLinkData ov addressChanged _ ->
|
||||
GLPOk groupSLinkInfo_ groupSLinkData ov addressChanged ->
|
||||
let direct = maybe True (\(GroupShortLinkInfo {direct = d}) -> d) groupSLinkInfo_
|
||||
in [grpLink $ (if direct then "ok to connect directly" else "ok to connect via relays") <> (if addressChanged then ", address changed" else "")]
|
||||
<> viewSigVerification ov
|
||||
|
||||
Reference in new issue
Block a user