core: add FromJSON instance to ChatResponse (#3129)

* Start adding FromJSON instances to ChatResponse

* progress

* FromJSON instance for ChatResponse compiles

* restore removed encodings

* remove comment

* diff

* update simplexmq, use TH for JSON

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
Alexander Bondarenko
2023-10-05 21:49:20 +03:00
committed by GitHub
parent 27e8a81c9f
commit fc9db9c381
18 changed files with 483 additions and 209 deletions

View File

@@ -9,7 +9,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: 753a6c7542c3764fda9ce3f4c4cdc9f2329816d3
tag: 96a38505d63ec9a12096991e7725b250e397af72
source-repository-package
type: git

View File

@@ -1,5 +1,5 @@
{
"https://github.com/simplex-chat/simplexmq.git"."ec1b72cb8013a65a5d9783104a47ae44f5730089" = "1lz5rvgxp242zg95r9zd9j50y45314cf8nfpjg1qsa55nrk2w19b";
"https://github.com/simplex-chat/simplexmq.git"."96a38505d63ec9a12096991e7725b250e397af72" = "0kllakklvfrbpjlk6zi5mbxqm1prp6xdwyh2y4fw9n6c8b76is98";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/kazu-yamamoto/http2.git"."b5a1b7200cf5bc7044af34ba325284271f6dff25" = "0dqb50j57an64nf4qcf5vcz4xkd1vzvghvf8bk529c1k30r9nfzb";
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "0kiwhvml42g9anw4d2v0zd1fpc790pj9syg5x3ik4l97fnkbbwpp";

View File

@@ -469,11 +469,11 @@ processChatCommand = \case
DeleteUser uName delSMPQueues viewPwd_ -> withUserName uName $ \userId -> APIDeleteUser userId delSMPQueues viewPwd_
StartChat subConns enableExpireCIs startXFTPWorkers -> withUser' $ \_ ->
asks agentAsync >>= readTVarIO >>= \case
Just _ -> pure CRChatRunning
_ -> checkStoreNotChanged $ startChatController subConns enableExpireCIs startXFTPWorkers $> CRChatStarted
Just _ -> pure $ CRChatRunning Nothing
_ -> checkStoreNotChanged $ startChatController subConns enableExpireCIs startXFTPWorkers $> CRChatStarted Nothing
APIStopChat -> do
ask >>= stopChatController
pure CRChatStopped
pure $ CRChatStopped Nothing
APIActivateChat -> withUser $ \_ -> do
restoreCalls
withAgent foregroundAgent
@@ -2814,7 +2814,7 @@ processAgentMessageNoConn = \case
DISCONNECT p h -> hostEvent $ CRHostDisconnected p h
DOWN srv conns -> serverEvent srv conns CRContactsDisconnected "disconnected"
UP srv conns -> serverEvent srv conns CRContactsSubscribed "connected"
SUSPENDED -> toView CRChatSuspended
SUSPENDED -> toView $ CRChatSuspended Nothing
DEL_USER agentUserId -> toView $ CRAgentUserDeleted agentUserId
where
hostEvent :: ChatResponse -> m ()

View File

@@ -49,6 +49,9 @@ data CallStateTag
| CSTCallNegotiated
deriving (Show, Generic)
instance FromJSON CallStateTag where
parseJSON = J.genericParseJSON . enumJSON $ dropPrefix "CSTCall"
instance ToJSON CallStateTag where
toJSON = J.genericToJSON . enumJSON $ dropPrefix "CSTCall"
toEncoding = J.genericToEncoding . enumJSON $ dropPrefix "CSTCall"
@@ -132,7 +135,7 @@ data RcvCallInvitation = RcvCallInvitation
sharedKey :: Maybe C.Key,
callTs :: UTCTime
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON RcvCallInvitation where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -157,10 +160,7 @@ data CallInvitation = CallInvitation
{ callType :: CallType,
callDhPubKey :: Maybe C.PublicKeyX25519
}
deriving (Eq, Show, Generic)
instance FromJSON CallInvitation where
parseJSON = J.genericParseJSON J.defaultOptions {J.omitNothingFields = True}
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON CallInvitation where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -190,10 +190,7 @@ data CallOffer = CallOffer
rtcSession :: WebRTCSession,
callDhPubKey :: Maybe C.PublicKeyX25519
}
deriving (Eq, Show, Generic)
instance FromJSON CallOffer where
parseJSON = J.genericParseJSON J.defaultOptions {J.omitNothingFields = True}
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON CallOffer where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -203,10 +200,7 @@ data WebRTCCallOffer = WebRTCCallOffer
{ callType :: CallType,
rtcSession :: WebRTCSession
}
deriving (Eq, Show, Generic)
instance FromJSON WebRTCCallOffer where
parseJSON = J.genericParseJSON J.defaultOptions {J.omitNothingFields = True}
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON WebRTCCallOffer where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}

View File

@@ -1,4 +1,5 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
@@ -12,6 +13,7 @@
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module Simplex.Chat.Controller where
@@ -24,11 +26,13 @@ import Control.Monad.Reader
import Crypto.Random (ChaChaDRG)
import Data.Aeson (FromJSON (..), ToJSON (..), (.:), (.:?))
import qualified Data.Aeson as J
import qualified Data.Aeson.TH as JQ
import qualified Data.Aeson.Types as JT
import qualified Data.Attoparsec.ByteString.Char8 as A
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.Char (ord)
import Data.Constraint (Dict (..))
import Data.Int (Int64)
import Data.List.NonEmpty (NonEmpty)
import Data.Map.Strict (Map)
@@ -64,7 +68,7 @@ import qualified Simplex.Messaging.Crypto.File as CF
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus)
import Simplex.Messaging.Parsers (dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON)
import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType, CorrId, MsgFlags, NtfServer, ProtoServerWithAuth, ProtocolTypeI, QueueId, SProtocolType, SubscriptionMode (..), UserProtocol, XFTPServerWithAuth)
import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType (..), CorrId, MsgFlags, NtfServer, ProtoServerWithAuth, ProtocolTypeI, QueueId, SProtocolType, SubscriptionMode (..), UserProtocol, XFTPServerWithAuth, userProtocol)
import Simplex.Messaging.TMap (TMap)
import Simplex.Messaging.Transport (simplexMQVersion)
import Simplex.Messaging.Transport.Client (TransportHost)
@@ -200,6 +204,9 @@ data ChatController = ChatController
data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIncognito | HSMarkdown | HSMessages | HSSettings | HSDatabase
deriving (Show, Generic)
instance FromJSON HelpSection where
parseJSON = J.genericParseJSON . enumJSON $ dropPrefix "HS"
instance ToJSON HelpSection where
toJSON = J.genericToJSON . enumJSON $ dropPrefix "HS"
toEncoding = J.genericToEncoding . enumJSON $ dropPrefix "HS"
@@ -438,10 +445,10 @@ data ChatCommand
data ChatResponse
= CRActiveUser {user :: User}
| CRUsersList {users :: [UserInfo]}
| CRChatStarted
| CRChatRunning
| CRChatStopped
| CRChatSuspended
| CRChatStarted {_nullary :: Maybe Int}
| CRChatRunning {_nullary :: Maybe Int}
| CRChatStopped {_nullary :: Maybe Int}
| CRChatSuspended {_nullary :: Maybe Int}
| CRApiChats {user :: User, chats :: [AChat]}
| CRChats {chats :: [AChat]}
| CRApiChat {user :: User, chat :: AChat}
@@ -605,7 +612,7 @@ data ChatResponse
| CRRemoteHostDeleted {remoteHostId :: RemoteHostId}
| CRRemoteCtrlList {remoteCtrls :: [RemoteCtrlInfo]}
| CRRemoteCtrlRegistered {remoteCtrlId :: RemoteCtrlId}
| CRRemoteCtrlStarted
| CRRemoteCtrlStarted {_nullary :: Maybe Int}
| CRRemoteCtrlAnnounce {fingerprint :: C.KeyHash} -- unregistered fingerprint, needs confirmation
| CRRemoteCtrlFound {remoteCtrl :: RemoteCtrl} -- registered fingerprint, may connect
| CRRemoteCtrlAccepted {remoteCtrlId :: RemoteCtrlId}
@@ -629,7 +636,7 @@ data ChatResponse
| CRChatError {user_ :: Maybe User, chatError :: ChatError}
| CRArchiveImported {archiveErrors :: [ArchiveError]}
| CRTimedAction {action :: String, durationMilliseconds :: Int64}
deriving (Show, Generic)
deriving (Show)
logResponseToFile :: ChatResponse -> Bool
logResponseToFile = \case
@@ -650,17 +657,12 @@ logResponseToFile = \case
CRMessageError {} -> True
_ -> False
instance FromJSON ChatResponse where
parseJSON todo = pure $ CRCmdOk Nothing -- TODO: actually use the instances
instance ToJSON ChatResponse where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "CR"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "CR"
data RemoteCtrlOOB = RemoteCtrlOOB
{ caFingerprint :: C.KeyHash
}
deriving (Show, Generic, ToJSON)
deriving (Show, Generic, FromJSON)
instance ToJSON RemoteCtrlOOB where toEncoding = J.genericToEncoding J.defaultOptions
data RemoteHostInfo = RemoteHostInfo
{ remoteHostId :: RemoteHostId,
@@ -668,14 +670,18 @@ data RemoteHostInfo = RemoteHostInfo
displayName :: Text,
sessionActive :: Bool
}
deriving (Show, Generic, ToJSON)
deriving (Show, Generic, FromJSON)
instance ToJSON RemoteHostInfo where toEncoding = J.genericToEncoding J.defaultOptions
data RemoteCtrlInfo = RemoteCtrlInfo
{ remoteCtrlId :: RemoteCtrlId,
displayName :: Text,
sessionActive :: Bool
}
deriving (Eq, Show, Generic, ToJSON)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON RemoteCtrlInfo where toEncoding = J.genericToEncoding J.defaultOptions
newtype UserPwd = UserPwd {unUserPwd :: Text}
deriving (Eq, Show)
@@ -695,6 +701,9 @@ instance StrEncoding AgentQueueId where
strDecode s = AgentQueueId <$> strDecode s
strP = AgentQueueId <$> strP
instance FromJSON AgentQueueId where
parseJSON = strParseJSON "AgentQueueId"
instance ToJSON AgentQueueId where
toJSON = strToJSON
toEncoding = strToJEncoding
@@ -713,12 +722,23 @@ data UserProtoServers p = UserProtoServers
}
deriving (Show, Generic)
instance ProtocolTypeI p => FromJSON (UserProtoServers p) where
parseJSON = J.genericParseJSON J.defaultOptions
instance ProtocolTypeI p => ToJSON (UserProtoServers p) where
toJSON = J.genericToJSON J.defaultOptions
toEncoding = J.genericToEncoding J.defaultOptions
data AUserProtoServers = forall p. (ProtocolTypeI p, UserProtocol p) => AUPS (UserProtoServers p)
instance FromJSON AUserProtoServers where
parseJSON v = J.withObject "AUserProtoServers" parse v
where
parse o = do
AProtocolType (p :: SProtocolType p) <- o .: "serverProtocol"
case userProtocol p of
Just Dict -> AUPS <$> J.parseJSON @(UserProtoServers p) v
Nothing -> fail $ "AUserProtoServers: unsupported protocol " <> show p
instance ToJSON AUserProtoServers where
toJSON (AUPS s) = J.genericToJSON J.defaultOptions s
toEncoding (AUPS s) = J.genericToEncoding J.defaultOptions s
@@ -747,7 +767,7 @@ data ContactSubStatus = ContactSubStatus
{ contact :: Contact,
contactError :: Maybe ChatError
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON ContactSubStatus where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -757,7 +777,7 @@ data MemberSubStatus = MemberSubStatus
{ member :: GroupMember,
memberError :: Maybe ChatError
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON MemberSubStatus where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -767,7 +787,7 @@ data UserContactSubStatus = UserContactSubStatus
{ userContact :: UserContact,
userContactError :: Maybe ChatError
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON UserContactSubStatus where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -777,7 +797,7 @@ data PendingSubStatus = PendingSubStatus
{ connection :: PendingContactConnection,
connError :: Maybe ChatError
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON PendingSubStatus where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -789,7 +809,7 @@ data UserProfileUpdateSummary = UserProfileUpdateSummary
updateFailures :: Int,
changedContacts :: [Contact]
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON UserProfileUpdateSummary where toEncoding = J.genericToEncoding J.defaultOptions
@@ -825,12 +845,10 @@ data XFTPFileConfig = XFTPFileConfig
defaultXFTPFileConfig :: XFTPFileConfig
defaultXFTPFileConfig = XFTPFileConfig {minFileSize = 0}
instance ToJSON XFTPFileConfig where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
instance ToJSON XFTPFileConfig where toEncoding = J.genericToEncoding J.defaultOptions
data NtfMsgInfo = NtfMsgInfo {msgTs :: UTCTime, msgFlags :: MsgFlags}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON NtfMsgInfo where toEncoding = J.genericToEncoding J.defaultOptions
@@ -842,7 +860,7 @@ data SwitchProgress = SwitchProgress
switchPhase :: SwitchPhase,
connectionStats :: ConnectionStats
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON SwitchProgress where toEncoding = J.genericToEncoding J.defaultOptions
@@ -850,7 +868,7 @@ data RatchetSyncProgress = RatchetSyncProgress
{ ratchetSyncStatus :: RatchetSyncState,
connectionStats :: ConnectionStats
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON RatchetSyncProgress where toEncoding = J.genericToEncoding J.defaultOptions
@@ -858,7 +876,7 @@ data ParsedServerAddress = ParsedServerAddress
{ serverAddress :: Maybe ServerAddress,
parseError :: String
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON ParsedServerAddress where toEncoding = J.genericToEncoding J.defaultOptions
@@ -869,7 +887,7 @@ data ServerAddress = ServerAddress
keyHash :: String,
basicAuth :: String
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON ServerAddress where toEncoding = J.genericToEncoding J.defaultOptions
@@ -893,7 +911,7 @@ data CoreVersionInfo = CoreVersionInfo
simplexmqVersion :: String,
simplexmqCommit :: String
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON CoreVersionInfo where toEncoding = J.genericToEncoding J.defaultOptions
@@ -906,7 +924,7 @@ data SlowSQLQuery = SlowSQLQuery
{ query :: Text,
queryStats :: SlowQueryStats
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON SlowSQLQuery where toEncoding = J.genericToEncoding J.defaultOptions
@@ -919,6 +937,9 @@ data ChatError
| ChatErrorRemoteHost {remoteHostId :: RemoteHostId, remoteHostError :: RemoteHostError}
deriving (Show, Exception, Generic)
instance FromJSON ChatError where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "Chat"
instance ToJSON ChatError where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "Chat"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "Chat"
@@ -1002,6 +1023,9 @@ data ChatErrorType
| CEException {message :: String}
deriving (Show, Exception, Generic)
instance FromJSON ChatErrorType where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "CE"
instance ToJSON ChatErrorType where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "CE"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "CE"
@@ -1014,6 +1038,9 @@ data DatabaseError
| DBErrorOpen {sqliteError :: SQLiteError}
deriving (Show, Exception, Generic)
instance FromJSON DatabaseError where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "DB"
instance ToJSON DatabaseError where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "DB"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "DB"
@@ -1021,6 +1048,9 @@ instance ToJSON DatabaseError where
data SQLiteError = SQLiteErrorNotADatabase | SQLiteError String
deriving (Show, Exception, Generic)
instance FromJSON SQLiteError where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "SQLite"
instance ToJSON SQLiteError where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "SQLite"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "SQLite"
@@ -1070,6 +1100,9 @@ data ArchiveError
| AEImportFile {file :: String, chatError :: ChatError}
deriving (Show, Exception, Generic)
instance FromJSON ArchiveError where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "AE"
instance ToJSON ArchiveError where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "AE"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "AE"
@@ -1156,3 +1189,5 @@ withStoreCtx ctx_ action = do
where
handleInternal :: String -> SomeException -> IO (Either StoreError a)
handleInternal ctxStr e = pure . Left . SEInternalError $ show e <> ctxStr
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CR") ''ChatResponse)

View File

@@ -1,3 +1,4 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
@@ -10,7 +11,7 @@
module Simplex.Chat.Markdown where
import Control.Applicative (optional, (<|>))
import Data.Aeson (ToJSON)
import Data.Aeson (FromJSON, ToJSON)
import qualified Data.Aeson as J
import Data.Attoparsec.Text (Parser)
import qualified Data.Attoparsec.Text as A
@@ -56,6 +57,9 @@ data Format
data SimplexLinkType = XLContact | XLInvitation | XLGroup
deriving (Eq, Show, Generic)
instance FromJSON SimplexLinkType where
parseJSON = J.genericParseJSON . enumJSON $ dropPrefix "XL"
instance ToJSON SimplexLinkType where
toJSON = J.genericToJSON . enumJSON $ dropPrefix "XL"
toEncoding = J.genericToEncoding . enumJSON $ dropPrefix "XL"
@@ -66,6 +70,9 @@ colored = Colored . FormatColor
markdown :: Format -> Text -> Markdown
markdown = Markdown . Just
instance FromJSON Format where
parseJSON = J.genericParseJSON $ sumTypeJSON fstToLower
instance ToJSON Format where
toJSON = J.genericToJSON $ sumTypeJSON fstToLower
toEncoding = J.genericToEncoding $ sumTypeJSON fstToLower
@@ -91,6 +98,18 @@ instance IsString Markdown where fromString = unmarked . T.pack
newtype FormatColor = FormatColor Color
deriving (Eq, Show)
instance FromJSON FormatColor where
parseJSON = J.withText "FormatColor" $ fmap FormatColor . \case
"red" -> pure Red
"green" -> pure Green
"blue" -> pure Blue
"yellow" -> pure Yellow
"cyan" -> pure Cyan
"magenta" -> pure Magenta
"black" -> pure Black
"white" -> pure White
unexpected -> fail $ "unexpected FormatColor: " <> show unexpected
instance ToJSON FormatColor where
toJSON (FormatColor c) = case c of
Red -> "red"
@@ -103,7 +122,7 @@ instance ToJSON FormatColor where
White -> "white"
data FormattedText = FormattedText {format :: Maybe Format, text :: Text}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON FormattedText where
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
@@ -129,7 +148,7 @@ parseMaybeMarkdownList s
| otherwise = Just . reverse $ foldl' acc [] ml
where
ml = intercalate ["\n"] . map (markdownToList . parseMarkdown) $ T.lines s
acc [] m = [m]
acc [] m = [m]
acc ms@(FormattedText f t : ms') ft@(FormattedText f' t')
| f == f' = FormattedText f (t <> t') : ms'
| otherwise = ft : ms

View File

@@ -17,7 +17,7 @@
module Simplex.Chat.Messages where
import Control.Applicative ((<|>))
import Data.Aeson (FromJSON, ToJSON)
import Data.Aeson (FromJSON, ToJSON, (.:))
import qualified Data.Aeson as J
import qualified Data.Aeson.Encoding as JE
import qualified Data.Attoparsec.ByteString.Char8 as A
@@ -66,6 +66,9 @@ chatNameStr (ChatName cType name) = chatTypeStr cType <> T.unpack name
data ChatRef = ChatRef ChatType Int64
deriving (Eq, Show, Ord)
instance FromJSON ChatType where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "CT"
instance ToJSON ChatType where
toJSON = J.genericToJSON . enumJSON $ dropPrefix "CT"
toEncoding = J.genericToEncoding . enumJSON $ dropPrefix "CT"
@@ -110,10 +113,16 @@ data JSONChatInfo
| JCInfoContactConnection {contactConnection :: PendingContactConnection}
deriving (Generic)
instance FromJSON JSONChatInfo where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "JCInfo"
instance ToJSON JSONChatInfo where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "JCInfo"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "JCInfo"
instance ChatTypeI c => FromJSON (ChatInfo c) where
parseJSON v = (\(AChatInfo _ c) -> checkChatType c) <$?> J.parseJSON v
instance ToJSON (ChatInfo c) where
toJSON = J.toJSON . jsonChatInfo
toEncoding = J.toEncoding . jsonChatInfo
@@ -125,10 +134,20 @@ jsonChatInfo = \case
ContactRequest g -> JCInfoContactRequest g
ContactConnection c -> JCInfoContactConnection c
data AChatInfo = forall c. AChatInfo (SChatType c) (ChatInfo c)
data AChatInfo = forall c. ChatTypeI c => AChatInfo (SChatType c) (ChatInfo c)
deriving instance Show AChatInfo
jsonAChatInfo :: JSONChatInfo -> AChatInfo
jsonAChatInfo = \case
JCInfoDirect c -> AChatInfo SCTDirect $ DirectChat c
JCInfoGroup g -> AChatInfo SCTGroup $ GroupChat g
JCInfoContactRequest g -> AChatInfo SCTContactRequest $ ContactRequest g
JCInfoContactConnection c -> AChatInfo SCTContactConnection $ ContactConnection c
instance FromJSON AChatInfo where
parseJSON v = jsonAChatInfo <$> J.parseJSON v
instance ToJSON AChatInfo where
toJSON (AChatInfo _ c) = J.toJSON c
toEncoding (AChatInfo _ c) = J.toEncoding c
@@ -144,7 +163,10 @@ data ChatItem (c :: ChatType) (d :: MsgDirection) = ChatItem
}
deriving (Show, Generic)
instance MsgDirectionI d => ToJSON (ChatItem c d) where
instance (ChatTypeI c, MsgDirectionI d) => FromJSON (ChatItem c d) where
parseJSON = J.genericParseJSON J.defaultOptions
instance (ChatTypeI c, MsgDirectionI d) => ToJSON (ChatItem c d) where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
@@ -156,6 +178,16 @@ data CIDirection (c :: ChatType) (d :: MsgDirection) where
deriving instance Show (CIDirection c d)
data CCIDirection c = forall d. MsgDirectionI d => CCID (SMsgDirection d) (CIDirection c d)
instance ChatTypeI c => FromJSON (CCIDirection c) where
parseJSON v = (\(ACID _ d x) -> checkChatType (CCID d x)) <$?> J.parseJSON v
data ACIDirection = forall c d. (ChatTypeI c, MsgDirectionI d) => ACID (SChatType c) (SMsgDirection d) (CIDirection c d)
instance FromJSON ACIDirection where
parseJSON v = jsonACIDirection <$> J.parseJSON v
data JSONCIDirection
= JCIDirectSnd
| JCIDirectRcv
@@ -163,10 +195,16 @@ data JSONCIDirection
| JCIGroupRcv {groupMember :: GroupMember}
deriving (Generic, Show)
instance FromJSON JSONCIDirection where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "JCI"
instance ToJSON JSONCIDirection where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "JCI"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "JCI"
instance (ChatTypeI c, MsgDirectionI d) => FromJSON (CIDirection c d) where
parseJSON v = (\(CCID _ x') -> checkDirection x') <$?> J.parseJSON v
instance ToJSON (CIDirection c d) where
toJSON = J.toJSON . jsonCIDirection
toEncoding = J.toEncoding . jsonCIDirection
@@ -178,8 +216,15 @@ jsonCIDirection = \case
CIGroupSnd -> JCIGroupSnd
CIGroupRcv m -> JCIGroupRcv m
jsonACIDirection :: JSONCIDirection -> ACIDirection
jsonACIDirection = \case
JCIDirectSnd -> ACID SCTDirect SMDSnd CIDirectSnd
JCIDirectRcv -> ACID SCTDirect SMDRcv CIDirectRcv
JCIGroupSnd -> ACID SCTGroup SMDSnd CIGroupSnd
JCIGroupRcv m -> ACID SCTGroup SMDRcv $ CIGroupRcv m
data CIReactionCount = CIReactionCount {reaction :: MsgReaction, userReacted :: Bool, totalReacted :: Int}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON CIReactionCount where toEncoding = J.genericToEncoding J.defaultOptions
@@ -187,7 +232,15 @@ data CChatItem c = forall d. MsgDirectionI d => CChatItem (SMsgDirection d) (Cha
deriving instance Show (CChatItem c)
instance ToJSON (CChatItem c) where
instance forall c. ChatTypeI c => FromJSON (CChatItem c) where
parseJSON v = J.withObject "CChatItem" parse v
where
parse o = do
CCID d (_ :: CIDirection c d) <- o .: "chatDir"
ci <- J.parseJSON @(ChatItem c d) v
pure $ CChatItem d ci
instance ChatTypeI c => ToJSON (CChatItem c) where
toJSON (CChatItem _ ci) = J.toJSON ci
toEncoding (CChatItem _ ci) = J.toEncoding ci
@@ -279,14 +332,19 @@ data Chat c = Chat
}
deriving (Show, Generic)
instance ToJSON (Chat c) where
toJSON = J.genericToJSON J.defaultOptions
toEncoding = J.genericToEncoding J.defaultOptions
instance ChatTypeI c => ToJSON (Chat c) where toEncoding = J.genericToEncoding J.defaultOptions
data AChat = forall c. AChat (SChatType c) (Chat c)
data AChat = forall c. ChatTypeI c => AChat (SChatType c) (Chat c)
deriving instance Show AChat
instance FromJSON AChat where
parseJSON = J.withObject "AChat" $ \o -> do
AChatInfo c chatInfo <- o .: "chatInfo"
chatItems <- o .: "chatItems"
chatStats <- o .: "chatStats"
pure $ AChat c Chat {chatInfo, chatItems, chatStats}
instance ToJSON AChat where
toJSON (AChat _ c) = J.toJSON c
toEncoding (AChat _ c) = J.toEncoding c
@@ -296,17 +354,21 @@ data ChatStats = ChatStats
minUnreadItemId :: ChatItemId,
unreadChat :: Bool
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON ChatStats where
toJSON = J.genericToJSON J.defaultOptions
toEncoding = J.genericToEncoding J.defaultOptions
instance ToJSON ChatStats where toEncoding = J.genericToEncoding J.defaultOptions
-- | type to show a mix of messages from multiple chats
data AChatItem = forall c d. MsgDirectionI d => AChatItem (SChatType c) (SMsgDirection d) (ChatInfo c) (ChatItem c d)
data AChatItem = forall c d. (ChatTypeI c, MsgDirectionI d) => AChatItem (SChatType c) (SMsgDirection d) (ChatInfo c) (ChatItem c d)
deriving instance Show AChatItem
instance FromJSON AChatItem where
parseJSON = J.withObject "AChatItem" $ \o -> do
AChatInfo c chatInfo <- o .: "chatInfo"
CChatItem d chatItem <- o .: "chatItem"
pure $ AChatItem c d chatInfo chatItem
instance ToJSON AChatItem where
toJSON (AChatItem _ _ chat item) = J.toJSON $ JSONAnyChatItem chat item
toEncoding (AChatItem _ _ chat item) = J.toEncoding $ JSONAnyChatItem chat item
@@ -330,7 +392,7 @@ updateFileStatus ci@ChatItem {file} status = case file of
Just f -> ci {file = Just (f :: CIFile d) {fileStatus = status}}
Nothing -> ci
instance MsgDirectionI d => ToJSON (JSONAnyChatItem c d) where
instance (ChatTypeI c, MsgDirectionI d) => ToJSON (JSONAnyChatItem c d) where
toJSON = J.genericToJSON J.defaultOptions
toEncoding = J.genericToEncoding J.defaultOptions
@@ -349,7 +411,7 @@ data CIMeta (c :: ChatType) (d :: MsgDirection) = CIMeta
createdAt :: UTCTime,
updatedAt :: UTCTime
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
mkCIMeta :: ChatItemId -> CIContent d -> Text -> CIStatus d -> Maybe SharedMsgId -> Maybe (CIDeleted c) -> Bool -> Maybe CITimed -> Maybe Bool -> UTCTime -> ChatItemTs -> UTCTime -> UTCTime -> CIMeta c d
mkCIMeta itemId itemContent itemText itemStatus itemSharedMsgId itemDeleted itemEdited itemTimed itemLive currentTs itemTs createdAt updatedAt =
@@ -358,13 +420,13 @@ mkCIMeta itemId itemContent itemText itemStatus itemSharedMsgId itemDeleted item
_ -> False
in CIMeta {itemId, itemTs, itemText, itemStatus, itemSharedMsgId, itemDeleted, itemEdited, itemTimed, itemLive, editable, createdAt, updatedAt}
instance ToJSON (CIMeta c d) where toEncoding = J.genericToEncoding J.defaultOptions
instance ChatTypeI c => ToJSON (CIMeta c d) where toEncoding = J.genericToEncoding J.defaultOptions
data CITimed = CITimed
{ ttl :: Int, -- seconds
deleteAt :: Maybe UTCTime -- this is initially Nothing for received items, the timer starts when they are read
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON CITimed where toEncoding = J.genericToEncoding J.defaultOptions
@@ -402,6 +464,9 @@ data CIQuote (c :: ChatType) = CIQuote
}
deriving (Show, Generic)
instance ChatTypeI c => FromJSON (CIQuote c) where
parseJSON = J.genericParseJSON J.defaultOptions
instance ToJSON (CIQuote c) where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
@@ -414,24 +479,39 @@ data CIReaction (c :: ChatType) (d :: MsgDirection) = CIReaction
}
deriving (Show, Generic)
instance ToJSON (CIReaction c d) where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
instance (ChatTypeI c, MsgDirectionI d) => FromJSON (CIReaction c d) where
parseJSON = J.genericParseJSON J.defaultOptions
data ACIReaction = forall c d. ACIReaction (SChatType c) (SMsgDirection d) (ChatInfo c) (CIReaction c d)
instance ChatTypeI c => ToJSON (CIReaction c d) where
toEncoding = J.genericToEncoding J.defaultOptions
data AnyCIReaction = forall c d. ChatTypeI c => ACIR (SChatType c) (SMsgDirection d) (CIReaction c d)
instance FromJSON AnyCIReaction where
parseJSON v = J.withObject "AnyCIReaction" parse v
where
parse o = do
ACID c d (_ :: CIDirection c d) <- o .: "chatDir"
ACIR c d <$> J.parseJSON @(CIReaction c d) v
data ACIReaction = forall c d. ChatTypeI c => ACIReaction (SChatType c) (SMsgDirection d) (ChatInfo c) (CIReaction c d)
deriving instance Show ACIReaction
instance FromJSON ACIReaction where
parseJSON = J.withObject "ACIReaction" $ \o -> do
ACIR c d reaction <- o .: "chatReaction"
cInfo <- o .: "chatInfo"
pure $ ACIReaction c d cInfo reaction
instance ToJSON ACIReaction where
toJSON (ACIReaction _ _ chat reaction) = J.toJSON $ JSONCIReaction chat reaction
toEncoding (ACIReaction _ _ chat reaction) = J.toEncoding $ JSONCIReaction chat reaction
toJSON (ACIReaction _ _ cInfo reaction) = J.toJSON $ JSONCIReaction cInfo reaction
toEncoding (ACIReaction _ _ cInfo reaction) = J.toEncoding $ JSONCIReaction cInfo reaction
data JSONCIReaction c d = JSONCIReaction {chatInfo :: ChatInfo c, chatReaction :: CIReaction c d}
deriving (Generic)
instance ToJSON (JSONCIReaction c d) where
toJSON = J.genericToJSON J.defaultOptions
toEncoding = J.genericToEncoding J.defaultOptions
instance ChatTypeI c => ToJSON (JSONCIReaction c d) where toEncoding = J.genericToEncoding J.defaultOptions
data CIQDirection (c :: ChatType) where
CIQDirectSnd :: CIQDirection 'CTDirect
@@ -441,6 +521,11 @@ data CIQDirection (c :: ChatType) where
deriving instance Show (CIQDirection c)
data ACIQDirection = forall c. ChatTypeI c => ACIQDirection (SChatType c) (CIQDirection c)
instance ChatTypeI c => FromJSON (CIQDirection c) where
parseJSON v = (\(ACIQDirection _ x) -> checkChatType x) . jsonACIQDirection <$?> J.parseJSON v
instance ToJSON (CIQDirection c) where
toJSON = J.toJSON . jsonCIQDirection
toEncoding = J.toEncoding . jsonCIQDirection
@@ -453,6 +538,14 @@ jsonCIQDirection = \case
CIQGroupRcv (Just m) -> Just $ JCIGroupRcv m
CIQGroupRcv Nothing -> Nothing
jsonACIQDirection :: Maybe JSONCIDirection -> ACIQDirection
jsonACIQDirection = \case
Just JCIDirectSnd -> ACIQDirection SCTDirect CIQDirectSnd
Just JCIDirectRcv -> ACIQDirection SCTDirect CIQDirectRcv
Just JCIGroupSnd -> ACIQDirection SCTGroup CIQGroupSnd
Just (JCIGroupRcv m) -> ACIQDirection SCTGroup $ CIQGroupRcv (Just m)
Nothing -> ACIQDirection SCTGroup $ CIQGroupRcv Nothing
quoteMsgDirection :: CIQDirection c -> MsgDirection
quoteMsgDirection = \case
CIQDirectSnd -> MDSnd
@@ -470,6 +563,9 @@ data CIFile (d :: MsgDirection) = CIFile
}
deriving (Show, Generic)
instance MsgDirectionI d => FromJSON (CIFile d) where
parseJSON = J.genericParseJSON J.defaultOptions
instance MsgDirectionI d => ToJSON (CIFile d) where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
@@ -481,6 +577,9 @@ instance FromField FileProtocol where fromField = fromTextField_ textDecode
instance ToField FileProtocol where toField = toField . textEncode
instance FromJSON FileProtocol where
parseJSON = textParseJSON "FileProtocol"
instance ToJSON FileProtocol where
toJSON = J.String . textEncode
toEncoding = JE.text . textEncode
@@ -527,6 +626,9 @@ ciFileEnded = \case
CIFSRcvError -> True
CIFSInvalid {} -> True
instance MsgDirectionI d => FromJSON (CIFileStatus d) where
parseJSON v = (\(AFS _ s) -> checkDirection s) . aciFileStatusJSON <$?> J.parseJSON v
instance ToJSON (CIFileStatus d) where
toJSON = J.toJSON . jsonCIFileStatus
toEncoding = J.toEncoding . jsonCIFileStatus
@@ -594,6 +696,9 @@ data JSONCIFileStatus
| JCIFSInvalid {text :: Text}
deriving (Generic)
instance FromJSON JSONCIFileStatus where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "JCIFS"
instance ToJSON JSONCIFileStatus where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "JCIFS"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "JCIFS"
@@ -658,6 +763,9 @@ deriving instance Eq (CIStatus d)
deriving instance Show (CIStatus d)
instance MsgDirectionI d => FromJSON (CIStatus d) where
parseJSON v = (\(ACIStatus _ s) -> checkDirection s) . jsonACIStatus <$?> J.parseJSON v
instance ToJSON (CIStatus d) where
toJSON = J.toJSON . jsonCIStatus
toEncoding = J.toEncoding . jsonCIStatus
@@ -712,6 +820,9 @@ data JSONCIStatus
| JCISInvalid {text :: Text}
deriving (Show, Generic)
instance FromJSON JSONCIStatus where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "JCIS"
instance ToJSON JSONCIStatus where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "JCIS"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "JCIS"
@@ -727,6 +838,17 @@ jsonCIStatus = \case
CISRcvRead -> JCISRcvRead
CISInvalid text -> JCISInvalid text
jsonACIStatus :: JSONCIStatus -> ACIStatus
jsonACIStatus = \case
JCISSndNew -> ACIStatus SMDSnd CISSndNew
JCISSndSent sndProgress -> ACIStatus SMDSnd $ CISSndSent sndProgress
JCISSndRcvd msgRcptStatus sndProgress -> ACIStatus SMDSnd $ CISSndRcvd msgRcptStatus sndProgress
JCISSndErrorAuth -> ACIStatus SMDSnd CISSndErrorAuth
JCISSndError e -> ACIStatus SMDSnd $ CISSndError e
JCISRcvNew -> ACIStatus SMDRcv CISRcvNew
JCISRcvRead -> ACIStatus SMDRcv CISRcvRead
JCISInvalid text -> ACIStatus SMDSnd $ CISInvalid text
ciStatusNew :: forall d. MsgDirectionI d => CIStatus d
ciStatusNew = case msgDirection @d of
SMDSnd -> CISSndNew
@@ -757,6 +879,9 @@ data SndCIStatusProgress
| SSPComplete
deriving (Eq, Show, Generic)
instance FromJSON SndCIStatusProgress where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "SSP"
instance ToJSON SndCIStatusProgress where
toJSON = J.genericToJSON . enumJSON $ dropPrefix "SSP"
toEncoding = J.genericToEncoding . enumJSON $ dropPrefix "SSP"
@@ -796,6 +921,8 @@ instance TestEquality SChatType where
testEquality SCTContactConnection SCTContactConnection = Just Refl
testEquality _ _ = Nothing
data AChatType = forall c. ChatTypeI c => ACT (SChatType c)
class ChatTypeI (c :: ChatType) where
chatTypeI :: SChatType c
@@ -803,6 +930,36 @@ instance ChatTypeI 'CTDirect where chatTypeI = SCTDirect
instance ChatTypeI 'CTGroup where chatTypeI = SCTGroup
instance ChatTypeI 'CTContactRequest where chatTypeI = SCTContactRequest
instance ChatTypeI 'CTContactConnection where chatTypeI = SCTContactConnection
instance ChatTypeI c => FromJSON (SChatType c) where
parseJSON v = (\(ACT t) -> checkChatType t) . aChatType <$?> J.parseJSON v
instance ToJSON (SChatType c) where
toJSON = J.toJSON . toChatType
toEncoding = J.toEncoding . toChatType
toChatType :: SChatType c -> ChatType
toChatType = \case
SCTDirect -> CTDirect
SCTGroup -> CTGroup
SCTContactRequest -> CTContactRequest
SCTContactConnection -> CTContactConnection
aChatType :: ChatType -> AChatType
aChatType = \case
CTDirect -> ACT SCTDirect
CTGroup -> ACT SCTGroup
CTContactRequest -> ACT SCTContactRequest
CTContactConnection -> ACT SCTContactConnection
checkChatType :: forall t c c'. (ChatTypeI c, ChatTypeI c') => t c' -> Either String (t c)
checkChatType x = case testEquality (chatTypeI @c) (chatTypeI @c') of
Just Refl -> Right x
Nothing -> Left "bad chat type"
data NewMessage e = NewMessage
{ chatMsgEvent :: ChatMsgEvent e,
msgBody :: MsgBody
@@ -920,35 +1077,43 @@ msgDeliveryStatusT' s =
Just Refl -> Just st
_ -> Nothing
checkDirection :: forall t d d'. (MsgDirectionI d, MsgDirectionI d') => t d' -> Either String (t d)
checkDirection x = case testEquality (msgDirection @d) (msgDirection @d') of
Just Refl -> Right x
Nothing -> Left "bad direction"
data CIDeleted (c :: ChatType) where
CIDeleted :: Maybe UTCTime -> CIDeleted c
CIModerated :: Maybe UTCTime -> GroupMember -> CIDeleted 'CTGroup
deriving instance Show (CIDeleted c)
instance ToJSON (CIDeleted d) where
data ACIDeleted = forall c. ChatTypeI c => ACIDeleted (SChatType c) (CIDeleted c)
instance ChatTypeI c => FromJSON (CIDeleted c) where
parseJSON v = (\(ACIDeleted _ x) -> checkChatType x) . jsonACIDeleted <$?> J.parseJSON v
instance ChatTypeI c => ToJSON (CIDeleted c) where
toJSON = J.toJSON . jsonCIDeleted
toEncoding = J.toEncoding . jsonCIDeleted
data JSONCIDeleted
= JCIDDeleted {deletedTs :: Maybe UTCTime}
= JCIDDeleted {deletedTs :: Maybe UTCTime, chatType :: ChatType}
| JCIDModerated {deletedTs :: Maybe UTCTime, byGroupMember :: GroupMember}
deriving (Show, Generic)
instance FromJSON JSONCIDeleted where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "JCID"
instance ToJSON JSONCIDeleted where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "JCID"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "JCID"
jsonCIDeleted :: CIDeleted d -> JSONCIDeleted
jsonCIDeleted :: forall d. ChatTypeI d => CIDeleted d -> JSONCIDeleted
jsonCIDeleted = \case
CIDeleted ts -> JCIDDeleted ts
CIDeleted ts -> JCIDDeleted ts (toChatType $ chatTypeI @d)
CIModerated ts m -> JCIDModerated ts m
jsonACIDeleted :: JSONCIDeleted -> ACIDeleted
jsonACIDeleted = \case
JCIDDeleted ts cType -> case aChatType cType of ACT c -> ACIDeleted c $ CIDeleted ts
JCIDModerated ts m -> ACIDeleted SCTGroup (CIModerated ts m)
itemDeletedTs :: CIDeleted d -> Maybe UTCTime
itemDeletedTs = \case
CIDeleted ts -> ts
@@ -958,7 +1123,7 @@ data ChatItemInfo = ChatItemInfo
{ itemVersions :: [ChatItemVersion],
memberDeliveryStatuses :: Maybe [MemberDeliveryStatus]
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON ChatItemInfo where toEncoding = J.genericToEncoding J.defaultOptions
@@ -969,7 +1134,7 @@ data ChatItemVersion = ChatItemVersion
itemVersionTs :: UTCTime,
createdAt :: UTCTime
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON ChatItemVersion where toEncoding = J.genericToEncoding J.defaultOptions
@@ -990,7 +1155,7 @@ data MemberDeliveryStatus = MemberDeliveryStatus
{ groupMemberId :: GroupMemberId,
memberDeliveryStatus :: CIStatus 'MDSnd
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON MemberDeliveryStatus where toEncoding = J.genericToEncoding J.defaultOptions

View File

@@ -9,12 +9,14 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module Simplex.Chat.Messages.CIContent where
import Data.Aeson (FromJSON, ToJSON)
import qualified Data.Aeson as J
import qualified Data.Aeson.TH as JQ
import Data.Int (Int64)
import Data.Text (Text)
import Data.Text.Encoding (decodeLatin1, encodeUtf8)
@@ -34,7 +36,7 @@ import Simplex.Chat.Types.Util
import Simplex.Messaging.Agent.Protocol (MsgErrorType (..), RatchetSyncState (..), SwitchPhase (..))
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (dropPrefix, enumJSON, fstToLower, singleFieldJSON, sumTypeJSON)
import Simplex.Messaging.Util (safeDecodeUtf8, tshow)
import Simplex.Messaging.Util (safeDecodeUtf8, tshow, (<$?>))
data MsgDirection = MDRcv | MDSnd
deriving (Eq, Show, Generic)
@@ -69,6 +71,13 @@ instance TestEquality SMsgDirection where
testEquality SMDSnd SMDSnd = Just Refl
testEquality _ _ = Nothing
instance MsgDirectionI d => FromJSON (SMsgDirection d) where
parseJSON v = (\(AMsgDirection d) -> checkDirection d) . fromMsgDirection <$?> J.parseJSON v
instance ToJSON (SMsgDirection d) where
toJSON = J.toJSON . toMsgDirection
toEncoding = J.toEncoding . toMsgDirection
instance ToField (SMsgDirection d) where toField = toField . msgDirectionInt . toMsgDirection
data AMsgDirection = forall d. MsgDirectionI d => AMsgDirection (SMsgDirection d)
@@ -92,6 +101,11 @@ instance MsgDirectionI 'MDRcv where msgDirection = SMDRcv
instance MsgDirectionI 'MDSnd where msgDirection = SMDSnd
checkDirection :: forall t d d'. (MsgDirectionI d, MsgDirectionI d') => t d' -> Either String (t d)
checkDirection x = case testEquality (msgDirection @d) (msgDirection @d') of
Just Refl -> Right x
Nothing -> Left "bad direction"
msgDirectionInt :: MsgDirection -> Int
msgDirectionInt = \case
MDRcv -> 0
@@ -481,27 +495,10 @@ msgDirToModeratedContent_ = \case
ciModeratedText :: Text
ciModeratedText = "moderated"
-- platform independent
instance MsgDirectionI d => ToField (CIContent d) where
toField = toField . encodeJSON . dbJsonCIContent
-- platform specific
instance MsgDirectionI d => ToJSON (CIContent d) where
toJSON = J.toJSON . jsonCIContent
toEncoding = J.toEncoding . jsonCIContent
data ACIContent = forall d. MsgDirectionI d => ACIContent (SMsgDirection d) (CIContent d)
deriving instance Show ACIContent
-- platform independent
dbParseACIContent :: Text -> Either String ACIContent
dbParseACIContent = fmap aciContentDBJSON . J.eitherDecodeStrict' . encodeUtf8
-- platform specific
instance FromJSON ACIContent where
parseJSON = fmap aciContentJSON . J.parseJSON
-- platform specific
data JSONCIContent
= JCISndMsgContent {msgContent :: MsgContent}
@@ -527,17 +524,9 @@ data JSONCIContent
| JCISndGroupFeature {groupFeature :: GroupFeature, preference :: GroupPreference, param :: Maybe Int}
| JCIRcvChatFeatureRejected {feature :: ChatFeature}
| JCIRcvGroupFeatureRejected {groupFeature :: GroupFeature}
| JCISndModerated
| JCIRcvModerated
| JCISndModerated {_nullary :: Maybe Int}
| JCIRcvModerated {_nullary :: Maybe Int}
| JCIInvalidJSON {direction :: MsgDirection, json :: Text}
deriving (Generic)
instance FromJSON JSONCIContent where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "JCI"
instance ToJSON JSONCIContent where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "JCI"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "JCI"
jsonCIContent :: forall d. MsgDirectionI d => CIContent d -> JSONCIContent
jsonCIContent = \case
@@ -564,8 +553,8 @@ jsonCIContent = \case
CISndGroupFeature groupFeature preference param -> JCISndGroupFeature {groupFeature, preference, param}
CIRcvChatFeatureRejected feature -> JCIRcvChatFeatureRejected {feature}
CIRcvGroupFeatureRejected groupFeature -> JCIRcvGroupFeatureRejected {groupFeature}
CISndModerated -> JCISndModerated
CIRcvModerated -> JCISndModerated
CISndModerated -> JCISndModerated Nothing
CIRcvModerated -> JCISndModerated Nothing
CIInvalidJSON json -> JCIInvalidJSON (toMsgDirection $ msgDirection @d) json
aciContentJSON :: JSONCIContent -> ACIContent
@@ -593,8 +582,8 @@ aciContentJSON = \case
JCISndGroupFeature {groupFeature, preference, param} -> ACIContent SMDSnd $ CISndGroupFeature groupFeature preference param
JCIRcvChatFeatureRejected {feature} -> ACIContent SMDRcv $ CIRcvChatFeatureRejected feature
JCIRcvGroupFeatureRejected {groupFeature} -> ACIContent SMDRcv $ CIRcvGroupFeatureRejected groupFeature
JCISndModerated -> ACIContent SMDSnd CISndModerated
JCIRcvModerated -> ACIContent SMDRcv CIRcvModerated
JCISndModerated _ -> ACIContent SMDSnd CISndModerated
JCIRcvModerated _ -> ACIContent SMDRcv CIRcvModerated
JCIInvalidJSON dir json -> case fromMsgDirection dir of
AMsgDirection d -> ACIContent d $ CIInvalidJSON json
@@ -623,17 +612,9 @@ data DBJSONCIContent
| DBJCISndGroupFeature {groupFeature :: GroupFeature, preference :: GroupPreference, param :: Maybe Int}
| DBJCIRcvChatFeatureRejected {feature :: ChatFeature}
| DBJCIRcvGroupFeatureRejected {groupFeature :: GroupFeature}
| DBJCISndModerated
| DBJCIRcvModerated
| DBJCISndModerated {_nullary :: Maybe Int}
| DBJCIRcvModerated {_nullary :: Maybe Int}
| DBJCIInvalidJSON {direction :: MsgDirection, json :: Text}
deriving (Generic)
instance FromJSON DBJSONCIContent where
parseJSON = J.genericParseJSON . singleFieldJSON $ dropPrefix "DBJCI"
instance ToJSON DBJSONCIContent where
toJSON = J.genericToJSON . singleFieldJSON $ dropPrefix "DBJCI"
toEncoding = J.genericToEncoding . singleFieldJSON $ dropPrefix "DBJCI"
dbJsonCIContent :: forall d. MsgDirectionI d => CIContent d -> DBJSONCIContent
dbJsonCIContent = \case
@@ -660,8 +641,8 @@ dbJsonCIContent = \case
CISndGroupFeature groupFeature preference param -> DBJCISndGroupFeature {groupFeature, preference, param}
CIRcvChatFeatureRejected feature -> DBJCIRcvChatFeatureRejected {feature}
CIRcvGroupFeatureRejected groupFeature -> DBJCIRcvGroupFeatureRejected {groupFeature}
CISndModerated -> DBJCISndModerated
CIRcvModerated -> DBJCIRcvModerated
CISndModerated -> DBJCISndModerated Nothing
CIRcvModerated -> DBJCIRcvModerated Nothing
CIInvalidJSON json -> DBJCIInvalidJSON (toMsgDirection $ msgDirection @d) json
aciContentDBJSON :: DBJSONCIContent -> ACIContent
@@ -689,8 +670,8 @@ aciContentDBJSON = \case
DBJCISndGroupFeature {groupFeature, preference, param} -> ACIContent SMDSnd $ CISndGroupFeature groupFeature preference param
DBJCIRcvChatFeatureRejected {feature} -> ACIContent SMDRcv $ CIRcvChatFeatureRejected feature
DBJCIRcvGroupFeatureRejected {groupFeature} -> ACIContent SMDRcv $ CIRcvGroupFeatureRejected groupFeature
DBJCISndModerated -> ACIContent SMDSnd CISndModerated
DBJCIRcvModerated -> ACIContent SMDRcv CIRcvModerated
DBJCISndModerated _ -> ACIContent SMDSnd CISndModerated
DBJCIRcvModerated _ -> ACIContent SMDRcv CIRcvModerated
DBJCIInvalidJSON dir json -> case fromMsgDirection dir of
AMsgDirection d -> ACIContent d $ CIInvalidJSON json
@@ -703,14 +684,7 @@ data CICallStatus
| CISCallProgress
| CISCallEnded
| CISCallError
deriving (Show, Generic)
instance FromJSON CICallStatus where
parseJSON = J.genericParseJSON . enumJSON $ dropPrefix "CISCall"
instance ToJSON CICallStatus where
toJSON = J.genericToJSON . enumJSON $ dropPrefix "CISCall"
toEncoding = J.genericToEncoding . enumJSON $ dropPrefix "CISCall"
deriving (Show)
ciCallInfoText :: CICallStatus -> Int -> Text
ciCallInfoText status duration = case status of
@@ -722,3 +696,31 @@ ciCallInfoText status duration = case status of
CISCallProgress -> "in progress " <> durationText duration
CISCallEnded -> "ended " <> durationText duration
CISCallError -> "error"
$(JQ.deriveJSON (enumJSON $ dropPrefix "CISCall") ''CICallStatus)
-- platform specific
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "JCI") ''JSONCIContent)
-- platform independent
$(JQ.deriveJSON (singleFieldJSON $ dropPrefix "DBJCI") ''DBJSONCIContent)
-- platform independent
instance MsgDirectionI d => ToField (CIContent d) where
toField = toField . encodeJSON . dbJsonCIContent
-- platform specific
instance MsgDirectionI d => ToJSON (CIContent d) where
toJSON = J.toJSON . jsonCIContent
toEncoding = J.toEncoding . jsonCIContent
instance MsgDirectionI d => FromJSON (CIContent d) where
parseJSON v = (\(ACIContent _ c) -> checkDirection c) <$?> J.parseJSON v
-- platform independent
dbParseACIContent :: Text -> Either String ACIContent
dbParseACIContent = fmap aciContentDBJSON . J.eitherDecodeStrict' . encodeUtf8
-- platform specific
instance FromJSON ACIContent where
parseJSON = fmap aciContentJSON . J.parseJSON

View File

@@ -72,6 +72,9 @@ data ConnectionEntity
| UserContactConnection {entityConnection :: Connection, userContact :: UserContact}
deriving (Eq, Show, Generic)
instance FromJSON ConnectionEntity where
parseJSON = J.genericParseJSON $ sumTypeJSON fstToLower
instance ToJSON ConnectionEntity where
toJSON = J.genericToJSON $ sumTypeJSON fstToLower
toEncoding = J.genericToEncoding $ sumTypeJSON fstToLower

View File

@@ -201,7 +201,7 @@ startRemoteCtrl =
chatWriteVar remoteCtrlSession Nothing
toView $ CRRemoteCtrlStopped {remoteCtrlId}
chatWriteVar remoteCtrlSession $ Just RemoteCtrlSession {discoverer, supervisor, hostServer = Nothing, discovered, accepted}
pure CRRemoteCtrlStarted
pure $ CRRemoteCtrlStarted Nothing
discoverRemoteCtrls :: (ChatMonad m) => TM.TMap C.KeyHash TransportHost -> m ()
discoverRemoteCtrls discovered = Discovery.openListener >>= go

View File

@@ -1,14 +1,14 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE TemplateHaskell #-}
module Simplex.Chat.Remote.Types where
import Control.Concurrent.Async (Async)
import Data.Aeson (ToJSON (..))
import qualified Data.Aeson.TH as J
import Data.Int (Int64)
import Data.Text (Text)
import GHC.Generics (Generic)
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.TMap (TMap)
import Simplex.Messaging.Transport.Client (TransportHost)
@@ -37,7 +37,9 @@ data RemoteCtrl = RemoteCtrl
fingerprint :: C.KeyHash,
accepted :: Maybe Bool
}
deriving (Show, Generic, ToJSON)
deriving (Show)
$(J.deriveJSON J.defaultOptions ''RemoteCtrl)
data RemoteHostSession
= RemoteHostSessionStarting

View File

@@ -1,3 +1,4 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
@@ -59,7 +60,7 @@ where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Data.Aeson (ToJSON)
import Data.Aeson (FromJSON, ToJSON)
import qualified Data.Aeson as J
import Data.Functor (($>))
import Data.Int (Int64)
@@ -398,7 +399,7 @@ data UserContactLink = UserContactLink
{ connReqContact :: ConnReqContact,
autoAccept :: Maybe AutoAccept
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON UserContactLink where toEncoding = J.genericToEncoding J.defaultOptions
@@ -406,7 +407,7 @@ data AutoAccept = AutoAccept
{ acceptIncognito :: IncognitoEnabled,
autoReply :: Maybe MsgContent
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON AutoAccept where toEncoding = J.genericToEncoding J.defaultOptions

View File

@@ -16,7 +16,7 @@ import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Crypto.Random (ChaChaDRG, randomBytesGenerate)
import Data.Aeson (ToJSON)
import Data.Aeson (FromJSON, ToJSON)
import qualified Data.Aeson as J
import qualified Data.ByteString.Base64 as B64
import Data.ByteString.Char8 (ByteString)
@@ -102,6 +102,9 @@ data StoreError
| SENoGroupSndStatus {itemId :: ChatItemId, groupMemberId :: GroupMemberId}
deriving (Show, Exception, Generic)
instance FromJSON StoreError where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "SE"
instance ToJSON StoreError where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "SE"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "SE"

View File

@@ -15,6 +15,7 @@
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
@@ -25,9 +26,10 @@
module Simplex.Chat.Types where
import Crypto.Number.Serialize (os2ip)
import Data.Aeson (FromJSON (..), ToJSON (..), (.=))
import Data.Aeson (FromJSON (..), ToJSON (..), (.:), (.=))
import qualified Data.Aeson as J
import qualified Data.Aeson.Encoding as JE
import qualified Data.Aeson.TH as JQ
import qualified Data.Aeson.Types as JT
import qualified Data.Attoparsec.ByteString.Char8 as A
import Data.ByteString.Char8 (ByteString, pack, unpack)
@@ -112,18 +114,14 @@ data User = User
sendRcptsContacts :: Bool,
sendRcptsSmallGroups :: Bool
}
deriving (Show, Generic, FromJSON)
instance ToJSON User where
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
deriving (Show)
data NewUser = NewUser
{ profile :: Maybe Profile,
sameServers :: Bool,
pastTimestamp :: Bool
}
deriving (Show, Generic, FromJSON)
deriving (Show)
newtype B64UrlByteString = B64UrlByteString ByteString
deriving (Eq, Show)
@@ -144,19 +142,13 @@ instance ToJSON B64UrlByteString where
toEncoding = strToJEncoding
data UserPwdHash = UserPwdHash {hash :: B64UrlByteString, salt :: B64UrlByteString}
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON UserPwdHash where toEncoding = J.genericToEncoding J.defaultOptions
deriving (Eq, Show)
data UserInfo = UserInfo
{ user :: User,
unreadCount :: Int
}
deriving (Show, Generic, FromJSON)
instance ToJSON UserInfo where
toJSON = J.genericToJSON J.defaultOptions
toEncoding = J.genericToEncoding J.defaultOptions
deriving (Show)
type ContactId = Int64
@@ -179,11 +171,7 @@ data Contact = Contact
contactGroupMemberId :: Maybe GroupMemberId,
contactGrpInvSent :: Bool
}
deriving (Eq, Show, Generic)
instance ToJSON Contact where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
deriving (Eq, Show)
contactConn :: Contact -> Connection
contactConn Contact {activeConn} = activeConn
@@ -221,6 +209,9 @@ instance FromField ContactStatus where fromField = fromTextField_ textDecode
instance ToField ContactStatus where toField = toField . textEncode
instance FromJSON ContactStatus where
parseJSON = textParseJSON "ContactStatus"
instance ToJSON ContactStatus where
toJSON = J.String . textEncode
toEncoding = JE.text . textEncode
@@ -240,9 +231,7 @@ data ContactRef = ContactRef
agentConnId :: AgentConnId,
localDisplayName :: ContactName
}
deriving (Eq, Show, Generic)
instance ToJSON ContactRef where toEncoding = J.genericToEncoding J.defaultOptions
deriving (Eq, Show)
data ContactOrGroupMember = CGMContact Contact | CGMGroupMember GroupInfo GroupMember
deriving (Show)
@@ -262,15 +251,13 @@ data UserContact = UserContact
connReqContact :: ConnReqContact,
groupId :: Maybe GroupId
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON UserContact where toEncoding = J.genericToEncoding J.defaultOptions
userContactGroupId :: UserContact -> Maybe GroupId
userContactGroupId UserContact {groupId} = groupId
instance ToJSON UserContact where
toJSON = J.genericToJSON J.defaultOptions
toEncoding = J.genericToEncoding J.defaultOptions
data UserContactRequest = UserContactRequest
{ contactRequestId :: Int64,
agentInvitationId :: AgentInvId,
@@ -284,7 +271,7 @@ data UserContactRequest = UserContactRequest
updatedAt :: UTCTime,
xContactId :: Maybe XContactId
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON UserContactRequest where
toEncoding = J.genericToEncoding J.defaultOptions
@@ -341,7 +328,7 @@ optionalFullName displayName fullName
| otherwise = " (" <> fullName <> ")"
data Group = Group {groupInfo :: GroupInfo, members :: [GroupMember]}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON Group where toEncoding = J.genericToEncoding J.defaultOptions
@@ -359,7 +346,7 @@ data GroupInfo = GroupInfo
updatedAt :: UTCTime,
chatTs :: Maybe UTCTime
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON GroupInfo where toEncoding = J.genericToEncoding J.defaultOptions
@@ -369,7 +356,7 @@ groupName' GroupInfo {localDisplayName = g} = g
data GroupSummary = GroupSummary
{ currentMembers :: Int
}
deriving (Show, Generic)
deriving (Show, Generic, FromJSON)
instance ToJSON GroupSummary where toEncoding = J.genericToEncoding J.defaultOptions
@@ -639,7 +626,7 @@ data GroupMember = GroupMember
memberContactProfileId :: ProfileId,
activeConn :: Maybe Connection
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON GroupMember where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -710,6 +697,9 @@ instance ToJSON MemberId where
data InvitedBy = IBContact {byContactId :: Int64} | IBUser | IBUnknown
deriving (Eq, Show, Generic)
instance FromJSON InvitedBy where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "IB"
instance ToJSON InvitedBy where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "IB"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "IB"
@@ -803,6 +793,9 @@ instance FromField GroupMemberCategory where fromField = fromTextField_ textDeco
instance ToField GroupMemberCategory where toField = toField . textEncode
instance FromJSON GroupMemberCategory where
parseJSON = textParseJSON "GroupMemberCategory"
instance ToJSON GroupMemberCategory where
toJSON = J.String . textEncode
toEncoding = JE.text . textEncode
@@ -840,6 +833,9 @@ instance FromField GroupMemberStatus where fromField = fromTextField_ textDecode
instance ToField GroupMemberStatus where toField = toField . textEncode
instance FromJSON GroupMemberStatus where
parseJSON = textParseJSON "GroupMemberStatus"
instance ToJSON GroupMemberStatus where
toJSON = J.String . textEncode
toEncoding = JE.text . textEncode
@@ -931,7 +927,7 @@ data SndFileTransfer = SndFileTransfer
fileDescrId :: Maybe Int64,
fileInline :: Maybe InlineFileMode
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON SndFileTransfer where toEncoding = J.genericToEncoding J.defaultOptions
@@ -997,7 +993,7 @@ instance FromField InlineFileMode where fromField = fromTextField_ textDecode
instance ToField InlineFileMode where toField = toField . textEncode
instance FromJSON InlineFileMode where
parseJSON = J.withText "InlineFileMode" $ maybe (fail "bad InlineFileMode") pure . textDecode
parseJSON = textParseJSON "InlineFileMode"
instance ToJSON InlineFileMode where
toJSON = J.String . textEncode
@@ -1017,7 +1013,7 @@ data RcvFileTransfer = RcvFileTransfer
-- SMP files are encrypted after all chunks are received
cryptoArgs :: Maybe CryptoFileArgs
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON RcvFileTransfer where toEncoding = J.genericToEncoding J.defaultOptions
@@ -1026,7 +1022,7 @@ data XFTPRcvFile = XFTPRcvFile
agentRcvFileId :: Maybe AgentRcvFileId,
agentRcvFileDeleted :: Bool
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON XFTPRcvFile where toEncoding = J.genericToEncoding J.defaultOptions
@@ -1036,7 +1032,7 @@ data RcvFileDescr = RcvFileDescr
fileDescrPartNo :: Int,
fileDescrComplete :: Bool
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON RcvFileDescr where toEncoding = J.genericToEncoding J.defaultOptions
@@ -1048,6 +1044,9 @@ data RcvFileStatus
| RFSCancelled (Maybe RcvFileInfo)
deriving (Eq, Show, Generic)
instance FromJSON RcvFileStatus where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "RFS"
instance ToJSON RcvFileStatus where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "RFS"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "RFS"
@@ -1065,7 +1064,7 @@ data RcvFileInfo = RcvFileInfo
connId :: Maybe Int64,
agentConnId :: Maybe AgentConnId
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON RcvFileInfo where toEncoding = J.genericToEncoding J.defaultOptions
@@ -1094,6 +1093,9 @@ instance StrEncoding AgentConnId where
strDecode s = AgentConnId <$> strDecode s
strP = AgentConnId <$> strP
instance FromJSON AgentConnId where
parseJSON = strParseJSON "AgentConnId"
instance ToJSON AgentConnId where
toJSON = strToJSON
toEncoding = strToJEncoding
@@ -1110,6 +1112,9 @@ instance StrEncoding AgentSndFileId where
strDecode s = AgentSndFileId <$> strDecode s
strP = AgentSndFileId <$> strP
instance FromJSON AgentSndFileId where
parseJSON = strParseJSON "AgentSndFileId"
instance ToJSON AgentSndFileId where
toJSON = strToJSON
toEncoding = strToJEncoding
@@ -1126,6 +1131,9 @@ instance StrEncoding AgentRcvFileId where
strDecode s = AgentRcvFileId <$> strDecode s
strP = AgentRcvFileId <$> strP
instance FromJSON AgentRcvFileId where
parseJSON = strParseJSON "AgentRcvFileId"
instance ToJSON AgentRcvFileId where
toJSON = strToJSON
toEncoding = strToJEncoding
@@ -1142,6 +1150,9 @@ instance StrEncoding AgentInvId where
strDecode s = AgentInvId <$> strDecode s
strP = AgentInvId <$> strP
instance FromJSON AgentInvId where
parseJSON = strParseJSON "AgentInvId"
instance ToJSON AgentInvId where
toJSON = strToJSON
toEncoding = strToJEncoding
@@ -1158,6 +1169,9 @@ data FileTransfer
| FTRcv {rcvFileTransfer :: RcvFileTransfer}
deriving (Show, Generic)
instance FromJSON FileTransfer where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "FT"
instance ToJSON FileTransfer where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "FT"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "FT"
@@ -1172,7 +1186,7 @@ data FileTransferMeta = FileTransferMeta
chunkSize :: Integer,
cancelled :: Bool
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON FileTransferMeta where toEncoding = J.genericToEncoding J.defaultOptions
@@ -1182,7 +1196,7 @@ data XFTPSndFile = XFTPSndFile
agentSndFileDeleted :: Bool,
cryptoArgs :: Maybe CryptoFileArgs
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON XFTPSndFile where toEncoding = J.genericToEncoding J.defaultOptions
@@ -1197,6 +1211,9 @@ instance FromField FileStatus where fromField = fromTextField_ textDecode
instance ToField FileStatus where toField = toField . textEncode
instance FromJSON FileStatus where
parseJSON = textParseJSON "FileStatus"
instance ToJSON FileStatus where
toJSON = J.String . textEncode
toEncoding = JE.text . textEncode
@@ -1250,11 +1267,9 @@ connDisabled :: Connection -> Bool
connDisabled Connection {authErrCounter} = authErrCounter >= authErrDisableCount
data SecurityCode = SecurityCode {securityCode :: Text, verifiedAt :: UTCTime}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
instance ToJSON SecurityCode where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
instance ToJSON SecurityCode where toEncoding = J.genericToEncoding J.defaultOptions
verificationCode :: ByteString -> Text
verificationCode = T.pack . unwords . chunks 5 . show . os2ip
@@ -1273,6 +1288,9 @@ aConnId Connection {agentConnId = AgentConnId cId} = cId
connIncognito :: Connection -> Bool
connIncognito Connection {customUserProfileId} = isJust customUserProfileId
instance FromJSON Connection where
parseJSON = J.genericParseJSON J.defaultOptions {J.omitNothingFields = True}
instance ToJSON Connection where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
@@ -1290,7 +1308,7 @@ data PendingContactConnection = PendingContactConnection
createdAt :: UTCTime,
updatedAt :: UTCTime
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
aConnId' :: PendingContactConnection -> ConnId
aConnId' PendingContactConnection {pccAgentConnId = AgentConnId cId} = cId
@@ -1318,6 +1336,9 @@ instance FromField ConnStatus where fromField = fromTextField_ textDecode
instance ToField ConnStatus where toField = toField . textEncode
instance FromJSON ConnStatus where
parseJSON = textParseJSON "ConnStatus"
instance ToJSON ConnStatus where
toJSON = J.String . textEncode
toEncoding = JE.text . textEncode
@@ -1348,6 +1369,9 @@ instance FromField ConnType where fromField = fromTextField_ textDecode
instance ToField ConnType where toField = toField . textEncode
instance FromJSON ConnType where
parseJSON = textParseJSON "ConnType"
instance ToJSON ConnType where
toJSON = J.String . textEncode
toEncoding = JE.text . textEncode
@@ -1550,6 +1574,24 @@ instance ToJSON ChatVersionRange where
newtype JVersionRange = JVersionRange {fromJVersionRange :: VersionRange} deriving (Eq, Show)
instance FromJSON JVersionRange where
parseJSON = J.withObject "JVersionRange" $ \o -> do
minv <- o .: "minVersion"
maxv <- o .: "maxVersion"
maybe (fail "bad version range") (pure . JVersionRange) $ safeVersionRange minv maxv
instance ToJSON JVersionRange where
toJSON (JVersionRange (VersionRange minV maxV)) = J.object ["minVersion" .= minV, "maxVersion" .= maxV]
toEncoding (JVersionRange (VersionRange minV maxV)) = J.pairs $ "minVersion" .= minV <> "maxVersion" .= maxV
$(JQ.deriveJSON defOpts ''UserPwdHash)
$(JQ.deriveJSON defOpts ''User)
$(JQ.deriveJSON defOpts ''NewUser)
$(JQ.deriveJSON defOpts ''UserInfo)
$(JQ.deriveJSON defOpts ''Contact)
$(JQ.deriveJSON defOpts ''ContactRef)

View File

@@ -338,7 +338,7 @@ data ContactUserPreferences = ContactUserPreferences
voice :: ContactUserPreference VoicePreference,
calls :: ContactUserPreference CallsPreference
}
deriving (Eq, Show, Generic)
deriving (Eq, Show, Generic, FromJSON)
data ContactUserPreference p = ContactUserPreference
{ enabled :: PrefEnabled,
@@ -352,8 +352,13 @@ data ContactUserPref p = CUPContact {preference :: p} | CUPUser {preference :: p
instance ToJSON ContactUserPreferences where toEncoding = J.genericToEncoding J.defaultOptions
instance FromJSON p => FromJSON (ContactUserPreference p) where parseJSON = J.genericParseJSON J.defaultOptions
instance ToJSON p => ToJSON (ContactUserPreference p) where toEncoding = J.genericToEncoding J.defaultOptions
instance FromJSON p => FromJSON (ContactUserPref p) where
parseJSON = J.genericParseJSON . sumTypeJSON $ dropPrefix "CUP"
instance ToJSON p => ToJSON (ContactUserPref p) where
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "CUP"
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "CUP"

View File

@@ -28,3 +28,6 @@ fromBlobField_ p = \case
Right k -> Ok k
Left e -> returnError ConversionFailed f ("could not parse field: " ++ e)
f -> returnError ConversionFailed f "expecting SQLBlob column type"
defOpts :: J.Options
defOpts = J.defaultOptions {J.omitNothingFields = True}

View File

@@ -73,10 +73,10 @@ responseToView :: Maybe User -> ChatConfig -> Bool -> CurrentTime -> TimeZone ->
responseToView user_ ChatConfig {logLevel, showReactions, showReceipts, testView} liveItems ts tz = \case
CRActiveUser User {profile} -> viewUserProfile $ fromLocalProfile profile
CRUsersList users -> viewUsersList users
CRChatStarted -> ["chat started"]
CRChatRunning -> ["chat is running"]
CRChatStopped -> ["chat stopped"]
CRChatSuspended -> ["chat suspended"]
CRChatStarted _ -> ["chat started"]
CRChatRunning _ -> ["chat is running"]
CRChatStopped _ -> ["chat stopped"]
CRChatSuspended _ -> ["chat suspended"]
CRApiChats u chats -> ttyUser u $ if testView then testViewChats chats else [plain . bshow $ J.encode chats]
CRChats chats -> viewChats ts tz chats
CRApiChat u chat -> ttyUser u $ if testView then testViewChat chat else [plain . bshow $ J.encode chat]
@@ -267,7 +267,7 @@ responseToView user_ ChatConfig {logLevel, showReactions, showReceipts, testView
CRRemoteHostDeleted rhId -> ["remote host " <> sShow rhId <> " deleted"]
CRRemoteCtrlList cs -> viewRemoteCtrls cs
CRRemoteCtrlRegistered rcId -> ["remote controller " <> sShow rcId <> " registered"]
CRRemoteCtrlStarted -> ["remote controller started"]
CRRemoteCtrlStarted _ -> ["remote controller started"]
CRRemoteCtrlAnnounce fingerprint -> ["remote controller announced", "connection code:", plain $ strEncode fingerprint]
CRRemoteCtrlFound rc -> ["remote controller found:", viewRemoteCtrl rc]
CRRemoteCtrlAccepted rcId -> ["remote controller " <> sShow rcId <> " accepted"]

View File

@@ -49,7 +49,7 @@ extra-deps:
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
# - ../simplexmq
- github: simplex-chat/simplexmq
commit: ec1b72cb8013a65a5d9783104a47ae44f5730089
commit: 96a38505d63ec9a12096991e7725b250e397af72
- github: kazu-yamamoto/http2
commit: b5a1b7200cf5bc7044af34ba325284271f6dff25
# - ../direct-sqlcipher