core: api for pending contact connections (#553)

* core: api for pending contact connections

* core: pending contact connection events / api
This commit is contained in:
Evgeny Poberezkin
2022-04-23 17:32:40 +01:00
committed by GitHub
parent a525f24969
commit 14514050ae
6 changed files with 112 additions and 48 deletions

View File

@@ -183,11 +183,12 @@ processChatCommand = \case
ff <- asks filesFolder
atomically . writeTVar ff $ Just filesFolder'
pure CRCmdOk
APIGetChats -> CRApiChats <$> withUser (\user -> withStore (`getChatPreviews` user))
APIGetChats withPCC -> CRApiChats <$> withUser (\user -> withStore $ \st -> getChatPreviews st user withPCC)
APIGetChat cType cId pagination -> withUser $ \user -> case cType of
CTDirect -> CRApiChat . AChat SCTDirect <$> withStore (\st -> getDirectChat st user cId pagination)
CTGroup -> CRApiChat . AChat SCTGroup <$> withStore (\st -> getGroupChat st user cId pagination)
CTContactRequest -> pure $ chatCmdError "not implemented"
CTContactConnection -> pure $ chatCmdError "not supported"
APIGetChatItems _pagination -> pure $ chatCmdError "not implemented"
APISendMessage cType chatId file_ quotedItemId_ mc -> withUser $ \user@User {userId} -> withChatLock $ case cType of
CTDirect -> do
@@ -263,6 +264,7 @@ processChatCommand = \case
quoteData (CIRcvMsgContent qmc) (CIGroupRcv m) _ = pure (qmc, CIQGroupRcv $ Just m, False, m)
quoteData _ _ _ = throwChatError CEInvalidQuote
CTContactRequest -> pure $ chatCmdError "not supported"
CTContactConnection -> pure $ chatCmdError "not supported"
where
quoteContent qmc = \case
MCText _ -> qmc
@@ -300,6 +302,7 @@ processChatCommand = \case
_ -> throwChatError CEInvalidChatItemUpdate
CChatItem SMDRcv _ -> throwChatError CEInvalidChatItemUpdate
CTContactRequest -> pure $ chatCmdError "not supported"
CTContactConnection -> pure $ chatCmdError "not supported"
APIDeleteChatItem cType chatId itemId mode -> withUser $ \user@User {userId} -> withChatLock $ case cType of
CTDirect -> do
(ct@Contact {localDisplayName = c}, CChatItem msgDir deletedItem@ChatItem {meta = CIMeta {itemSharedMsgId}, file}) <- withStore $ \st -> (,) <$> getContact st userId chatId <*> getDirectChatItem st userId chatId itemId
@@ -332,6 +335,7 @@ processChatCommand = \case
pure $ CRChatItemDeleted (AChatItem SCTGroup msgDir (GroupChat gInfo) deletedItem) toCi
(CIDMBroadcast, _, _) -> throwChatError CEInvalidChatItemDelete
CTContactRequest -> pure $ chatCmdError "not supported"
CTContactConnection -> pure $ chatCmdError "not supported"
where
deleteFile :: MsgDirectionI d => UserId -> Maybe (CIFile d) -> m ()
deleteFile userId file =
@@ -343,6 +347,7 @@ processChatCommand = \case
CTDirect -> withStore (\st -> updateDirectChatItemsRead st chatId fromToIds) $> CRCmdOk
CTGroup -> withStore (\st -> updateGroupChatItemsRead st chatId fromToIds) $> CRCmdOk
CTContactRequest -> pure $ chatCmdError "not supported"
CTContactConnection -> pure $ chatCmdError "not supported"
APIDeleteChat cType chatId -> withUser $ \User {userId} -> case cType of
CTDirect -> do
ct@Contact {localDisplayName} <- withStore $ \st -> getContact st userId chatId
@@ -360,6 +365,8 @@ processChatCommand = \case
unsetActive $ ActiveC localDisplayName
pure $ CRContactDeleted ct
gs -> throwChatError $ CEContactGroups ct gs
CTContactConnection ->
CRContactConnectionDeleted <$> withStore (\st -> deletePendingContactConnection st userId chatId)
CTGroup -> pure $ chatCmdError "not implemented"
CTContactRequest -> pure $ chatCmdError "not supported"
APIAcceptContact connReqId -> withUser $ \user@User {userId} -> withChatLock $ do
@@ -387,11 +394,13 @@ processChatCommand = \case
Welcome -> withUser $ pure . CRWelcome
AddContact -> withUser $ \User {userId} -> withChatLock . procCmd $ do
(connId, cReq) <- withAgent (`createConnection` SCMInvitation)
withStore $ \st -> createDirectConnection st userId connId
conn <- withStore $ \st -> createDirectConnection st userId connId ConnNew
toView $ CRNewContactConnection conn
pure $ CRInvitation cReq
Connect (Just (ACR SCMInvitation cReq)) -> withUser $ \User {userId, profile} -> withChatLock . procCmd $ do
connId <- withAgent $ \a -> joinConnection a cReq . directMessage $ XInfo profile
withStore $ \st -> createDirectConnection st userId connId
conn <- withStore $ \st -> createDirectConnection st userId connId ConnJoined
toView $ CRNewContactConnection conn
pure CRSentConfirmation
Connect (Just (ACR SCMContact cReq)) -> withUser $ \User {userId, profile} ->
connectViaContact userId cReq profile
@@ -647,7 +656,8 @@ processChatCommand = \case
let randomXContactId = XContactId <$> (asks idsDrg >>= liftIO . (`randomBytes` 16))
xContactId <- maybe randomXContactId pure xContactId_
connId <- withAgent $ \a -> joinConnection a cReq $ directMessage (XContact profile $ Just xContactId)
withStore $ \st -> createConnReqConnection st userId connId cReqHash xContactId
conn <- withStore $ \st -> createConnReqConnection st userId connId cReqHash xContactId
toView $ CRNewContactConnection conn
pure CRSentInvitation
contactMember :: Contact -> [GroupMember] -> Maybe GroupMember
contactMember Contact {contactId} =
@@ -1889,7 +1899,7 @@ chatCommandP =
<|> ("/user" <|> "/u") $> ShowActiveUser
<|> "/_start" $> StartChat
<|> "/_files_folder " *> (SetFilesFolder <$> filePath)
<|> "/_get chats" $> APIGetChats
<|> "/_get chats" *> (APIGetChats <$> (" connections" $> True <|> pure False))
<|> "/_get chat " *> (APIGetChat <$> chatTypeP <*> A.decimal <* A.space <*> chatPaginationP)
<|> "/_get items count=" *> (APIGetChatItems <$> A.decimal)
<|> "/_send " *> (APISendMessage <$> chatTypeP <*> A.decimal <*> optional filePathTagged <*> optional quotedItemIdTagged <* A.space <*> msgContentP)
@@ -1962,7 +1972,7 @@ chatCommandP =
where
imagePrefix = (<>) <$> "data:" <*> ("image/png;base64," <|> "image/jpg;base64,")
imageP = safeDecodeUtf8 <$> ((<>) <$> imagePrefix <*> (B64.encode <$> base64P))
chatTypeP = A.char '@' $> CTDirect <|> A.char '#' $> CTGroup
chatTypeP = A.char '@' $> CTDirect <|> A.char '#' $> CTGroup <|> A.char ':' $> CTContactConnection
chatPaginationP =
(CPLast <$ "count=" <*> A.decimal)
<|> (CPAfter <$ "after=" <*> A.decimal <* A.space <* "count=" <*> A.decimal)

View File

@@ -96,7 +96,7 @@ data ChatCommand
| CreateActiveUser Profile
| StartChat
| SetFilesFolder FilePath
| APIGetChats
| APIGetChats {pendingConnections :: Bool}
| APIGetChat ChatType Int64 ChatPagination
| APIGetChatItems Int
| APISendMessage ChatType Int64 (Maybe FilePath) (Maybe ChatItemId) MsgContent
@@ -246,6 +246,8 @@ data ChatResponse
| CRUserContactLinkSubscribed
| CRUserContactLinkSubError {chatError :: ChatError}
| CRNtfTokenStatus {status :: NtfTknStatus}
| CRNewContactConnection {connection :: PendingContactConnection}
| CRContactConnectionDeleted {connection :: PendingContactConnection}
| CRMessageError {severity :: Text, errorMessage :: Text}
| CRChatCmdError {chatError :: ChatError}
| CRChatError {chatError :: ChatError}

View File

@@ -38,7 +38,7 @@ import Simplex.Messaging.Parsers (dropPrefix, enumJSON, fromTextField_, singleFi
import Simplex.Messaging.Protocol (MsgBody)
import Simplex.Messaging.Util ((<$?>))
data ChatType = CTDirect | CTGroup | CTContactRequest
data ChatType = CTDirect | CTGroup | CTContactRequest | CTContactConnection
deriving (Show, Generic)
instance ToJSON ChatType where
@@ -49,6 +49,7 @@ data ChatInfo (c :: ChatType) where
DirectChat :: Contact -> ChatInfo 'CTDirect
GroupChat :: GroupInfo -> ChatInfo 'CTGroup
ContactRequest :: UserContactRequest -> ChatInfo 'CTContactRequest
ContactConnection :: PendingContactConnection -> ChatInfo 'CTContactConnection
deriving instance Show (ChatInfo c)
@@ -56,6 +57,7 @@ data JSONChatInfo
= JCInfoDirect {contact :: Contact}
| JCInfoGroup {groupInfo :: GroupInfo}
| JCInfoContactRequest {contactRequest :: UserContactRequest}
| JCInfoContactConnection {contactConnection :: PendingContactConnection}
deriving (Generic)
instance ToJSON JSONChatInfo where
@@ -71,6 +73,7 @@ jsonChatInfo = \case
DirectChat c -> JCInfoDirect c
GroupChat g -> JCInfoGroup g
ContactRequest g -> JCInfoContactRequest g
ContactConnection c -> JCInfoContactConnection c
data ChatItem (c :: ChatType) (d :: MsgDirection) = ChatItem
{ chatDir :: CIDirection c d,
@@ -519,12 +522,15 @@ data SChatType (c :: ChatType) where
SCTDirect :: SChatType 'CTDirect
SCTGroup :: SChatType 'CTGroup
SCTContactRequest :: SChatType 'CTContactRequest
SCTContactConnection :: SChatType 'CTContactConnection
deriving instance Show (SChatType c)
instance TestEquality SChatType where
testEquality SCTDirect SCTDirect = Just Refl
testEquality SCTGroup SCTGroup = Just Refl
testEquality SCTContactRequest SCTContactRequest = Just Refl
testEquality SCTContactConnection SCTContactConnection = Just Refl
testEquality _ _ = Nothing
class ChatTypeI (c :: ChatType) where

View File

@@ -151,6 +151,7 @@ module Simplex.Chat.Store
updateGroupChatItemsRead,
getSMPServers,
overwriteSMPServers,
deletePendingContactConnection,
)
where
@@ -171,7 +172,7 @@ import Data.Function (on)
import Data.Functor (($>))
import Data.Int (Int64)
import Data.List (find, sortBy, sortOn)
import Data.Maybe (fromMaybe, listToMaybe)
import Data.Maybe (fromMaybe, isJust, listToMaybe)
import Data.Ord (Down (..))
import Data.Text (Text)
import qualified Data.Text as T
@@ -296,10 +297,11 @@ setActiveUser st userId = do
DB.execute_ db "UPDATE users SET active_user = 0"
DB.execute db "UPDATE users SET active_user = 1 WHERE user_id = ?" (Only userId)
createConnReqConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnId -> ConnReqUriHash -> XContactId -> m ()
createConnReqConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnId -> ConnReqUriHash -> XContactId -> m PendingContactConnection
createConnReqConnection st userId acId cReqHash xContactId = do
liftIO . withTransaction st $ \db -> do
currentTs <- getCurrentTime
createdAt <- getCurrentTime
let pccConnStatus = ConnJoined
DB.execute
db
[sql|
@@ -308,7 +310,9 @@ createConnReqConnection st userId acId cReqHash xContactId = do
created_at, updated_at, via_contact_uri_hash, xcontact_id
) VALUES (?,?,?,?,?,?,?,?)
|]
(userId, acId, ConnNew, ConnContact, currentTs, currentTs, cReqHash, xContactId)
(userId, acId, pccConnStatus, ConnContact, createdAt, createdAt, cReqHash, xContactId)
pccConnId <- insertedRowId db
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = True, createdAt}
getConnReqContactXContactId :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnReqUriHash -> m (Maybe Contact, Maybe XContactId)
getConnReqContactXContactId st userId cReqHash = do
@@ -345,11 +349,19 @@ getConnReqContactXContactId st userId cReqHash = do
"SELECT xcontact_id FROM connections WHERE user_id = ? AND via_contact_uri_hash = ? LIMIT 1"
(userId, cReqHash)
createDirectConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnId -> m ()
createDirectConnection st userId agentConnId =
createDirectConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> ConnId -> ConnStatus -> m PendingContactConnection
createDirectConnection st userId acId pccConnStatus =
liftIO . withTransaction st $ \db -> do
currentTs <- getCurrentTime
void $ createContactConnection_ db userId agentConnId Nothing 0 currentTs
createdAt <- getCurrentTime
DB.execute
db
[sql|
INSERT INTO connections
(user_id, agent_conn_id, conn_status, conn_type, created_at, updated_at) VALUES (?,?,?,?,?,?)
|]
(userId, acId, pccConnStatus, ConnContact, createdAt, createdAt)
pccConnId <- insertedRowId db
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, createdAt, viaContactUri = False}
createContactConnection_ :: DB.Connection -> UserId -> ConnId -> Maybe Int64 -> Int -> UTCTime -> IO Connection
createContactConnection_ db userId = createConnection_ db userId ConnContact Nothing
@@ -2500,13 +2512,14 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
ciQuoteGroup [] = ciQuote Nothing $ CIQGroupRcv Nothing
ciQuoteGroup ((Only itemId :. memberRow) : _) = ciQuote itemId . CIQGroupRcv . Just $ toGroupMember userContactId memberRow
getChatPreviews :: MonadUnliftIO m => SQLiteStore -> User -> m [AChat]
getChatPreviews st user =
getChatPreviews :: MonadUnliftIO m => SQLiteStore -> User -> Bool -> m [AChat]
getChatPreviews st user withPCC =
liftIO . withTransaction st $ \db -> do
directChats <- getDirectChatPreviews_ db user
groupChats <- getGroupChatPreviews_ db user
cReqChats <- getContactRequestChatPreviews_ db user
pure $ sortOn (Down . ts) (directChats <> groupChats <> cReqChats)
connChats <- getContactConnectionChatPreviews_ db user withPCC
pure $ sortOn (Down . ts) (directChats <> groupChats <> cReqChats <> connChats)
where
ts :: AChat -> UTCTime
ts (AChat _ Chat {chatItems = ci : _}) = chatItemTs ci
@@ -2514,6 +2527,7 @@ getChatPreviews st user =
DirectChat Contact {createdAt} -> createdAt
GroupChat GroupInfo {createdAt} -> createdAt
ContactRequest UserContactRequest {createdAt} -> createdAt
ContactConnection PendingContactConnection {createdAt} -> createdAt
chatItemTs :: CChatItem d -> UTCTime
chatItemTs (CChatItem _ ChatItem {meta = CIMeta {itemTs}}) = itemTs
@@ -2671,6 +2685,45 @@ getContactRequestChatPreviews_ db User {userId} =
stats = ChatStats {unreadCount = 0, minUnreadItemId = 0}
in AChat SCTContactRequest $ Chat (ContactRequest cReq) [] stats
getContactConnectionChatPreviews_ :: DB.Connection -> User -> Bool -> IO [AChat]
getContactConnectionChatPreviews_ _ _ False = pure []
getContactConnectionChatPreviews_ db User {userId} _ =
map toContactConnectionChatPreview
<$> DB.query
db
[sql|
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, created_at
FROM connections
WHERE user_id = ? AND conn_type = ? AND contact_id IS NULL AND conn_level = 0 AND via_contact IS NULL
|]
(userId, ConnContact)
where
toContactConnectionChatPreview :: (Int64, ConnId, ConnStatus, Maybe ByteString, UTCTime) -> AChat
toContactConnectionChatPreview connRow =
let conn = toPendingContactConnection connRow
stats = ChatStats {unreadCount = 0, minUnreadItemId = 0}
in AChat SCTContactConnection $ Chat (ContactConnection conn) [] stats
deletePendingContactConnection :: StoreMonad m => SQLiteStore -> UserId -> Int64 -> m PendingContactConnection
deletePendingContactConnection st userId connId =
liftIOEither . withTransaction st $ \db -> runExceptT $ do
conn <-
ExceptT . firstRow toPendingContactConnection (SEPendingConnectionNotFound connId) $
DB.query
db
[sql|
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, created_at
FROM connections
WHERE user_id = ? AND conn_type = ? AND contact_id IS NULL AND conn_level = 0 AND via_contact IS NULL
|]
(userId, ConnContact)
liftIO $ DB.execute db "DELETE FROM connections WHERE connection_id = ?" (Only connId)
pure conn
toPendingContactConnection :: (Int64, ConnId, ConnStatus, Maybe ByteString, UTCTime) -> PendingContactConnection
toPendingContactConnection (pccConnId, acId, pccConnStatus, connReqHash, createdAt) =
PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = isJust connReqHash, createdAt}
getDirectChat :: StoreMonad m => SQLiteStore -> User -> Int64 -> ChatPagination -> m (Chat 'CTDirect)
getDirectChat st user contactId pagination =
liftIOEither . withTransaction st $ \db -> runExceptT $ do
@@ -3646,6 +3699,7 @@ data StoreError
| SESharedMsgIdNotFoundByFileId {fileId :: FileTransferId}
| SEFileIdNotFoundBySharedMsgId {sharedMsgId :: SharedMsgId}
| SEConnectionNotFound {agentConnId :: AgentConnId}
| SEPendingConnectionNotFound {connId :: Int64}
| SEIntroNotFound
| SEUniqueID
| SEInternalError {message :: String}

View File

@@ -72,7 +72,7 @@ data Contact = Contact
viaGroup :: Maybe Int64,
createdAt :: UTCTime
}
deriving (Eq, Show, Generic, FromJSON)
deriving (Eq, Show, Generic)
instance ToJSON Contact where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -101,7 +101,7 @@ data UserContactRequest = UserContactRequest
createdAt :: UTCTime,
xContactId :: Maybe XContactId
}
deriving (Eq, Show, Generic, FromJSON)
deriving (Eq, Show, Generic)
instance ToJSON UserContactRequest where
toEncoding = J.genericToEncoding J.defaultOptions
@@ -160,7 +160,7 @@ data GroupInfo = GroupInfo
membership :: GroupMember,
createdAt :: UTCTime
}
deriving (Eq, Show, Generic, FromJSON)
deriving (Eq, Show, Generic)
instance ToJSON GroupInfo where toEncoding = J.genericToEncoding J.defaultOptions
@@ -262,7 +262,7 @@ data GroupMember = GroupMember
memberContactId :: Maybe Int64,
activeConn :: Maybe Connection
}
deriving (Eq, Show, Generic, FromJSON)
deriving (Eq, Show, Generic)
instance ToJSON GroupMember where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
@@ -306,9 +306,6 @@ 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"
@@ -401,8 +398,6 @@ 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
@@ -440,8 +435,6 @@ 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
@@ -536,7 +529,7 @@ data RcvFileTransfer = RcvFileTransfer
cancelled :: Bool,
grpMemberId :: Maybe Int64
}
deriving (Eq, Show, Generic, FromJSON)
deriving (Eq, Show, Generic)
instance ToJSON RcvFileTransfer where toEncoding = J.genericToEncoding J.defaultOptions
@@ -548,9 +541,6 @@ data RcvFileStatus
| RFSCancelled 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"
@@ -560,7 +550,7 @@ data RcvFileInfo = RcvFileInfo
connId :: Int64,
agentConnId :: AgentConnId
}
deriving (Eq, Show, Generic, FromJSON)
deriving (Eq, Show, Generic)
instance ToJSON RcvFileInfo where toEncoding = J.genericToEncoding J.defaultOptions
@@ -572,9 +562,6 @@ 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
@@ -591,9 +578,6 @@ 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
@@ -636,8 +620,6 @@ 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
@@ -674,7 +656,7 @@ data Connection = Connection
entityId :: Maybe Int64, -- contact, group member, file ID or user contact ID
createdAt :: UTCTime
}
deriving (Eq, Show, Generic, FromJSON)
deriving (Eq, Show, Generic)
aConnId :: Connection -> ConnId
aConnId Connection {agentConnId = AgentConnId cId} = cId
@@ -683,6 +665,17 @@ instance ToJSON Connection where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
data PendingContactConnection = PendingContactConnection
{ pccConnId :: Int64,
pccAgentConnId :: AgentConnId,
pccConnStatus :: ConnStatus,
viaContactUri :: Bool,
createdAt :: UTCTime
}
deriving (Eq, Show, Generic)
instance ToJSON PendingContactConnection where toEncoding = J.genericToEncoding J.defaultOptions
data ConnStatus
= -- | connection is created by initiating party with agent NEW command (createConnection)
ConnNew
@@ -704,8 +697,6 @@ 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
@@ -736,8 +727,6 @@ 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

View File

@@ -141,6 +141,8 @@ responseToView testView = \case
["received file " <> sShow fileId <> " (" <> plain fileName <> ") error: " <> sShow e]
CRUserContactLinkSubscribed -> ["Your address is active! To show: " <> highlight' "/sa"]
CRUserContactLinkSubError e -> ["user address error: " <> sShow e, "to delete your address: " <> highlight' "/da"]
CRNewContactConnection _ -> []
CRContactConnectionDeleted _ -> []
CRNtfTokenStatus status -> ["device token status: " <> plain (smpEncode status)]
CRMessageError prefix err -> [plain prefix <> ": " <> plain err]
CRChatError e -> viewChatError e
@@ -152,6 +154,7 @@ responseToView testView = \case
toChatView (AChat _ (Chat (DirectChat Contact {localDisplayName}) items _)) = ("@" <> localDisplayName, toCIPreview items)
toChatView (AChat _ (Chat (GroupChat GroupInfo {localDisplayName}) items _)) = ("#" <> localDisplayName, toCIPreview items)
toChatView (AChat _ (Chat (ContactRequest UserContactRequest {localDisplayName}) items _)) = ("<@" <> localDisplayName, toCIPreview items)
toChatView (AChat _ (Chat (ContactConnection PendingContactConnection {pccConnId}) items _)) = (":" <> T.pack (show pccConnId), toCIPreview items)
toCIPreview :: [CChatItem c] -> Text
toCIPreview ((CChatItem _ ChatItem {meta}) : _) = itemText meta
toCIPreview _ = ""