core: track network statuses, use in commands/events (#3211)

* core: track network statuses, use in commands/events

* ui types, test

* remove comment
This commit is contained in:
Evgeny Poberezkin
2023-10-13 11:51:01 +01:00
committed by GitHub
parent 675fc19745
commit ab290fb068
13 changed files with 275 additions and 102 deletions
+14 -1
View File
@@ -32,6 +32,7 @@ import Data.Char (ord)
import Data.Int (Int64)
import Data.List.NonEmpty (NonEmpty)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.String
import Data.Text (Text)
import Data.Time (NominalDiffTime, UTCTime)
@@ -124,7 +125,8 @@ data ChatConfig = ChatConfig
initialCleanupManagerDelay :: Int64,
cleanupManagerInterval :: NominalDiffTime,
cleanupManagerStepDelay :: Int64,
ciExpirationInterval :: Int64 -- microseconds
ciExpirationInterval :: Int64, -- microseconds
coreApi :: Bool
}
data DefaultAgentServers = DefaultAgentServers
@@ -164,6 +166,7 @@ data ChatController = ChatController
idsDrg :: TVar ChaChaDRG,
inputQ :: TBQueue String,
outputQ :: TBQueue (Maybe CorrId, ChatResponse),
connNetworkStatuses :: TMap AgentConnId NetworkStatus,
subscriptionMode :: TVar SubscriptionMode,
chatLock :: Lock,
sndFiles :: TVar (Map Int64 Handle),
@@ -251,6 +254,7 @@ data ChatCommand
| APIEndCall ContactId
| APIGetCallInvitations
| APICallStatus ContactId WebRTCCallStatus
| APIGetNetworkStatuses
| APIUpdateProfile UserId Profile
| APISetContactPrefs ContactId Preferences
| APISetContactAlias ContactId LocalAlias
@@ -528,6 +532,8 @@ data ChatResponse
| CRContactSubError {user :: User, contact :: Contact, chatError :: ChatError}
| CRContactSubSummary {user :: User, contactSubscriptions :: [ContactSubStatus]}
| CRUserContactSubSummary {user :: User, userContactSubscriptions :: [UserContactSubStatus]}
| CRNetworkStatus {networkStatus :: NetworkStatus, connections :: [AgentConnId]}
| CRNetworkStatuses {user_ :: Maybe User, networkStatuses :: [ConnNetworkStatus]}
| CRHostConnected {protocol :: AProtocolType, transportHost :: TransportHost}
| CRHostDisconnected {protocol :: AProtocolType, transportHost :: TransportHost}
| CRGroupInvitation {user :: User, groupInfo :: GroupInfo}
@@ -1044,6 +1050,13 @@ chatWriteVar :: ChatMonad' m => (ChatController -> TVar a) -> a -> m ()
chatWriteVar f value = asks f >>= atomically . (`writeTVar` value)
{-# INLINE chatWriteVar #-}
chatModifyVar :: ChatMonad' m => (ChatController -> TVar a) -> (a -> a) -> m ()
chatModifyVar f newValue = asks f >>= atomically . (`modifyTVar'` newValue)
{-# INLINE chatModifyVar #-}
setContactNetworkStatus :: ChatMonad' m => Contact -> NetworkStatus -> m ()
setContactNetworkStatus ct = chatModifyVar connNetworkStatuses . M.insert (contactAgentConnId ct)
tryChatError :: ChatMonad m => m a -> m (Either ChatError a)
tryChatError = tryAllErrors mkChatError
{-# INLINE tryChatError #-}
+2 -1
View File
@@ -169,7 +169,8 @@ defaultMobileConfig :: ChatConfig
defaultMobileConfig =
defaultChatConfig
{ confirmMigrations = MCYesUp,
logLevel = CLLError
logLevel = CLLError,
coreApi = True
}
getActiveUser_ :: SQLiteStore -> IO (Maybe User)
+36 -1
View File
@@ -192,6 +192,9 @@ instance ToJSON Contact where
contactConn :: Contact -> Connection
contactConn Contact {activeConn} = activeConn
contactAgentConnId :: Contact -> AgentConnId
contactAgentConnId Contact {activeConn = Connection {agentConnId}} = agentConnId
contactConnId :: Contact -> ConnId
contactConnId = aConnId . contactConn
@@ -1140,13 +1143,16 @@ liveRcvFileTransferPath ft = fp <$> liveRcvFileTransferInfo ft
fp RcvFileInfo {filePath} = filePath
newtype AgentConnId = AgentConnId ConnId
deriving (Eq, Show)
deriving (Eq, Ord, Show)
instance StrEncoding AgentConnId where
strEncode (AgentConnId connId) = strEncode connId
strDecode s = AgentConnId <$> strDecode s
strP = AgentConnId <$> strP
instance FromJSON AgentConnId where
parseJSON = strParseJSON "AgentConnId"
instance ToJSON AgentConnId where
toJSON = strToJSON
toEncoding = strToJEncoding
@@ -1477,6 +1483,35 @@ serializeIntroStatus = \case
textParseJSON :: TextEncoding a => String -> J.Value -> JT.Parser a
textParseJSON name = J.withText name $ maybe (fail $ "bad " <> name) pure . textDecode
data NetworkStatus
= NSUnknown
| NSConnected
| NSDisconnected
| NSError {connectionError :: String}
deriving (Eq, Ord, Show, Generic)
netStatusStr :: NetworkStatus -> String
netStatusStr = \case
NSUnknown -> "unknown"
NSConnected -> "connected"
NSDisconnected -> "disconnected"
NSError e -> "error: " <> e
instance FromJSON NetworkStatus where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "NS"
instance ToJSON NetworkStatus where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "NS"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "NS"
data ConnNetworkStatus = ConnNetworkStatus
{ agentConnId :: AgentConnId,
networkStatus :: NetworkStatus
}
deriving (Show, Generic, FromJSON)
instance ToJSON ConnNetworkStatus where toEncoding = J.genericToEncoding J.defaultOptions
type CommandId = Int64
aCorrId :: CommandId -> ACorrId
+9 -1
View File
@@ -19,7 +19,7 @@ import Data.Char (isSpace, toUpper)
import Data.Function (on)
import Data.Int (Int64)
import Data.List (groupBy, intercalate, intersperse, partition, sortOn)
import Data.List.NonEmpty (NonEmpty)
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as L
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
@@ -210,6 +210,8 @@ responseToView user_ ChatConfig {logLevel, showReactions, showReceipts, testView
(addresses, groupLinks) = partition (\UserContactSubStatus {userContact} -> isNothing . userContactGroupId $ userContact) summary
addressSS UserContactSubStatus {userContactError} = maybe ("Your address is active! To show: " <> highlight' "/sa") (\e -> "User address error: " <> sShow e <> ", to delete your address: " <> highlight' "/da") userContactError
(groupLinkErrors, groupLinksSubscribed) = partition (isJust . userContactError) groupLinks
CRNetworkStatus status conns -> if testView then [plain $ show (length conns) <> " connections " <> netStatusStr status] else []
CRNetworkStatuses u statuses -> if testView then ttyUser' u $ viewNetworkStatuses statuses else []
CRGroupInvitation u g -> ttyUser u [groupInvitation' g]
CRReceivedGroupInvitation {user = u, groupInfo = g, contact = c, memberRole = r} -> ttyUser u $ viewReceivedGroupInvitation g c r
CRUserJoinedGroup u g _ -> ttyUser u $ viewUserJoinedGroup g
@@ -800,6 +802,12 @@ viewDirectMessagesProhibited :: MsgDirection -> Contact -> [StyledString]
viewDirectMessagesProhibited MDSnd c = ["direct messages to indirect contact " <> ttyContact' c <> " are prohibited"]
viewDirectMessagesProhibited MDRcv c = ["received prohibited direct message from indirect contact " <> ttyContact' c <> " (discarded)"]
viewNetworkStatuses :: [ConnNetworkStatus] -> [StyledString]
viewNetworkStatuses = map viewStatuses . L.groupBy ((==) `on` netStatus) . sortOn netStatus
where
netStatus ConnNetworkStatus {networkStatus} = networkStatus
viewStatuses ss@(s :| _) = plain $ show (L.length ss) <> " connections " <> netStatusStr (netStatus s)
viewUserJoinedGroup :: GroupInfo -> [StyledString]
viewUserJoinedGroup g =
case incognitoMembershipProfile g of