diff --git a/cabal.project b/cabal.project index c13077743a..d50291f9bb 100644 --- a/cabal.project +++ b/cabal.project @@ -5,7 +5,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: d1db7d6f79b527a689af1807e7d91a92076d2d8c + tag: 0ab44b18362114752e529405da33b85f091495ec source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 5388d80e6a..89725a1c15 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."d1db7d6f79b527a689af1807e7d91a92076d2d8c" = "1zlp925qq8akv8v846g03czn0yb293vdsgd7vs6g0axs4yx6c9ap"; + "https://github.com/simplex-chat/simplexmq.git"."0ab44b18362114752e529405da33b85f091495ec" = "0pskp5ni8c0w71ck60laq7n2vf3sivaf6lvjbcv7rpkhibm6svqm"; "https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp"; "https://github.com/simplex-chat/haskell-terminal.git"."f708b00009b54890172068f168bf98508ffcd495" = "0zmq7lmfsk8m340g47g5963yba7i88n4afa6z93sg9px5jv1mijj"; "https://github.com/zw3rk/android-support.git"."3c3a5ab0b8b137a072c98d3d0937cbdc96918ddb" = "1r6jyxbim3dsvrmakqfyxbd6ms6miaghpbwyl0sr6dzwpgaprz97"; diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 4a6213f904..0aac23fecc 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -39,6 +39,7 @@ import Data.Maybe (fromMaybe, isJust, mapMaybe) import Data.Text (Text) import qualified Data.Text as T import Data.Time.Clock (UTCTime, diffUTCTime, getCurrentTime, nominalDiffTimeToSeconds) +import Data.Time.Clock.System (SystemTime, systemToUTCTime) import Data.Time.LocalTime (getCurrentTimeZone, getZonedTime) import Data.Word (Word32) import qualified Database.SQLite.Simple as DB @@ -204,6 +205,7 @@ processChatCommand = \case APIStopChat -> do ask >>= stopChatController pure CRChatStopped + APISetAppPhase phase -> withAgent (`setAgentPhase` phase) $> CRCmdOk ResubscribeAllConnections -> withUser (subscribeUserConnections resubscribeConnection) $> CRCmdOk SetFilesFolder filesFolder' -> withUser $ \_ -> do createDirectoryIfMissing True filesFolder' @@ -542,6 +544,12 @@ processChatCommand = \case APIVerifyToken token code nonce -> withUser $ \_ -> withAgent (\a -> verifyNtfToken a token code nonce) $> CRCmdOk APIIntervalNofication token interval -> withUser $ \_ -> withAgent (\a -> enableNtfCron a token interval) $> CRCmdOk APIDeleteToken token -> withUser $ \_ -> withAgent (`deleteNtfToken` token) $> CRCmdOk + APIGetNtfMessage nonce encNtfInfo -> withUser $ \user -> do + (NotificationInfo {ntfConnId, ntfMsgMeta}, msgs) <- withAgent $ \a -> getNotificationMessage a nonce encNtfInfo + let ntfMessages = map (\SMP.SMPMsgMeta {msgTs, msgFlags} -> NtfMsgInfo {msgTs = systemToUTCTime msgTs, msgFlags}) msgs + msgTs' = systemToUTCTime . (SMP.msgTs :: SMP.NMsgMeta -> SystemTime) <$> ntfMsgMeta + connEntity <- withStore (\db -> Just <$> getConnectionEntity db user ntfConnId) `catchError` \_ -> pure Nothing + pure CRNtfMessages {connEntity, msgTs = msgTs', ntfMessages} GetUserSMPServers -> CRUserSMPServers <$> withUser (\user -> withStore' (`getSMPServers` user)) SetUserSMPServers smpServers -> withUser $ \user -> withChatLock $ do withStore $ \db -> overwriteSMPServers db user smpServers @@ -2240,6 +2248,7 @@ chatCommandP = <|> ("/user" <|> "/u") $> ShowActiveUser <|> "/_start" $> StartChat <|> "/_stop" $> APIStopChat + <|> "/_app phase " *> (APISetAppPhase <$> strP) <|> "/_resubscribe all" $> ResubscribeAllConnections <|> "/_files_folder " *> (SetFilesFolder <$> filePath) <|> "/_db export " *> (APIExportArchive <$> jsonP) @@ -2270,6 +2279,7 @@ chatCommandP = <|> "/_ntf verify " *> (APIVerifyToken <$> tokenP <* A.space <*> strP <* A.space <*> strP) <|> "/_ntf interval " *> (APIIntervalNofication <$> tokenP <* A.space <*> A.decimal) <|> "/_ntf delete " *> (APIDeleteToken <$> tokenP) + <|> "/_ntf message " *> (APIGetNtfMessage <$> strP <* A.space <*> strP) <|> "/smp_servers default" $> SetUserSMPServers [] <|> "/smp_servers " *> (SetUserSMPServers <$> smpServersP) <|> "/smp_servers" $> GetUserSMPServers diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index dd58d5e71a..a24274688c 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -40,7 +40,7 @@ import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus) import Simplex.Messaging.Parsers (dropPrefix, enumJSON, sumTypeJSON) -import Simplex.Messaging.Protocol (CorrId) +import Simplex.Messaging.Protocol (CorrId, MsgFlags) import Simplex.Messaging.TMap (TMap) import System.IO (Handle) import UnliftIO.STM @@ -101,6 +101,7 @@ data ChatCommand | CreateActiveUser Profile | StartChat | APIStopChat + | APISetAppPhase AgentPhase | ResubscribeAllConnections | SetFilesFolder FilePath | APIExportArchive ArchiveConfig @@ -131,6 +132,7 @@ data ChatCommand | APIVerifyToken DeviceToken ByteString C.CbNonce | APIIntervalNofication DeviceToken Word16 | APIDeleteToken DeviceToken + | APIGetNtfMessage {nonce :: C.CbNonce, encNtfInfo :: ByteString} | GetUserSMPServers | SetUserSMPServers [SMPServer] | ChatHelp HelpSection @@ -183,6 +185,7 @@ data ChatResponse | CRChatStarted | CRChatRunning | CRChatStopped + | CRAppPhase {appPhase :: AgentPhase} | CRApiChats {chats :: [AChat]} | CRApiChat {chat :: AChat} | CRLastMessages {chatItems :: [AChatItem]} @@ -273,6 +276,7 @@ data ChatResponse | CRUserContactLinkSubscribed | CRUserContactLinkSubError {chatError :: ChatError} | CRNtfTokenStatus {status :: NtfTknStatus} + | CRNtfMessages {connEntity :: Maybe ConnectionEntity, msgTs :: Maybe UTCTime, ntfMessages :: [NtfMsgInfo]} | CRNewContactConnection {connection :: PendingContactConnection} | CRContactConnectionDeleted {connection :: PendingContactConnection} | CRMessageError {severity :: Text, errorMessage :: Text} @@ -323,6 +327,11 @@ data ComposedMessage = ComposedMessage } deriving (Show, Generic, FromJSON) +data NtfMsgInfo = NtfMsgInfo {msgTs :: UTCTime, msgFlags :: MsgFlags} + deriving (Show, Generic) + +instance ToJSON NtfMsgInfo where toEncoding = J.genericToEncoding J.defaultOptions + data ChatError = ChatError {errorType :: ChatErrorType} | ChatErrorAgent {agentError :: AgentErrorType} diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 9418d26223..df6dc640ba 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -33,7 +33,7 @@ import Simplex.Chat.Call import Simplex.Chat.Types import Simplex.Chat.Util (safeDecodeUtf8) import Simplex.Messaging.Encoding.String -import Simplex.Messaging.Parsers (fromTextField_) +import Simplex.Messaging.Parsers (fromTextField_, sumTypeJSON) import Simplex.Messaging.Util (eitherToMaybe, (<$?>)) data ConnectionEntity @@ -42,7 +42,11 @@ data ConnectionEntity | SndFileConnection {entityConnection :: Connection, sndFileTransfer :: SndFileTransfer} | RcvFileConnection {entityConnection :: Connection, rcvFileTransfer :: RcvFileTransfer} | UserContactConnection {entityConnection :: Connection, userContact :: UserContact} - deriving (Eq, Show) + deriving (Eq, Show, Generic) + +instance ToJSON ConnectionEntity where + toJSON = J.genericToJSON $ sumTypeJSON id + toEncoding = J.genericToEncoding $ sumTypeJSON id updateEntityConnStatus :: ConnectionEntity -> ConnStatus -> ConnectionEntity updateEntityConnStatus connEntity connStatus = case connEntity of diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index d123a90355..e3c679372b 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -99,7 +99,11 @@ data UserContact = UserContact { userContactLinkId :: Int64, connReqContact :: ConnReqContact } - deriving (Eq, Show) + deriving (Eq, Show, Generic) + +instance ToJSON UserContact where + toJSON = J.genericToJSON J.defaultOptions + toEncoding = J.genericToEncoding J.defaultOptions data UserContactRequest = UserContactRequest { contactRequestId :: Int64, diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 6ac5b0faa0..090fe4fcff 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -55,6 +55,7 @@ responseToView testView = \case CRChatStarted -> ["chat started"] CRChatRunning -> ["chat is running"] CRChatStopped -> ["chat stopped"] + CRAppPhase _ -> [] CRApiChats chats -> if testView then testViewChats chats else [plain . bshow $ J.encode chats] CRApiChat chat -> if testView then testViewChat chat else [plain . bshow $ J.encode chat] CRApiParsedMarkdown ft -> [plain . bshow $ J.encode ft] @@ -161,6 +162,7 @@ responseToView testView = \case CRNewContactConnection _ -> [] CRContactConnectionDeleted PendingContactConnection {pccConnId} -> ["connection :" <> sShow pccConnId <> " deleted"] CRNtfTokenStatus status -> ["device token status: " <> plain (smpEncode status)] + CRNtfMessages {} -> [] CRMessageError prefix err -> [plain prefix <> ": " <> plain err] CRChatError e -> viewChatError e where diff --git a/stack.yaml b/stack.yaml index 3f3f2e42b4..4bdf273bae 100644 --- a/stack.yaml +++ b/stack.yaml @@ -49,7 +49,7 @@ extra-deps: # - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561 # - ../simplexmq - github: simplex-chat/simplexmq - commit: d1db7d6f79b527a689af1807e7d91a92076d2d8c + commit: 0ab44b18362114752e529405da33b85f091495ec # - terminal-0.2.0.0@sha256:de6770ecaae3197c66ac1f0db5a80cf5a5b1d3b64a66a05b50f442de5ad39570,2977 - github: simplex-chat/aeson commit: 3eb66f9a68f103b5f1489382aad89f5712a64db7