Revert "core: direct messages in group (#2994)"
This reverts commit 5fddf64adb
.
This commit is contained in:
parent
75f18bc5f0
commit
01f99baaac
@ -111,7 +111,6 @@ library
|
|||||||
Simplex.Chat.Migrations.M20230827_file_encryption
|
Simplex.Chat.Migrations.M20230827_file_encryption
|
||||||
Simplex.Chat.Migrations.M20230829_connections_chat_vrange
|
Simplex.Chat.Migrations.M20230829_connections_chat_vrange
|
||||||
Simplex.Chat.Migrations.M20230903_connections_to_subscribe
|
Simplex.Chat.Migrations.M20230903_connections_to_subscribe
|
||||||
Simplex.Chat.Migrations.M20230904_item_direct_group_member_id
|
|
||||||
Simplex.Chat.Mobile
|
Simplex.Chat.Mobile
|
||||||
Simplex.Chat.Mobile.File
|
Simplex.Chat.Mobile.File
|
||||||
Simplex.Chat.Mobile.Shared
|
Simplex.Chat.Mobile.Shared
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -67,7 +67,7 @@ sendComposedMessage cc = sendComposedMessage' cc . contactId'
|
|||||||
sendComposedMessage' :: ChatController -> ContactId -> Maybe ChatItemId -> MsgContent -> IO ()
|
sendComposedMessage' :: ChatController -> ContactId -> Maybe ChatItemId -> MsgContent -> IO ()
|
||||||
sendComposedMessage' cc ctId quotedItemId msgContent = do
|
sendComposedMessage' cc ctId quotedItemId msgContent = do
|
||||||
let cm = ComposedMessage {fileSource = Nothing, quotedItemId, msgContent}
|
let cm = ComposedMessage {fileSource = Nothing, quotedItemId, msgContent}
|
||||||
sendChatCmd cc (APISendMessage (SRDirect ctId) False Nothing cm) >>= \case
|
sendChatCmd cc (APISendMessage (ChatRef CTDirect ctId) False Nothing cm) >>= \case
|
||||||
CRNewChatItem {} -> printLog cc CLLInfo $ "sent message to contact ID " <> show ctId
|
CRNewChatItem {} -> printLog cc CLLInfo $ "sent message to contact ID " <> show ctId
|
||||||
r -> putStrLn $ "unexpected send message response: " <> show r
|
r -> putStrLn $ "unexpected send message response: " <> show r
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ import Data.List.NonEmpty (NonEmpty)
|
|||||||
import Data.Map.Strict (Map)
|
import Data.Map.Strict (Map)
|
||||||
import Data.String
|
import Data.String
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
|
||||||
import Data.Time (NominalDiffTime)
|
import Data.Time (NominalDiffTime)
|
||||||
import Data.Time.Clock (UTCTime)
|
import Data.Time.Clock (UTCTime)
|
||||||
import Data.Version (showVersion)
|
import Data.Version (showVersion)
|
||||||
@ -242,7 +241,7 @@ data ChatCommand
|
|||||||
| APIGetChat ChatRef ChatPagination (Maybe String)
|
| APIGetChat ChatRef ChatPagination (Maybe String)
|
||||||
| APIGetChatItems ChatPagination (Maybe String)
|
| APIGetChatItems ChatPagination (Maybe String)
|
||||||
| APIGetChatItemInfo ChatRef ChatItemId
|
| APIGetChatItemInfo ChatRef ChatItemId
|
||||||
| APISendMessage {sendRef :: SendRef, liveMessage :: Bool, ttl :: Maybe Int, composedMessage :: ComposedMessage}
|
| APISendMessage {chatRef :: ChatRef, liveMessage :: Bool, ttl :: Maybe Int, composedMessage :: ComposedMessage}
|
||||||
| APIUpdateChatItem {chatRef :: ChatRef, chatItemId :: ChatItemId, liveMessage :: Bool, msgContent :: MsgContent}
|
| APIUpdateChatItem {chatRef :: ChatRef, chatItemId :: ChatItemId, liveMessage :: Bool, msgContent :: MsgContent}
|
||||||
| APIDeleteChatItem ChatRef ChatItemId CIDeleteMode
|
| APIDeleteChatItem ChatRef ChatItemId CIDeleteMode
|
||||||
| APIDeleteMemberChatItem GroupId GroupMemberId ChatItemId
|
| APIDeleteMemberChatItem GroupId GroupMemberId ChatItemId
|
||||||
@ -353,14 +352,14 @@ data ChatCommand
|
|||||||
| AddressAutoAccept (Maybe AutoAccept)
|
| AddressAutoAccept (Maybe AutoAccept)
|
||||||
| AcceptContact IncognitoEnabled ContactName
|
| AcceptContact IncognitoEnabled ContactName
|
||||||
| RejectContact ContactName
|
| RejectContact ContactName
|
||||||
| SendMessage SendName Text
|
| SendMessage ChatName Text
|
||||||
| SendLiveMessage SendName Text
|
| SendLiveMessage ChatName Text
|
||||||
| SendMessageQuote {contactName :: ContactName, msgDir :: AMsgDirection, quotedMsg :: Text, message :: Text}
|
| SendMessageQuote {contactName :: ContactName, msgDir :: AMsgDirection, quotedMsg :: Text, message :: Text}
|
||||||
| SendMessageBroadcast Text -- UserId (not used in UI)
|
| SendMessageBroadcast Text -- UserId (not used in UI)
|
||||||
| DeleteMessage ChatName Text
|
| DeleteMessage ChatName Text
|
||||||
| DeleteMemberMessage GroupName ContactName Text
|
| DeleteMemberMessage GroupName ContactName Text
|
||||||
| EditMessage {chatName :: ChatName, editedMsg :: Text, message :: Text}
|
| EditMessage {chatName :: ChatName, editedMsg :: Text, message :: Text}
|
||||||
| UpdateLiveMessage {sendName :: SendName, chatItemId :: ChatItemId, liveMessage :: Bool, message :: Text}
|
| UpdateLiveMessage {chatName :: ChatName, chatItemId :: ChatItemId, liveMessage :: Bool, message :: Text}
|
||||||
| ReactToMessage {add :: Bool, reaction :: MsgReaction, chatName :: ChatName, reactToMessage :: Text}
|
| ReactToMessage {add :: Bool, reaction :: MsgReaction, chatName :: ChatName, reactToMessage :: Text}
|
||||||
| APINewGroup UserId GroupProfile
|
| APINewGroup UserId GroupProfile
|
||||||
| NewGroup GroupProfile
|
| NewGroup GroupProfile
|
||||||
@ -382,17 +381,17 @@ data ChatCommand
|
|||||||
| GroupLinkMemberRole GroupName GroupMemberRole
|
| GroupLinkMemberRole GroupName GroupMemberRole
|
||||||
| DeleteGroupLink GroupName
|
| DeleteGroupLink GroupName
|
||||||
| ShowGroupLink GroupName
|
| ShowGroupLink GroupName
|
||||||
| SendGroupMessageQuote {groupName :: GroupName, contactName_ :: Maybe ContactName, directMemberName :: Maybe ContactName, quotedMsg :: Text, message :: Text}
|
| SendGroupMessageQuote {groupName :: GroupName, contactName_ :: Maybe ContactName, quotedMsg :: Text, message :: Text}
|
||||||
| LastChats (Maybe Int) -- UserId (not used in UI)
|
| LastChats (Maybe Int) -- UserId (not used in UI)
|
||||||
| LastMessages (Maybe ChatName) Int (Maybe String) -- UserId (not used in UI)
|
| LastMessages (Maybe ChatName) Int (Maybe String) -- UserId (not used in UI)
|
||||||
| LastChatItemId (Maybe ChatName) Int -- UserId (not used in UI)
|
| LastChatItemId (Maybe ChatName) Int -- UserId (not used in UI)
|
||||||
| ShowChatItem (Maybe ChatItemId) -- UserId (not used in UI)
|
| ShowChatItem (Maybe ChatItemId) -- UserId (not used in UI)
|
||||||
| ShowChatItemInfo ChatName Text
|
| ShowChatItemInfo ChatName Text
|
||||||
| ShowLiveItems Bool
|
| ShowLiveItems Bool
|
||||||
| SendFile SendName FilePath
|
| SendFile ChatName FilePath
|
||||||
| SendImage SendName FilePath
|
| SendImage ChatName FilePath
|
||||||
| ForwardFile SendName FileTransferId
|
| ForwardFile ChatName FileTransferId
|
||||||
| ForwardImage SendName FileTransferId
|
| ForwardImage ChatName FileTransferId
|
||||||
| SendFileDescription ChatName FilePath
|
| SendFileDescription ChatName FilePath
|
||||||
| ReceiveFile {fileId :: FileTransferId, storeEncrypted :: Bool, fileInline :: Maybe Bool, filePath :: Maybe FilePath}
|
| ReceiveFile {fileId :: FileTransferId, storeEncrypted :: Bool, fileInline :: Maybe Bool, filePath :: Maybe FilePath}
|
||||||
| SetFileToReceive {fileId :: FileTransferId, storeEncrypted :: Bool}
|
| SetFileToReceive {fileId :: FileTransferId, storeEncrypted :: Bool}
|
||||||
@ -613,37 +612,6 @@ instance ToJSON ChatResponse where
|
|||||||
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "CR"
|
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "CR"
|
||||||
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "CR"
|
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "CR"
|
||||||
|
|
||||||
data SendRef
|
|
||||||
= SRDirect ContactId
|
|
||||||
| SRGroup GroupId (Maybe GroupMemberId)
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
sendToChatRef :: SendRef -> ChatRef
|
|
||||||
sendToChatRef = \case
|
|
||||||
SRDirect cId -> ChatRef CTDirect cId
|
|
||||||
SRGroup gId _ -> ChatRef CTGroup gId
|
|
||||||
|
|
||||||
data SendName
|
|
||||||
= SNDirect ContactName
|
|
||||||
| SNGroup GroupName (Maybe ContactName)
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
sendNameStr :: SendName -> String
|
|
||||||
sendNameStr = \case
|
|
||||||
SNDirect cName -> "@" <> T.unpack cName
|
|
||||||
SNGroup gName (Just cName) -> "#" <> T.unpack gName <> " @" <> T.unpack cName
|
|
||||||
SNGroup gName Nothing -> "#" <> T.unpack gName
|
|
||||||
|
|
||||||
data SendDirection
|
|
||||||
= SDDirect Contact
|
|
||||||
| SDGroup GroupInfo [GroupMember]
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
sendDirToContactOrGroup :: SendDirection -> ContactOrGroup
|
|
||||||
sendDirToContactOrGroup = \case
|
|
||||||
SDDirect c -> CGContact c
|
|
||||||
SDGroup g _ -> CGGroup g
|
|
||||||
|
|
||||||
newtype UserPwd = UserPwd {unUserPwd :: Text}
|
newtype UserPwd = UserPwd {unUserPwd :: Text}
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
@ -959,7 +927,6 @@ data ChatErrorType
|
|||||||
| CEAgentCommandError {message :: String}
|
| CEAgentCommandError {message :: String}
|
||||||
| CEInvalidFileDescription {message :: String}
|
| CEInvalidFileDescription {message :: String}
|
||||||
| CEConnectionIncognitoChangeProhibited
|
| CEConnectionIncognitoChangeProhibited
|
||||||
| CEPeerChatVRangeIncompatible
|
|
||||||
| CEInternalError {message :: String}
|
| CEInternalError {message :: String}
|
||||||
| CEException {message :: String}
|
| CEException {message :: String}
|
||||||
deriving (Show, Exception, Generic)
|
deriving (Show, Exception, Generic)
|
||||||
|
@ -50,6 +50,16 @@ data ChatType = CTDirect | CTGroup | CTContactRequest | CTContactConnection
|
|||||||
data ChatName = ChatName ChatType Text
|
data ChatName = ChatName ChatType Text
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
|
chatTypeStr :: ChatType -> String
|
||||||
|
chatTypeStr = \case
|
||||||
|
CTDirect -> "@"
|
||||||
|
CTGroup -> "#"
|
||||||
|
CTContactRequest -> "<@"
|
||||||
|
CTContactConnection -> ":"
|
||||||
|
|
||||||
|
chatNameStr :: ChatName -> String
|
||||||
|
chatNameStr (ChatName cType name) = chatTypeStr cType <> T.unpack name
|
||||||
|
|
||||||
data ChatRef = ChatRef ChatType Int64
|
data ChatRef = ChatRef ChatType Int64
|
||||||
deriving (Eq, Show, Ord)
|
deriving (Eq, Show, Ord)
|
||||||
|
|
||||||
@ -138,16 +148,16 @@ instance MsgDirectionI d => ToJSON (ChatItem c d) where
|
|||||||
data CIDirection (c :: ChatType) (d :: MsgDirection) where
|
data CIDirection (c :: ChatType) (d :: MsgDirection) where
|
||||||
CIDirectSnd :: CIDirection 'CTDirect 'MDSnd
|
CIDirectSnd :: CIDirection 'CTDirect 'MDSnd
|
||||||
CIDirectRcv :: CIDirection 'CTDirect 'MDRcv
|
CIDirectRcv :: CIDirection 'CTDirect 'MDRcv
|
||||||
CIGroupSnd :: Maybe GroupMember -> CIDirection 'CTGroup 'MDSnd
|
CIGroupSnd :: CIDirection 'CTGroup 'MDSnd
|
||||||
CIGroupRcv :: GroupMember -> MessageScope -> CIDirection 'CTGroup 'MDRcv
|
CIGroupRcv :: GroupMember -> CIDirection 'CTGroup 'MDRcv
|
||||||
|
|
||||||
deriving instance Show (CIDirection c d)
|
deriving instance Show (CIDirection c d)
|
||||||
|
|
||||||
data JSONCIDirection
|
data JSONCIDirection
|
||||||
= JCIDirectSnd
|
= JCIDirectSnd
|
||||||
| JCIDirectRcv
|
| JCIDirectRcv
|
||||||
| JCIGroupSnd {directMember :: Maybe GroupMember}
|
| JCIGroupSnd
|
||||||
| JCIGroupRcv {groupMember :: GroupMember, messageScope :: MessageScope}
|
| JCIGroupRcv {groupMember :: GroupMember}
|
||||||
deriving (Generic, Show)
|
deriving (Generic, Show)
|
||||||
|
|
||||||
instance ToJSON JSONCIDirection where
|
instance ToJSON JSONCIDirection where
|
||||||
@ -162,19 +172,8 @@ jsonCIDirection :: CIDirection c d -> JSONCIDirection
|
|||||||
jsonCIDirection = \case
|
jsonCIDirection = \case
|
||||||
CIDirectSnd -> JCIDirectSnd
|
CIDirectSnd -> JCIDirectSnd
|
||||||
CIDirectRcv -> JCIDirectRcv
|
CIDirectRcv -> JCIDirectRcv
|
||||||
CIGroupSnd dm -> JCIGroupSnd dm
|
CIGroupSnd -> JCIGroupSnd
|
||||||
CIGroupRcv m ms -> JCIGroupRcv m ms
|
CIGroupRcv m -> JCIGroupRcv m
|
||||||
|
|
||||||
ciDirDirectMember :: CIDirection 'CTGroup d -> Maybe GroupMember
|
|
||||||
ciDirDirectMember = \case
|
|
||||||
CIGroupSnd dm -> dm
|
|
||||||
CIGroupRcv _ MSGroup -> Nothing
|
|
||||||
CIGroupRcv m MSDirect -> Just m
|
|
||||||
|
|
||||||
directMemberToMsgScope :: Maybe GroupMember -> MessageScope
|
|
||||||
directMemberToMsgScope = \case
|
|
||||||
Nothing -> MSGroup
|
|
||||||
Just _ -> MSDirect
|
|
||||||
|
|
||||||
data CIReactionCount = CIReactionCount {reaction :: MsgReaction, userReacted :: Bool, totalReacted :: Int}
|
data CIReactionCount = CIReactionCount {reaction :: MsgReaction, userReacted :: Bool, totalReacted :: Int}
|
||||||
deriving (Show, Generic)
|
deriving (Show, Generic)
|
||||||
@ -209,8 +208,8 @@ timedDeleteAt' CITimed {deleteAt} = deleteAt
|
|||||||
|
|
||||||
chatItemMember :: GroupInfo -> ChatItem 'CTGroup d -> GroupMember
|
chatItemMember :: GroupInfo -> ChatItem 'CTGroup d -> GroupMember
|
||||||
chatItemMember GroupInfo {membership} ChatItem {chatDir} = case chatDir of
|
chatItemMember GroupInfo {membership} ChatItem {chatDir} = case chatDir of
|
||||||
CIGroupSnd _ -> membership
|
CIGroupSnd -> membership
|
||||||
CIGroupRcv m _ -> m
|
CIGroupRcv m -> m
|
||||||
|
|
||||||
ciReactionAllowed :: ChatItem c d -> Bool
|
ciReactionAllowed :: ChatItem c d -> Bool
|
||||||
ciReactionAllowed ChatItem {meta = CIMeta {itemDeleted = Just _}} = False
|
ciReactionAllowed ChatItem {meta = CIMeta {itemDeleted = Just _}} = False
|
||||||
@ -239,22 +238,22 @@ chatItemDeletedState ChatItem {meta = CIMeta {itemDeleted}, content} =
|
|||||||
data ChatDirection (c :: ChatType) (d :: MsgDirection) where
|
data ChatDirection (c :: ChatType) (d :: MsgDirection) where
|
||||||
CDDirectSnd :: Contact -> ChatDirection 'CTDirect 'MDSnd
|
CDDirectSnd :: Contact -> ChatDirection 'CTDirect 'MDSnd
|
||||||
CDDirectRcv :: Contact -> ChatDirection 'CTDirect 'MDRcv
|
CDDirectRcv :: Contact -> ChatDirection 'CTDirect 'MDRcv
|
||||||
CDGroupSnd :: GroupInfo -> Maybe GroupMember -> ChatDirection 'CTGroup 'MDSnd
|
CDGroupSnd :: GroupInfo -> ChatDirection 'CTGroup 'MDSnd
|
||||||
CDGroupRcv :: GroupInfo -> GroupMember -> MessageScope -> ChatDirection 'CTGroup 'MDRcv
|
CDGroupRcv :: GroupInfo -> GroupMember -> ChatDirection 'CTGroup 'MDRcv
|
||||||
|
|
||||||
toCIDirection :: ChatDirection c d -> CIDirection c d
|
toCIDirection :: ChatDirection c d -> CIDirection c d
|
||||||
toCIDirection = \case
|
toCIDirection = \case
|
||||||
CDDirectSnd _ -> CIDirectSnd
|
CDDirectSnd _ -> CIDirectSnd
|
||||||
CDDirectRcv _ -> CIDirectRcv
|
CDDirectRcv _ -> CIDirectRcv
|
||||||
CDGroupSnd _ dm -> CIGroupSnd dm
|
CDGroupSnd _ -> CIGroupSnd
|
||||||
CDGroupRcv _ m ms -> CIGroupRcv m ms
|
CDGroupRcv _ m -> CIGroupRcv m
|
||||||
|
|
||||||
toChatInfo :: ChatDirection c d -> ChatInfo c
|
toChatInfo :: ChatDirection c d -> ChatInfo c
|
||||||
toChatInfo = \case
|
toChatInfo = \case
|
||||||
CDDirectSnd c -> DirectChat c
|
CDDirectSnd c -> DirectChat c
|
||||||
CDDirectRcv c -> DirectChat c
|
CDDirectRcv c -> DirectChat c
|
||||||
CDGroupSnd g _ -> GroupChat g
|
CDGroupSnd g -> GroupChat g
|
||||||
CDGroupRcv g _ _ -> GroupChat g
|
CDGroupRcv g _ -> GroupChat g
|
||||||
|
|
||||||
data NewChatItem d = NewChatItem
|
data NewChatItem d = NewChatItem
|
||||||
{ createdByMsgId :: Maybe MessageId,
|
{ createdByMsgId :: Maybe MessageId,
|
||||||
@ -434,39 +433,29 @@ instance ToJSON (JSONCIReaction c d) where
|
|||||||
data CIQDirection (c :: ChatType) where
|
data CIQDirection (c :: ChatType) where
|
||||||
CIQDirectSnd :: CIQDirection 'CTDirect
|
CIQDirectSnd :: CIQDirection 'CTDirect
|
||||||
CIQDirectRcv :: CIQDirection 'CTDirect
|
CIQDirectRcv :: CIQDirection 'CTDirect
|
||||||
CIQGroupSnd :: MessageScope -> CIQDirection 'CTGroup
|
CIQGroupSnd :: CIQDirection 'CTGroup
|
||||||
CIQGroupRcv :: Maybe GroupMember -> MessageScope -> CIQDirection 'CTGroup -- member can be Nothing in case MsgRef has memberId that the user is not notified about yet
|
CIQGroupRcv :: Maybe GroupMember -> CIQDirection 'CTGroup -- member can be Nothing in case MsgRef has memberId that the user is not notified about yet
|
||||||
|
|
||||||
deriving instance Show (CIQDirection c)
|
deriving instance Show (CIQDirection c)
|
||||||
|
|
||||||
data JSONCIQDirection
|
|
||||||
= JCIQDirectSnd
|
|
||||||
| JCIQDirectRcv
|
|
||||||
| JCIQGroupSnd {messageScope :: MessageScope}
|
|
||||||
| JCIQGroupRcv {groupMember :: Maybe GroupMember, messageScope :: MessageScope}
|
|
||||||
deriving (Generic, Show)
|
|
||||||
|
|
||||||
instance ToJSON JSONCIQDirection where
|
|
||||||
toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "JCIQ"
|
|
||||||
toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "JCIQ"
|
|
||||||
|
|
||||||
instance ToJSON (CIQDirection c) where
|
instance ToJSON (CIQDirection c) where
|
||||||
toJSON = J.toJSON . jsonCIQDirection
|
toJSON = J.toJSON . jsonCIQDirection
|
||||||
toEncoding = J.toEncoding . jsonCIQDirection
|
toEncoding = J.toEncoding . jsonCIQDirection
|
||||||
|
|
||||||
jsonCIQDirection :: CIQDirection c -> JSONCIQDirection
|
jsonCIQDirection :: CIQDirection c -> Maybe JSONCIDirection
|
||||||
jsonCIQDirection = \case
|
jsonCIQDirection = \case
|
||||||
CIQDirectSnd -> JCIQDirectSnd
|
CIQDirectSnd -> Just JCIDirectSnd
|
||||||
CIQDirectRcv -> JCIQDirectRcv
|
CIQDirectRcv -> Just JCIDirectRcv
|
||||||
CIQGroupSnd ms -> JCIQGroupSnd ms
|
CIQGroupSnd -> Just JCIGroupSnd
|
||||||
CIQGroupRcv m ms -> JCIQGroupRcv m ms
|
CIQGroupRcv (Just m) -> Just $ JCIGroupRcv m
|
||||||
|
CIQGroupRcv Nothing -> Nothing
|
||||||
|
|
||||||
quoteMsgDirection :: CIQDirection c -> MsgDirection
|
quoteMsgDirection :: CIQDirection c -> MsgDirection
|
||||||
quoteMsgDirection = \case
|
quoteMsgDirection = \case
|
||||||
CIQDirectSnd -> MDSnd
|
CIQDirectSnd -> MDSnd
|
||||||
CIQDirectRcv -> MDRcv
|
CIQDirectRcv -> MDRcv
|
||||||
CIQGroupSnd _ -> MDSnd
|
CIQGroupSnd -> MDSnd
|
||||||
CIQGroupRcv _ _ -> MDRcv
|
CIQGroupRcv _ -> MDRcv
|
||||||
|
|
||||||
data CIFile (d :: MsgDirection) = CIFile
|
data CIFile (d :: MsgDirection) = CIFile
|
||||||
{ fileId :: Int64,
|
{ fileId :: Int64,
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
|
||||||
|
|
||||||
module Simplex.Chat.Migrations.M20230904_item_direct_group_member_id where
|
|
||||||
|
|
||||||
import Database.SQLite.Simple (Query)
|
|
||||||
import Database.SQLite.Simple.QQ (sql)
|
|
||||||
|
|
||||||
m20230904_item_direct_group_member_id :: Query
|
|
||||||
m20230904_item_direct_group_member_id =
|
|
||||||
[sql|
|
|
||||||
ALTER TABLE chat_items ADD COLUMN item_direct_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL;
|
|
||||||
ALTER TABLE chat_items ADD COLUMN quoted_message_scope TEXT;
|
|
||||||
|
|
||||||
CREATE INDEX idx_chat_items_item_direct_group_member_id ON chat_items(item_direct_group_member_id);
|
|
||||||
|]
|
|
||||||
|
|
||||||
down_m20230904_item_direct_group_member_id :: Query
|
|
||||||
down_m20230904_item_direct_group_member_id =
|
|
||||||
[sql|
|
|
||||||
DROP INDEX idx_chat_items_item_direct_group_member_id;
|
|
||||||
|
|
||||||
ALTER TABLE chat_items DROP COLUMN quoted_message_scope;
|
|
||||||
ALTER TABLE chat_items DROP COLUMN item_direct_group_member_id;
|
|
||||||
|]
|
|
@ -392,9 +392,7 @@ CREATE TABLE chat_items(
|
|||||||
timed_delete_at TEXT,
|
timed_delete_at TEXT,
|
||||||
item_live INTEGER,
|
item_live INTEGER,
|
||||||
item_deleted_by_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL,
|
item_deleted_by_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL,
|
||||||
item_deleted_ts TEXT,
|
item_deleted_ts TEXT
|
||||||
item_direct_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL,
|
|
||||||
quoted_message_scope TEXT
|
|
||||||
);
|
);
|
||||||
CREATE TABLE chat_item_messages(
|
CREATE TABLE chat_item_messages(
|
||||||
chat_item_id INTEGER NOT NULL REFERENCES chat_items ON DELETE CASCADE,
|
chat_item_id INTEGER NOT NULL REFERENCES chat_items ON DELETE CASCADE,
|
||||||
@ -715,6 +713,3 @@ CREATE INDEX idx_chat_items_user_id_item_status ON chat_items(
|
|||||||
item_status
|
item_status
|
||||||
);
|
);
|
||||||
CREATE INDEX idx_connections_to_subscribe ON connections(to_subscribe);
|
CREATE INDEX idx_connections_to_subscribe ON connections(to_subscribe);
|
||||||
CREATE INDEX idx_chat_items_item_direct_group_member_id ON chat_items(
|
|
||||||
item_direct_group_member_id
|
|
||||||
);
|
|
||||||
|
@ -44,7 +44,7 @@ import Simplex.Chat.Types
|
|||||||
import Simplex.Chat.Types.Util
|
import Simplex.Chat.Types.Util
|
||||||
import Simplex.Messaging.Encoding
|
import Simplex.Messaging.Encoding
|
||||||
import Simplex.Messaging.Encoding.String
|
import Simplex.Messaging.Encoding.String
|
||||||
import Simplex.Messaging.Parsers (dropPrefix, enumJSON, fromTextField_, fstToLower, parseAll, sumTypeJSON, taggedObjectJSON)
|
import Simplex.Messaging.Parsers (dropPrefix, fromTextField_, fstToLower, parseAll, sumTypeJSON, taggedObjectJSON)
|
||||||
import Simplex.Messaging.Util (eitherToMaybe, safeDecodeUtf8, (<$?>))
|
import Simplex.Messaging.Util (eitherToMaybe, safeDecodeUtf8, (<$?>))
|
||||||
import Simplex.Messaging.Version hiding (version)
|
import Simplex.Messaging.Version hiding (version)
|
||||||
|
|
||||||
@ -58,10 +58,6 @@ supportedChatVRange = mkVersionRange 1 currentChatVersion
|
|||||||
groupNoDirectVRange :: VersionRange
|
groupNoDirectVRange :: VersionRange
|
||||||
groupNoDirectVRange = mkVersionRange 2 currentChatVersion
|
groupNoDirectVRange = mkVersionRange 2 currentChatVersion
|
||||||
|
|
||||||
-- version range that supports private messages from members in a group
|
|
||||||
groupPrivateMessagesVRange :: VersionRange
|
|
||||||
groupPrivateMessagesVRange = mkVersionRange 2 currentChatVersion
|
|
||||||
|
|
||||||
data ConnectionEntity
|
data ConnectionEntity
|
||||||
= RcvDirectMsgConnection {entityConnection :: Connection, contact :: Maybe Contact}
|
= RcvDirectMsgConnection {entityConnection :: Connection, contact :: Maybe Contact}
|
||||||
| RcvGroupMsgConnection {entityConnection :: Connection, groupInfo :: GroupInfo, groupMember :: GroupMember}
|
| RcvGroupMsgConnection {entityConnection :: Connection, groupInfo :: GroupInfo, groupMember :: GroupMember}
|
||||||
@ -162,28 +158,11 @@ instance ToJSON SharedMsgId where
|
|||||||
toJSON = strToJSON
|
toJSON = strToJSON
|
||||||
toEncoding = strToJEncoding
|
toEncoding = strToJEncoding
|
||||||
|
|
||||||
data MessageScope = MSGroup | MSDirect
|
|
||||||
deriving (Eq, Show, Generic)
|
|
||||||
|
|
||||||
instance FromJSON MessageScope where
|
|
||||||
parseJSON = J.genericParseJSON . enumJSON $ dropPrefix "MS"
|
|
||||||
|
|
||||||
instance ToJSON MessageScope where
|
|
||||||
toJSON = J.genericToJSON . enumJSON $ dropPrefix "MS"
|
|
||||||
toEncoding = J.genericToEncoding . enumJSON $ dropPrefix "MS"
|
|
||||||
|
|
||||||
instance ToField MessageScope where
|
|
||||||
toField = toField . encodeJSON
|
|
||||||
|
|
||||||
instance FromField MessageScope where
|
|
||||||
fromField = fromTextField_ decodeJSON
|
|
||||||
|
|
||||||
data MsgRef = MsgRef
|
data MsgRef = MsgRef
|
||||||
{ msgId :: Maybe SharedMsgId,
|
{ msgId :: Maybe SharedMsgId,
|
||||||
sentAt :: UTCTime,
|
sentAt :: UTCTime,
|
||||||
sent :: Bool,
|
sent :: Bool,
|
||||||
memberId :: Maybe MemberId, -- must be present in all group message references, both referencing sent and received
|
memberId :: Maybe MemberId -- must be present in all group message references, both referencing sent and received
|
||||||
msgScope :: Maybe MessageScope
|
|
||||||
}
|
}
|
||||||
deriving (Eq, Show, Generic)
|
deriving (Eq, Show, Generic)
|
||||||
|
|
||||||
@ -468,13 +447,7 @@ msgContentTag = \case
|
|||||||
MCFile {} -> MCFile_
|
MCFile {} -> MCFile_
|
||||||
MCUnknown {tag} -> MCUnknown_ tag
|
MCUnknown {tag} -> MCUnknown_ tag
|
||||||
|
|
||||||
data ExtMsgContent = ExtMsgContent
|
data ExtMsgContent = ExtMsgContent {content :: MsgContent, file :: Maybe FileInvitation, ttl :: Maybe Int, live :: Maybe Bool}
|
||||||
{ content :: MsgContent,
|
|
||||||
file :: Maybe FileInvitation,
|
|
||||||
ttl :: Maybe Int,
|
|
||||||
live :: Maybe Bool,
|
|
||||||
scope :: Maybe MessageScope
|
|
||||||
}
|
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
parseMsgContainer :: J.Object -> JT.Parser MsgContainer
|
parseMsgContainer :: J.Object -> JT.Parser MsgContainer
|
||||||
@ -483,10 +456,10 @@ parseMsgContainer v =
|
|||||||
<|> (v .: "forward" >>= \f -> (if f then MCForward else MCSimple) <$> mc)
|
<|> (v .: "forward" >>= \f -> (if f then MCForward else MCSimple) <$> mc)
|
||||||
<|> MCSimple <$> mc
|
<|> MCSimple <$> mc
|
||||||
where
|
where
|
||||||
mc = ExtMsgContent <$> v .: "content" <*> v .:? "file" <*> v .:? "ttl" <*> v .:? "live" <*> v .:? "scope"
|
mc = ExtMsgContent <$> v .: "content" <*> v .:? "file" <*> v .:? "ttl" <*> v .:? "live"
|
||||||
|
|
||||||
extMsgContent :: MsgContent -> Maybe FileInvitation -> ExtMsgContent
|
extMsgContent :: MsgContent -> Maybe FileInvitation -> ExtMsgContent
|
||||||
extMsgContent mc file = ExtMsgContent mc file Nothing Nothing Nothing
|
extMsgContent mc file = ExtMsgContent mc file Nothing Nothing
|
||||||
|
|
||||||
justTrue :: Bool -> Maybe Bool
|
justTrue :: Bool -> Maybe Bool
|
||||||
justTrue True = Just True
|
justTrue True = Just True
|
||||||
@ -530,7 +503,7 @@ msgContainerJSON = \case
|
|||||||
MCSimple mc -> o $ msgContent mc
|
MCSimple mc -> o $ msgContent mc
|
||||||
where
|
where
|
||||||
o = JM.fromList
|
o = JM.fromList
|
||||||
msgContent (ExtMsgContent c file ttl live scope) = ("file" .=? file) $ ("ttl" .=? ttl) $ ("live" .=? live) $ ("scope" .=? scope) ["content" .= c]
|
msgContent (ExtMsgContent c file ttl live) = ("file" .=? file) $ ("ttl" .=? ttl) $ ("live" .=? live) ["content" .= c]
|
||||||
|
|
||||||
instance ToJSON MsgContent where
|
instance ToJSON MsgContent where
|
||||||
toJSON = \case
|
toJSON = \case
|
||||||
|
@ -25,7 +25,6 @@ module Simplex.Chat.Store.Messages
|
|||||||
createRcvMsgDeliveryEvent,
|
createRcvMsgDeliveryEvent,
|
||||||
createPendingGroupMessage,
|
createPendingGroupMessage,
|
||||||
getPendingGroupMessages,
|
getPendingGroupMessages,
|
||||||
deleteMessage,
|
|
||||||
deletePendingGroupMessage,
|
deletePendingGroupMessage,
|
||||||
deleteOldMessages,
|
deleteOldMessages,
|
||||||
updateChatTs,
|
updateChatTs,
|
||||||
@ -290,10 +289,6 @@ getPendingGroupMessages db groupMemberId =
|
|||||||
pendingGroupMessage (msgId, cmEventTag, msgBody, introId_) =
|
pendingGroupMessage (msgId, cmEventTag, msgBody, introId_) =
|
||||||
PendingGroupMessage {msgId, cmEventTag, msgBody, introId_}
|
PendingGroupMessage {msgId, cmEventTag, msgBody, introId_}
|
||||||
|
|
||||||
deleteMessage :: DB.Connection -> MessageId -> IO ()
|
|
||||||
deleteMessage db msgId = do
|
|
||||||
DB.execute db "DELETE FROM messages WHERE message_id = ?" (Only msgId)
|
|
||||||
|
|
||||||
deletePendingGroupMessage :: DB.Connection -> Int64 -> MessageId -> IO ()
|
deletePendingGroupMessage :: DB.Connection -> Int64 -> MessageId -> IO ()
|
||||||
deletePendingGroupMessage db groupMemberId messageId =
|
deletePendingGroupMessage db groupMemberId messageId =
|
||||||
DB.execute db "DELETE FROM pending_group_messages WHERE group_member_id = ? AND message_id = ?" (groupMemberId, messageId)
|
DB.execute db "DELETE FROM pending_group_messages WHERE group_member_id = ? AND message_id = ?" (groupMemberId, messageId)
|
||||||
@ -302,7 +297,7 @@ deleteOldMessages :: DB.Connection -> UTCTime -> IO ()
|
|||||||
deleteOldMessages db createdAtCutoff = do
|
deleteOldMessages db createdAtCutoff = do
|
||||||
DB.execute db "DELETE FROM messages WHERE created_at <= ?" (Only createdAtCutoff)
|
DB.execute db "DELETE FROM messages WHERE created_at <= ?" (Only createdAtCutoff)
|
||||||
|
|
||||||
type NewQuoteRow = (Maybe SharedMsgId, Maybe UTCTime, Maybe MsgContent, Maybe Bool, Maybe MemberId, Maybe MessageScope)
|
type NewQuoteRow = (Maybe SharedMsgId, Maybe UTCTime, Maybe MsgContent, Maybe Bool, Maybe MemberId)
|
||||||
|
|
||||||
updateChatTs :: DB.Connection -> User -> ChatDirection c d -> UTCTime -> IO ()
|
updateChatTs :: DB.Connection -> User -> ChatDirection c d -> UTCTime -> IO ()
|
||||||
updateChatTs db User {userId} chatDirection chatTs = case toChatInfo chatDirection of
|
updateChatTs db User {userId} chatDirection chatTs = case toChatInfo chatDirection of
|
||||||
@ -325,15 +320,14 @@ createNewSndChatItem db user chatDirection SndMessage {msgId, sharedMsgId} ciCon
|
|||||||
createdByMsgId = if msgId == 0 then Nothing else Just msgId
|
createdByMsgId = if msgId == 0 then Nothing else Just msgId
|
||||||
quoteRow :: NewQuoteRow
|
quoteRow :: NewQuoteRow
|
||||||
quoteRow = case quotedItem of
|
quoteRow = case quotedItem of
|
||||||
Nothing -> (Nothing, Nothing, Nothing, Nothing, Nothing, Nothing)
|
Nothing -> (Nothing, Nothing, Nothing, Nothing, Nothing)
|
||||||
Just CIQuote {chatDir, sharedMsgId = quotedSharedMsgId, sentAt, content} -> do
|
Just CIQuote {chatDir, sharedMsgId = quotedSharedMsgId, sentAt, content} ->
|
||||||
let (quotedSent, quotedMemberId, quotedMsgScope) = case chatDir of
|
uncurry (quotedSharedMsgId,Just sentAt,Just content,,) $ case chatDir of
|
||||||
CIQDirectSnd -> (Just True, Nothing, Nothing)
|
CIQDirectSnd -> (Just True, Nothing)
|
||||||
CIQDirectRcv -> (Just False, Nothing, Nothing)
|
CIQDirectRcv -> (Just False, Nothing)
|
||||||
CIQGroupSnd messageScope -> (Just True, Nothing, Just messageScope)
|
CIQGroupSnd -> (Just True, Nothing)
|
||||||
CIQGroupRcv (Just GroupMember {memberId}) messageScope -> (Just False, Just memberId, Just messageScope)
|
CIQGroupRcv (Just GroupMember {memberId}) -> (Just False, Just memberId)
|
||||||
CIQGroupRcv Nothing messageScope -> (Just False, Nothing, Just messageScope)
|
CIQGroupRcv Nothing -> (Just False, Nothing)
|
||||||
(quotedSharedMsgId, Just sentAt, Just content, quotedSent, quotedMemberId, quotedMsgScope)
|
|
||||||
|
|
||||||
createNewRcvChatItem :: DB.Connection -> User -> ChatDirection c 'MDRcv -> RcvMessage -> Maybe SharedMsgId -> CIContent 'MDRcv -> Maybe CITimed -> Bool -> UTCTime -> UTCTime -> IO (ChatItemId, Maybe (CIQuote c))
|
createNewRcvChatItem :: DB.Connection -> User -> ChatDirection c 'MDRcv -> RcvMessage -> Maybe SharedMsgId -> CIContent 'MDRcv -> Maybe CITimed -> Bool -> UTCTime -> UTCTime -> IO (ChatItemId, Maybe (CIQuote c))
|
||||||
createNewRcvChatItem db user chatDirection RcvMessage {msgId, chatMsgEvent} sharedMsgId_ ciContent timed live itemTs createdAt = do
|
createNewRcvChatItem db user chatDirection RcvMessage {msgId, chatMsgEvent} sharedMsgId_ ciContent timed live itemTs createdAt = do
|
||||||
@ -344,20 +338,19 @@ createNewRcvChatItem db user chatDirection RcvMessage {msgId, chatMsgEvent} shar
|
|||||||
quotedMsg = cmToQuotedMsg chatMsgEvent
|
quotedMsg = cmToQuotedMsg chatMsgEvent
|
||||||
quoteRow :: NewQuoteRow
|
quoteRow :: NewQuoteRow
|
||||||
quoteRow = case quotedMsg of
|
quoteRow = case quotedMsg of
|
||||||
Nothing -> (Nothing, Nothing, Nothing, Nothing, Nothing, Nothing)
|
Nothing -> (Nothing, Nothing, Nothing, Nothing, Nothing)
|
||||||
Just QuotedMsg {msgRef = MsgRef {msgId = sharedMsgId, sentAt, sent, memberId, msgScope}, content} -> do
|
Just QuotedMsg {msgRef = MsgRef {msgId = sharedMsgId, sentAt, sent, memberId}, content} ->
|
||||||
let (quotedSent, quotedMemberId, quotedMsgScope) = case chatDirection of
|
uncurry (sharedMsgId,Just sentAt,Just content,,) $ case chatDirection of
|
||||||
CDDirectRcv _ -> (Just $ not sent, Nothing, Nothing)
|
CDDirectRcv _ -> (Just $ not sent, Nothing)
|
||||||
CDGroupRcv GroupInfo {membership = GroupMember {memberId = userMemberId}} _ _ ->
|
CDGroupRcv GroupInfo {membership = GroupMember {memberId = userMemberId}} _ ->
|
||||||
(Just $ Just userMemberId == memberId, memberId, msgScope)
|
(Just $ Just userMemberId == memberId, memberId)
|
||||||
(sharedMsgId, Just sentAt, Just content, quotedSent, quotedMemberId, quotedMsgScope)
|
|
||||||
|
|
||||||
createNewChatItemNoMsg :: forall c d. MsgDirectionI d => DB.Connection -> User -> ChatDirection c d -> CIContent d -> UTCTime -> UTCTime -> IO ChatItemId
|
createNewChatItemNoMsg :: forall c d. MsgDirectionI d => DB.Connection -> User -> ChatDirection c d -> CIContent d -> UTCTime -> UTCTime -> IO ChatItemId
|
||||||
createNewChatItemNoMsg db user chatDirection ciContent =
|
createNewChatItemNoMsg db user chatDirection ciContent =
|
||||||
createNewChatItem_ db user chatDirection Nothing Nothing ciContent quoteRow Nothing False
|
createNewChatItem_ db user chatDirection Nothing Nothing ciContent quoteRow Nothing False
|
||||||
where
|
where
|
||||||
quoteRow :: NewQuoteRow
|
quoteRow :: NewQuoteRow
|
||||||
quoteRow = (Nothing, Nothing, Nothing, Nothing, Nothing, Nothing)
|
quoteRow = (Nothing, Nothing, Nothing, Nothing, Nothing)
|
||||||
|
|
||||||
createNewChatItem_ :: forall c d. MsgDirectionI d => DB.Connection -> User -> ChatDirection c d -> Maybe MessageId -> Maybe SharedMsgId -> CIContent d -> NewQuoteRow -> Maybe CITimed -> Bool -> UTCTime -> UTCTime -> IO ChatItemId
|
createNewChatItem_ :: forall c d. MsgDirectionI d => DB.Connection -> User -> ChatDirection c d -> Maybe MessageId -> Maybe SharedMsgId -> CIContent d -> NewQuoteRow -> Maybe CITimed -> Bool -> UTCTime -> UTCTime -> IO ChatItemId
|
||||||
createNewChatItem_ db User {userId} chatDirection msgId_ sharedMsgId ciContent quoteRow timed live itemTs createdAt = do
|
createNewChatItem_ db User {userId} chatDirection msgId_ sharedMsgId ciContent quoteRow timed live itemTs createdAt = do
|
||||||
@ -366,12 +359,12 @@ createNewChatItem_ db User {userId} chatDirection msgId_ sharedMsgId ciContent q
|
|||||||
[sql|
|
[sql|
|
||||||
INSERT INTO chat_items (
|
INSERT INTO chat_items (
|
||||||
-- user and IDs
|
-- user and IDs
|
||||||
user_id, created_by_msg_id, contact_id, group_id, group_member_id, item_direct_group_member_id,
|
user_id, created_by_msg_id, contact_id, group_id, group_member_id,
|
||||||
-- meta
|
-- meta
|
||||||
item_sent, item_ts, item_content, item_text, item_status, shared_msg_id, created_at, updated_at, item_live, timed_ttl, timed_delete_at,
|
item_sent, item_ts, item_content, item_text, item_status, shared_msg_id, created_at, updated_at, item_live, timed_ttl, timed_delete_at,
|
||||||
-- quote
|
-- quote
|
||||||
quoted_shared_msg_id, quoted_sent_at, quoted_content, quoted_sent, quoted_member_id, quoted_message_scope
|
quoted_shared_msg_id, quoted_sent_at, quoted_content, quoted_sent, quoted_member_id
|
||||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||||
|]
|
|]
|
||||||
((userId, msgId_) :. idsRow :. itemRow :. quoteRow)
|
((userId, msgId_) :. idsRow :. itemRow :. quoteRow)
|
||||||
ciId <- insertedRowId db
|
ciId <- insertedRowId db
|
||||||
@ -380,16 +373,12 @@ createNewChatItem_ db User {userId} chatDirection msgId_ sharedMsgId ciContent q
|
|||||||
where
|
where
|
||||||
itemRow :: (SMsgDirection d, UTCTime, CIContent d, Text, CIStatus d, Maybe SharedMsgId) :. (UTCTime, UTCTime, Maybe Bool) :. (Maybe Int, Maybe UTCTime)
|
itemRow :: (SMsgDirection d, UTCTime, CIContent d, Text, CIStatus d, Maybe SharedMsgId) :. (UTCTime, UTCTime, Maybe Bool) :. (Maybe Int, Maybe UTCTime)
|
||||||
itemRow = (msgDirection @d, itemTs, ciContent, ciContentToText ciContent, ciCreateStatus ciContent, sharedMsgId) :. (createdAt, createdAt, justTrue live) :. ciTimedRow timed
|
itemRow = (msgDirection @d, itemTs, ciContent, ciContentToText ciContent, ciCreateStatus ciContent, sharedMsgId) :. (createdAt, createdAt, justTrue live) :. ciTimedRow timed
|
||||||
idsRow :: (Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64)
|
idsRow :: (Maybe Int64, Maybe Int64, Maybe Int64)
|
||||||
idsRow = case chatDirection of
|
idsRow = case chatDirection of
|
||||||
CDDirectRcv Contact {contactId} -> (Just contactId, Nothing, Nothing, Nothing)
|
CDDirectRcv Contact {contactId} -> (Just contactId, Nothing, Nothing)
|
||||||
CDDirectSnd Contact {contactId} -> (Just contactId, Nothing, Nothing, Nothing)
|
CDDirectSnd Contact {contactId} -> (Just contactId, Nothing, Nothing)
|
||||||
CDGroupRcv GroupInfo {groupId} GroupMember {groupMemberId} messageScope -> case messageScope of
|
CDGroupRcv GroupInfo {groupId} GroupMember {groupMemberId} -> (Nothing, Just groupId, Just groupMemberId)
|
||||||
MSGroup -> (Nothing, Just groupId, Just groupMemberId, Nothing)
|
CDGroupSnd GroupInfo {groupId} -> (Nothing, Just groupId, Nothing)
|
||||||
MSDirect -> (Nothing, Just groupId, Just groupMemberId, Just groupMemberId)
|
|
||||||
CDGroupSnd GroupInfo {groupId} directMember -> case directMember of
|
|
||||||
Nothing -> (Nothing, Just groupId, Nothing, Nothing)
|
|
||||||
Just GroupMember {groupMemberId} -> (Nothing, Just groupId, Nothing, Just groupMemberId)
|
|
||||||
|
|
||||||
ciTimedRow :: Maybe CITimed -> (Maybe Int, Maybe UTCTime)
|
ciTimedRow :: Maybe CITimed -> (Maybe Int, Maybe UTCTime)
|
||||||
ciTimedRow (Just CITimed {ttl, deleteAt}) = (Just ttl, deleteAt)
|
ciTimedRow (Just CITimed {ttl, deleteAt}) = (Just ttl, deleteAt)
|
||||||
@ -399,21 +388,19 @@ insertChatItemMessage_ :: DB.Connection -> ChatItemId -> MessageId -> UTCTime ->
|
|||||||
insertChatItemMessage_ db ciId msgId ts = DB.execute db "INSERT INTO chat_item_messages (chat_item_id, message_id, created_at, updated_at) VALUES (?,?,?,?)" (ciId, msgId, ts, ts)
|
insertChatItemMessage_ db ciId msgId ts = DB.execute db "INSERT INTO chat_item_messages (chat_item_id, message_id, created_at, updated_at) VALUES (?,?,?,?)" (ciId, msgId, ts, ts)
|
||||||
|
|
||||||
getChatItemQuote_ :: DB.Connection -> User -> ChatDirection c 'MDRcv -> QuotedMsg -> IO (CIQuote c)
|
getChatItemQuote_ :: DB.Connection -> User -> ChatDirection c 'MDRcv -> QuotedMsg -> IO (CIQuote c)
|
||||||
getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRef = MsgRef {msgId, sentAt, sent, memberId, msgScope}, content} =
|
getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRef = MsgRef {msgId, sentAt, sent, memberId}, content} =
|
||||||
case chatDirection of
|
case chatDirection of
|
||||||
CDDirectRcv Contact {contactId} -> getDirectChatItemQuote_ contactId (not sent)
|
CDDirectRcv Contact {contactId} -> getDirectChatItemQuote_ contactId (not sent)
|
||||||
CDGroupRcv GroupInfo {groupId, membership = GroupMember {memberId = userMemberId}} sender@GroupMember {memberId = senderMemberId} _directMember ->
|
CDGroupRcv GroupInfo {groupId, membership = GroupMember {memberId = userMemberId}} sender@GroupMember {memberId = senderMemberId} ->
|
||||||
case memberId of
|
case memberId of
|
||||||
Just mId
|
Just mId
|
||||||
| mId == userMemberId -> (`ciQuote` CIQGroupSnd messageScope) <$> getUserGroupChatItemId_ groupId
|
| mId == userMemberId -> (`ciQuote` CIQGroupSnd) <$> getUserGroupChatItemId_ groupId
|
||||||
| mId == senderMemberId -> (`ciQuote` CIQGroupRcv (Just sender) messageScope) <$> getGroupChatItemId_ groupId mId
|
| mId == senderMemberId -> (`ciQuote` CIQGroupRcv (Just sender)) <$> getGroupChatItemId_ groupId mId
|
||||||
| otherwise -> getGroupChatItemQuote_ groupId mId
|
| otherwise -> getGroupChatItemQuote_ groupId mId
|
||||||
_ -> pure . ciQuote Nothing $ CIQGroupRcv Nothing messageScope
|
_ -> pure . ciQuote Nothing $ CIQGroupRcv Nothing
|
||||||
where
|
where
|
||||||
ciQuote :: Maybe ChatItemId -> CIQDirection c -> CIQuote c
|
ciQuote :: Maybe ChatItemId -> CIQDirection c -> CIQuote c
|
||||||
ciQuote itemId dir = CIQuote dir itemId msgId sentAt content . parseMaybeMarkdownList $ msgContentText content
|
ciQuote itemId dir = CIQuote dir itemId msgId sentAt content . parseMaybeMarkdownList $ msgContentText content
|
||||||
messageScope :: MessageScope
|
|
||||||
messageScope = fromMaybe MSGroup msgScope
|
|
||||||
getDirectChatItemQuote_ :: Int64 -> Bool -> IO (CIQuote 'CTDirect)
|
getDirectChatItemQuote_ :: Int64 -> Bool -> IO (CIQuote 'CTDirect)
|
||||||
getDirectChatItemQuote_ contactId userSent = do
|
getDirectChatItemQuote_ contactId userSent = do
|
||||||
fmap ciQuoteDirect . maybeFirstRow fromOnly $
|
fmap ciQuoteDirect . maybeFirstRow fromOnly $
|
||||||
@ -460,8 +447,8 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
|||||||
[":user_id" := userId, ":group_id" := groupId, ":member_id" := mId, ":msg_id" := msgId]
|
[":user_id" := userId, ":group_id" := groupId, ":member_id" := mId, ":msg_id" := msgId]
|
||||||
where
|
where
|
||||||
ciQuoteGroup :: [Only (Maybe ChatItemId) :. GroupMemberRow] -> CIQuote 'CTGroup
|
ciQuoteGroup :: [Only (Maybe ChatItemId) :. GroupMemberRow] -> CIQuote 'CTGroup
|
||||||
ciQuoteGroup [] = ciQuote Nothing $ CIQGroupRcv Nothing messageScope
|
ciQuoteGroup [] = ciQuote Nothing $ CIQGroupRcv Nothing
|
||||||
ciQuoteGroup ((Only itemId :. memberRow) : _) = ciQuote itemId $ CIQGroupRcv (Just $ toGroupMember userContactId memberRow) messageScope
|
ciQuoteGroup ((Only itemId :. memberRow) : _) = ciQuote itemId . CIQGroupRcv . Just $ toGroupMember userContactId memberRow
|
||||||
|
|
||||||
getChatPreviews :: DB.Connection -> User -> Bool -> IO [AChat]
|
getChatPreviews :: DB.Connection -> User -> Bool -> IO [AChat]
|
||||||
getChatPreviews db user withPCC = do
|
getChatPreviews db user withPCC = do
|
||||||
@ -569,7 +556,7 @@ getGroupChatPreviews_ db User {userId, userContactId} = do
|
|||||||
m.member_status, m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
m.member_status, m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
||||||
p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
|
p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
|
||||||
-- quoted ChatItem
|
-- quoted ChatItem
|
||||||
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent, i.quoted_message_scope,
|
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
|
||||||
-- quoted GroupMember
|
-- quoted GroupMember
|
||||||
rm.group_member_id, rm.group_id, rm.member_id, rm.member_role, rm.member_category,
|
rm.group_member_id, rm.group_id, rm.member_id, rm.member_role, rm.member_category,
|
||||||
rm.member_status, rm.invited_by, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
|
rm.member_status, rm.invited_by, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
|
||||||
@ -577,11 +564,7 @@ getGroupChatPreviews_ db User {userId, userContactId} = do
|
|||||||
-- deleted by GroupMember
|
-- deleted by GroupMember
|
||||||
dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.member_role, dbm.member_category,
|
dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.member_role, dbm.member_category,
|
||||||
dbm.member_status, dbm.invited_by, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
|
dbm.member_status, dbm.invited_by, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
|
||||||
dbp.display_name, dbp.full_name, dbp.image, dbp.contact_link, dbp.local_alias, dbp.preferences,
|
dbp.display_name, dbp.full_name, dbp.image, dbp.contact_link, dbp.local_alias, dbp.preferences
|
||||||
-- direct GroupMember
|
|
||||||
dirm.group_member_id, dirm.group_id, dirm.member_id, dirm.member_role, dirm.member_category,
|
|
||||||
dirm.member_status, dirm.invited_by, dirm.local_display_name, dirm.contact_id, dirm.contact_profile_id, dirp.contact_profile_id,
|
|
||||||
dirp.display_name, dirp.full_name, dirp.image, dirp.contact_link, dirp.local_alias, dirp.preferences
|
|
||||||
FROM groups g
|
FROM groups g
|
||||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||||
JOIN group_members mu ON mu.group_id = g.group_id
|
JOIN group_members mu ON mu.group_id = g.group_id
|
||||||
@ -607,8 +590,6 @@ getGroupChatPreviews_ db User {userId, userContactId} = do
|
|||||||
LEFT JOIN contact_profiles rp ON rp.contact_profile_id = COALESCE(rm.member_profile_id, rm.contact_profile_id)
|
LEFT JOIN contact_profiles rp ON rp.contact_profile_id = COALESCE(rm.member_profile_id, rm.contact_profile_id)
|
||||||
LEFT JOIN group_members dbm ON dbm.group_member_id = i.item_deleted_by_group_member_id
|
LEFT JOIN group_members dbm ON dbm.group_member_id = i.item_deleted_by_group_member_id
|
||||||
LEFT JOIN contact_profiles dbp ON dbp.contact_profile_id = COALESCE(dbm.member_profile_id, dbm.contact_profile_id)
|
LEFT JOIN contact_profiles dbp ON dbp.contact_profile_id = COALESCE(dbm.member_profile_id, dbm.contact_profile_id)
|
||||||
LEFT JOIN group_members dirm ON dirm.group_member_id = i.item_direct_group_member_id
|
|
||||||
LEFT JOIN contact_profiles dirp ON dirp.contact_profile_id = COALESCE(dirm.member_profile_id, dirm.contact_profile_id)
|
|
||||||
WHERE g.user_id = ? AND mu.contact_id = ?
|
WHERE g.user_id = ? AND mu.contact_id = ?
|
||||||
ORDER BY i.item_ts DESC
|
ORDER BY i.item_ts DESC
|
||||||
|]
|
|]
|
||||||
@ -986,8 +967,10 @@ toDirectQuote :: QuoteRow -> Maybe (CIQuote 'CTDirect)
|
|||||||
toDirectQuote qr@(_, _, _, _, quotedSent) = toQuote qr $ direction <$> quotedSent
|
toDirectQuote qr@(_, _, _, _, quotedSent) = toQuote qr $ direction <$> quotedSent
|
||||||
where
|
where
|
||||||
direction sent = if sent then CIQDirectSnd else CIQDirectRcv
|
direction sent = if sent then CIQDirectSnd else CIQDirectRcv
|
||||||
toQuote (quotedItemId, quotedSharedMsgId, quotedSentAt, quotedMsgContent, _) dir =
|
|
||||||
CIQuote <$> dir <*> pure quotedItemId <*> pure quotedSharedMsgId <*> quotedSentAt <*> quotedMsgContent <*> (parseMaybeMarkdownList . msgContentText <$> quotedMsgContent)
|
toQuote :: QuoteRow -> Maybe (CIQDirection c) -> Maybe (CIQuote c)
|
||||||
|
toQuote (quotedItemId, quotedSharedMsgId, quotedSentAt, quotedMsgContent, _) dir =
|
||||||
|
CIQuote <$> dir <*> pure quotedItemId <*> pure quotedSharedMsgId <*> quotedSentAt <*> quotedMsgContent <*> (parseMaybeMarkdownList . msgContentText <$> quotedMsgContent)
|
||||||
|
|
||||||
-- this function can be changed so it never fails, not only avoid failure on invalid json
|
-- this function can be changed so it never fails, not only avoid failure on invalid json
|
||||||
toDirectChatItem :: UTCTime -> ChatItemRow :. QuoteRow -> Either StoreError (CChatItem 'CTDirect)
|
toDirectChatItem :: UTCTime -> ChatItemRow :. QuoteRow -> Either StoreError (CChatItem 'CTDirect)
|
||||||
@ -1030,60 +1013,37 @@ toDirectChatItemList currentTs (((Just itemId, Just itemTs, Just msgDir, Just it
|
|||||||
either (const []) (: []) $ toDirectChatItem currentTs (((itemId, itemTs, msgDir, itemContent, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. fileRow) :. quoteRow)
|
either (const []) (: []) $ toDirectChatItem currentTs (((itemId, itemTs, msgDir, itemContent, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. fileRow) :. quoteRow)
|
||||||
toDirectChatItemList _ _ = []
|
toDirectChatItemList _ _ = []
|
||||||
|
|
||||||
type GroupQuoteRow = (Maybe ChatItemId, Maybe SharedMsgId, Maybe UTCTime, Maybe MsgContent, Maybe Bool, Maybe MessageScope)
|
type GroupQuoteRow = QuoteRow :. MaybeGroupMemberRow
|
||||||
|
|
||||||
type GroupQuoteMemberRow = GroupQuoteRow :. MaybeGroupMemberRow
|
type MaybeGroupChatItemRow = MaybeChatItemRow :. MaybeGroupMemberRow :. GroupQuoteRow :. MaybeGroupMemberRow
|
||||||
|
|
||||||
type MaybeGroupChatItemRow = MaybeChatItemRow :. MaybeGroupMemberRow :. GroupQuoteMemberRow :. MaybeGroupMemberRow :. MaybeGroupMemberRow
|
toGroupQuote :: QuoteRow -> Maybe GroupMember -> Maybe (CIQuote 'CTGroup)
|
||||||
|
toGroupQuote qr@(_, _, _, _, quotedSent) quotedMember_ = toQuote qr $ direction quotedSent quotedMember_
|
||||||
toGroupQuote :: GroupQuoteRow -> Maybe GroupMember -> Maybe (CIQuote 'CTGroup)
|
|
||||||
toGroupQuote qr@(_, _, _, _, quotedSent, msgScope) quotedMember_ =
|
|
||||||
toQuote qr $ direction quotedSent quotedMember_
|
|
||||||
where
|
where
|
||||||
direction (Just True) _ = Just $ CIQGroupSnd messageScope
|
direction (Just True) _ = Just CIQGroupSnd
|
||||||
direction (Just False) (Just member) = Just $ CIQGroupRcv (Just member) messageScope
|
direction (Just False) (Just member) = Just . CIQGroupRcv $ Just member
|
||||||
direction (Just False) Nothing = Just $ CIQGroupRcv Nothing messageScope
|
direction (Just False) Nothing = Just $ CIQGroupRcv Nothing
|
||||||
direction _ _ = Nothing
|
direction _ _ = Nothing
|
||||||
messageScope = fromMaybe MSGroup msgScope
|
|
||||||
toQuote (quotedItemId, quotedSharedMsgId, quotedSentAt, quotedMsgContent, _, _) dir =
|
|
||||||
CIQuote <$> dir <*> pure quotedItemId <*> pure quotedSharedMsgId <*> quotedSentAt <*> quotedMsgContent <*> (parseMaybeMarkdownList . msgContentText <$> quotedMsgContent)
|
|
||||||
|
|
||||||
-- this function can be changed so it never fails, not only avoid failure on invalid json
|
-- this function can be changed so it never fails, not only avoid failure on invalid json
|
||||||
toGroupChatItem :: UTCTime -> Int64 -> ChatItemRow :. MaybeGroupMemberRow :. GroupQuoteMemberRow :. MaybeGroupMemberRow :. MaybeGroupMemberRow -> Either StoreError (CChatItem 'CTGroup)
|
toGroupChatItem :: UTCTime -> Int64 -> ChatItemRow :. MaybeGroupMemberRow :. GroupQuoteRow :. MaybeGroupMemberRow -> Either StoreError (CChatItem 'CTGroup)
|
||||||
toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir, itemContentText, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileKey, fileNonce, fileStatus_, fileProtocol_)) :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_ :. directMemberRow_) = do
|
toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir, itemContentText, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileKey, fileNonce, fileStatus_, fileProtocol_)) :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_) = do
|
||||||
chatItem $ fromRight invalid $ dbParseACIContent itemContentText
|
chatItem $ fromRight invalid $ dbParseACIContent itemContentText
|
||||||
where
|
where
|
||||||
member_ = toMaybeGroupMember userContactId memberRow_
|
member_ = toMaybeGroupMember userContactId memberRow_
|
||||||
quotedMember_ = toMaybeGroupMember userContactId quotedMemberRow_
|
quotedMember_ = toMaybeGroupMember userContactId quotedMemberRow_
|
||||||
deletedByGroupMember_ = toMaybeGroupMember userContactId deletedByGroupMemberRow_
|
deletedByGroupMember_ = toMaybeGroupMember userContactId deletedByGroupMemberRow_
|
||||||
directMember_ = toMaybeGroupMember userContactId directMemberRow_
|
|
||||||
invalid = ACIContent msgDir $ CIInvalidJSON itemContentText
|
invalid = ACIContent msgDir $ CIInvalidJSON itemContentText
|
||||||
chatItem itemContent = case (itemContent, itemStatus, member_, fileStatus_) of
|
chatItem itemContent = case (itemContent, itemStatus, member_, fileStatus_) of
|
||||||
(ACIContent SMDSnd ciContent, ACIStatus SMDSnd ciStatus, _, Just (AFS SMDSnd fileStatus)) ->
|
(ACIContent SMDSnd ciContent, ACIStatus SMDSnd ciStatus, _, Just (AFS SMDSnd fileStatus)) ->
|
||||||
Right $ cItem SMDSnd (CIGroupSnd directMember_) ciStatus ciContent (maybeCIFile fileStatus)
|
Right $ cItem SMDSnd CIGroupSnd ciStatus ciContent (maybeCIFile fileStatus)
|
||||||
(ACIContent SMDSnd ciContent, ACIStatus SMDSnd ciStatus, _, Nothing) ->
|
(ACIContent SMDSnd ciContent, ACIStatus SMDSnd ciStatus, _, Nothing) ->
|
||||||
Right $ cItem SMDSnd (CIGroupSnd directMember_) ciStatus ciContent Nothing
|
Right $ cItem SMDSnd CIGroupSnd ciStatus ciContent Nothing
|
||||||
-- read of group chat item can be refactored so that direct member is not read for rcv items:
|
|
||||||
-- if item_direct_group_member_id is equal to group_member_id, then message scope is direct
|
|
||||||
(ACIContent SMDRcv ciContent, ACIStatus SMDRcv ciStatus, Just member, Just (AFS SMDRcv fileStatus)) ->
|
(ACIContent SMDRcv ciContent, ACIStatus SMDRcv ciStatus, Just member, Just (AFS SMDRcv fileStatus)) ->
|
||||||
case directMember_ of
|
Right $ cItem SMDRcv (CIGroupRcv member) ciStatus ciContent (maybeCIFile fileStatus)
|
||||||
Just directMember
|
|
||||||
| sameMember member directMember ->
|
|
||||||
Right $ cItem SMDRcv (CIGroupRcv member MSDirect) ciStatus ciContent (maybeCIFile fileStatus)
|
|
||||||
| otherwise -> badItem
|
|
||||||
Nothing ->
|
|
||||||
Right $ cItem SMDRcv (CIGroupRcv member MSGroup) ciStatus ciContent (maybeCIFile fileStatus)
|
|
||||||
(ACIContent SMDRcv ciContent, ACIStatus SMDRcv ciStatus, Just member, Nothing) ->
|
(ACIContent SMDRcv ciContent, ACIStatus SMDRcv ciStatus, Just member, Nothing) ->
|
||||||
case directMember_ of
|
Right $ cItem SMDRcv (CIGroupRcv member) ciStatus ciContent Nothing
|
||||||
Just directMember
|
|
||||||
| sameMember member directMember ->
|
|
||||||
Right $ cItem SMDRcv (CIGroupRcv member MSDirect) ciStatus ciContent Nothing
|
|
||||||
| otherwise -> badItem
|
|
||||||
Nothing ->
|
|
||||||
Right $ cItem SMDRcv (CIGroupRcv member MSGroup) ciStatus ciContent Nothing
|
|
||||||
_ -> badItem
|
_ -> badItem
|
||||||
sameMember :: GroupMember -> GroupMember -> Bool
|
|
||||||
sameMember GroupMember {groupMemberId = gmId1} GroupMember {groupMemberId = gmId2} = gmId1 == gmId2
|
|
||||||
maybeCIFile :: CIFileStatus d -> Maybe (CIFile d)
|
maybeCIFile :: CIFileStatus d -> Maybe (CIFile d)
|
||||||
maybeCIFile fileStatus =
|
maybeCIFile fileStatus =
|
||||||
case (fileId_, fileName_, fileSize_, fileProtocol_) of
|
case (fileId_, fileName_, fileSize_, fileProtocol_) of
|
||||||
@ -1108,8 +1068,8 @@ toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir,
|
|||||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||||
|
|
||||||
toGroupChatItemList :: UTCTime -> Int64 -> MaybeGroupChatItemRow -> [CChatItem 'CTGroup]
|
toGroupChatItemList :: UTCTime -> Int64 -> MaybeGroupChatItemRow -> [CChatItem 'CTGroup]
|
||||||
toGroupChatItemList currentTs userContactId (((Just itemId, Just itemTs, Just msgDir, Just itemContent, Just itemText, Just itemStatus, sharedMsgId) :. (Just itemDeleted, deletedTs, itemEdited, Just createdAt, Just updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. fileRow) :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_ :. directMemberRow_) =
|
toGroupChatItemList currentTs userContactId (((Just itemId, Just itemTs, Just msgDir, Just itemContent, Just itemText, Just itemStatus, sharedMsgId) :. (Just itemDeleted, deletedTs, itemEdited, Just createdAt, Just updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. fileRow) :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_) =
|
||||||
either (const []) (: []) $ toGroupChatItem currentTs userContactId (((itemId, itemTs, msgDir, itemContent, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. fileRow) :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_ :. directMemberRow_)
|
either (const []) (: []) $ toGroupChatItem currentTs userContactId (((itemId, itemTs, msgDir, itemContent, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. fileRow) :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_)
|
||||||
toGroupChatItemList _ _ _ = []
|
toGroupChatItemList _ _ _ = []
|
||||||
|
|
||||||
getAllChatItems :: DB.Connection -> User -> ChatPagination -> Maybe String -> ExceptT StoreError IO [AChatItem]
|
getAllChatItems :: DB.Connection -> User -> ChatPagination -> Maybe String -> ExceptT StoreError IO [AChatItem]
|
||||||
@ -1524,7 +1484,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
|||||||
m.member_status, m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
m.member_status, m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
||||||
p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
|
p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
|
||||||
-- quoted ChatItem
|
-- quoted ChatItem
|
||||||
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent, i.quoted_message_scope,
|
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
|
||||||
-- quoted GroupMember
|
-- quoted GroupMember
|
||||||
rm.group_member_id, rm.group_id, rm.member_id, rm.member_role, rm.member_category,
|
rm.group_member_id, rm.group_id, rm.member_id, rm.member_role, rm.member_category,
|
||||||
rm.member_status, rm.invited_by, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
|
rm.member_status, rm.invited_by, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
|
||||||
@ -1532,11 +1492,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
|||||||
-- deleted by GroupMember
|
-- deleted by GroupMember
|
||||||
dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.member_role, dbm.member_category,
|
dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.member_role, dbm.member_category,
|
||||||
dbm.member_status, dbm.invited_by, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
|
dbm.member_status, dbm.invited_by, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
|
||||||
dbp.display_name, dbp.full_name, dbp.image, dbp.contact_link, dbp.local_alias, dbp.preferences,
|
dbp.display_name, dbp.full_name, dbp.image, dbp.contact_link, dbp.local_alias, dbp.preferences
|
||||||
-- direct GroupMember
|
|
||||||
dirm.group_member_id, dirm.group_id, dirm.member_id, dirm.member_role, dirm.member_category,
|
|
||||||
dirm.member_status, dirm.invited_by, dirm.local_display_name, dirm.contact_id, dirm.contact_profile_id, dirp.contact_profile_id,
|
|
||||||
dirp.display_name, dirp.full_name, dirp.image, dirp.contact_link, dirp.local_alias, dirp.preferences
|
|
||||||
FROM chat_items i
|
FROM chat_items i
|
||||||
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
|
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
|
||||||
LEFT JOIN group_members m ON m.group_member_id = i.group_member_id
|
LEFT JOIN group_members m ON m.group_member_id = i.group_member_id
|
||||||
@ -1546,8 +1502,6 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
|||||||
LEFT JOIN contact_profiles rp ON rp.contact_profile_id = COALESCE(rm.member_profile_id, rm.contact_profile_id)
|
LEFT JOIN contact_profiles rp ON rp.contact_profile_id = COALESCE(rm.member_profile_id, rm.contact_profile_id)
|
||||||
LEFT JOIN group_members dbm ON dbm.group_member_id = i.item_deleted_by_group_member_id
|
LEFT JOIN group_members dbm ON dbm.group_member_id = i.item_deleted_by_group_member_id
|
||||||
LEFT JOIN contact_profiles dbp ON dbp.contact_profile_id = COALESCE(dbm.member_profile_id, dbm.contact_profile_id)
|
LEFT JOIN contact_profiles dbp ON dbp.contact_profile_id = COALESCE(dbm.member_profile_id, dbm.contact_profile_id)
|
||||||
LEFT JOIN group_members dirm ON dirm.group_member_id = i.item_direct_group_member_id
|
|
||||||
LEFT JOIN contact_profiles dirp ON dirp.contact_profile_id = COALESCE(dirm.member_profile_id, dirm.contact_profile_id)
|
|
||||||
WHERE i.user_id = ? AND i.group_id = ? AND i.chat_item_id = ?
|
WHERE i.user_id = ? AND i.group_id = ? AND i.chat_item_id = ?
|
||||||
|]
|
|]
|
||||||
(userId, groupId, itemId)
|
(userId, groupId, itemId)
|
||||||
|
@ -79,7 +79,6 @@ import Simplex.Chat.Migrations.M20230814_indexes
|
|||||||
import Simplex.Chat.Migrations.M20230827_file_encryption
|
import Simplex.Chat.Migrations.M20230827_file_encryption
|
||||||
import Simplex.Chat.Migrations.M20230829_connections_chat_vrange
|
import Simplex.Chat.Migrations.M20230829_connections_chat_vrange
|
||||||
import Simplex.Chat.Migrations.M20230903_connections_to_subscribe
|
import Simplex.Chat.Migrations.M20230903_connections_to_subscribe
|
||||||
import Simplex.Chat.Migrations.M20230904_item_direct_group_member_id
|
|
||||||
import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..))
|
import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..))
|
||||||
|
|
||||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||||
@ -158,8 +157,7 @@ schemaMigrations =
|
|||||||
("20230814_indexes", m20230814_indexes, Just down_m20230814_indexes),
|
("20230814_indexes", m20230814_indexes, Just down_m20230814_indexes),
|
||||||
("20230827_file_encryption", m20230827_file_encryption, Just down_m20230827_file_encryption),
|
("20230827_file_encryption", m20230827_file_encryption, Just down_m20230827_file_encryption),
|
||||||
("20230829_connections_chat_vrange", m20230829_connections_chat_vrange, Just down_m20230829_connections_chat_vrange),
|
("20230829_connections_chat_vrange", m20230829_connections_chat_vrange, Just down_m20230829_connections_chat_vrange),
|
||||||
("20230903_connections_to_subscribe", m20230903_connections_to_subscribe, Just down_m20230903_connections_to_subscribe),
|
("20230903_connections_to_subscribe", m20230903_connections_to_subscribe, Just down_m20230903_connections_to_subscribe)
|
||||||
("20230904_item_direct_group_member_id", m20230904_item_direct_group_member_id, Just down_m20230904_item_direct_group_member_id)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | The list of migrations in ascending order by date
|
-- | The list of migrations in ascending order by date
|
||||||
|
@ -73,19 +73,19 @@ runInputLoop ct@ChatTerminal {termState, liveMessageState} cc = forever $ do
|
|||||||
Right SendMessageBroadcast {} -> True
|
Right SendMessageBroadcast {} -> True
|
||||||
_ -> False
|
_ -> False
|
||||||
startLiveMessage :: Either a ChatCommand -> ChatResponse -> IO ()
|
startLiveMessage :: Either a ChatCommand -> ChatResponse -> IO ()
|
||||||
startLiveMessage (Right (SendLiveMessage sendName msg)) (CRNewChatItem _ (AChatItem cType SMDSnd _ ChatItem {meta = CIMeta {itemId}})) = do
|
startLiveMessage (Right (SendLiveMessage chatName msg)) (CRNewChatItem _ (AChatItem cType SMDSnd _ ChatItem {meta = CIMeta {itemId}})) = do
|
||||||
whenM (isNothing <$> readTVarIO liveMessageState) $ do
|
whenM (isNothing <$> readTVarIO liveMessageState) $ do
|
||||||
let s = T.unpack msg
|
let s = T.unpack msg
|
||||||
int = case cType of SCTGroup -> 5000000; _ -> 3000000 :: Int
|
int = case cType of SCTGroup -> 5000000; _ -> 3000000 :: Int
|
||||||
liveThreadId <- mkWeakThreadId =<< runLiveMessage int `forkFinally` const (atomically $ writeTVar liveMessageState Nothing)
|
liveThreadId <- mkWeakThreadId =<< runLiveMessage int `forkFinally` const (atomically $ writeTVar liveMessageState Nothing)
|
||||||
promptThreadId <- mkWeakThreadId =<< forkIO blinkLivePrompt
|
promptThreadId <- mkWeakThreadId =<< forkIO blinkLivePrompt
|
||||||
atomically $ do
|
atomically $ do
|
||||||
let lm = LiveMessage {sendName, chatItemId = itemId, livePrompt = True, sentMsg = s, typedMsg = s, liveThreadId, promptThreadId}
|
let lm = LiveMessage {chatName, chatItemId = itemId, livePrompt = True, sentMsg = s, typedMsg = s, liveThreadId, promptThreadId}
|
||||||
writeTVar liveMessageState (Just lm)
|
writeTVar liveMessageState (Just lm)
|
||||||
modifyTVar termState $ \ts -> ts {inputString = s, inputPosition = length s, inputPrompt = liveInputPrompt lm}
|
modifyTVar termState $ \ts -> ts {inputString = s, inputPosition = length s, inputPrompt = liveInputPrompt lm}
|
||||||
where
|
where
|
||||||
liveInputPrompt LiveMessage {sendName = n, livePrompt} =
|
liveInputPrompt LiveMessage {chatName = n, livePrompt} =
|
||||||
"> " <> sendNameStr n <> " [" <> (if livePrompt then "LIVE" else " ") <> "] "
|
"> " <> chatNameStr n <> " [" <> (if livePrompt then "LIVE" else " ") <> "] "
|
||||||
runLiveMessage :: Int -> IO ()
|
runLiveMessage :: Int -> IO ()
|
||||||
runLiveMessage int = do
|
runLiveMessage int = do
|
||||||
threadDelay int
|
threadDelay int
|
||||||
@ -123,8 +123,8 @@ runInputLoop ct@ChatTerminal {termState, liveMessageState} cc = forever $ do
|
|||||||
startLiveMessage _ _ = pure ()
|
startLiveMessage _ _ = pure ()
|
||||||
|
|
||||||
sendUpdatedLiveMessage :: ChatController -> String -> LiveMessage -> Bool -> IO ChatResponse
|
sendUpdatedLiveMessage :: ChatController -> String -> LiveMessage -> Bool -> IO ChatResponse
|
||||||
sendUpdatedLiveMessage cc sentMsg LiveMessage {sendName, chatItemId} live = do
|
sendUpdatedLiveMessage cc sentMsg LiveMessage {chatName, chatItemId} live = do
|
||||||
let cmd = UpdateLiveMessage sendName chatItemId live $ T.pack sentMsg
|
let cmd = UpdateLiveMessage chatName chatItemId live $ T.pack sentMsg
|
||||||
either (CRChatCmdError Nothing) id <$> runExceptT (processChatCommand cmd) `runReaderT` cc
|
either (CRChatCmdError Nothing) id <$> runExceptT (processChatCommand cmd) `runReaderT` cc
|
||||||
|
|
||||||
runTerminalInput :: ChatTerminal -> ChatController -> IO ()
|
runTerminalInput :: ChatTerminal -> ChatController -> IO ()
|
||||||
@ -174,14 +174,14 @@ receiveFromTTY cc@ChatController {inputQ, activeTo, currentUser, chatStore} ct@C
|
|||||||
let s = inputString ts
|
let s = inputString ts
|
||||||
lm_ <- readTVar liveMessageState
|
lm_ <- readTVar liveMessageState
|
||||||
case lm_ of
|
case lm_ of
|
||||||
Just LiveMessage {sendName}
|
Just LiveMessage {chatName}
|
||||||
| live -> do
|
| live -> do
|
||||||
writeTVar termState ts' {previousInput}
|
writeTVar termState ts' {previousInput}
|
||||||
writeTBQueue inputQ $ "/live " <> sendNameStr sendName
|
writeTBQueue inputQ $ "/live " <> chatNameStr chatName
|
||||||
| otherwise ->
|
| otherwise ->
|
||||||
writeTVar termState ts' {inputPrompt = "> ", previousInput}
|
writeTVar termState ts' {inputPrompt = "> ", previousInput}
|
||||||
where
|
where
|
||||||
previousInput = sendNameStr sendName <> " " <> s
|
previousInput = chatNameStr chatName <> " " <> s
|
||||||
_
|
_
|
||||||
| live -> when (isSend s) $ do
|
| live -> when (isSend s) $ do
|
||||||
writeTVar termState ts' {previousInput = s}
|
writeTVar termState ts' {previousInput = s}
|
||||||
|
@ -55,7 +55,7 @@ data AutoCompleteState = ACState
|
|||||||
}
|
}
|
||||||
|
|
||||||
data LiveMessage = LiveMessage
|
data LiveMessage = LiveMessage
|
||||||
{ sendName :: SendName,
|
{ chatName :: ChatName,
|
||||||
chatItemId :: ChatItemId,
|
chatItemId :: ChatItemId,
|
||||||
livePrompt :: Bool,
|
livePrompt :: Bool,
|
||||||
sentMsg :: String,
|
sentMsg :: String,
|
||||||
|
@ -330,12 +330,12 @@ data GroupSummary = GroupSummary
|
|||||||
|
|
||||||
instance ToJSON GroupSummary where toEncoding = J.genericToEncoding J.defaultOptions
|
instance ToJSON GroupSummary where toEncoding = J.genericToEncoding J.defaultOptions
|
||||||
|
|
||||||
data ContactOrGroup = CGContact Contact | CGGroup GroupInfo
|
data ContactOrGroup = CGContact Contact | CGGroup Group
|
||||||
|
|
||||||
contactAndGroupIds :: ContactOrGroup -> (Maybe ContactId, Maybe GroupId)
|
contactAndGroupIds :: ContactOrGroup -> (Maybe ContactId, Maybe GroupId)
|
||||||
contactAndGroupIds = \case
|
contactAndGroupIds = \case
|
||||||
CGContact Contact {contactId} -> (Just contactId, Nothing)
|
CGContact Contact {contactId} -> (Just contactId, Nothing)
|
||||||
CGGroup GroupInfo {groupId} -> (Nothing, Just groupId)
|
CGGroup (Group GroupInfo {groupId} _) -> (Nothing, Just groupId)
|
||||||
|
|
||||||
-- TODO when more settings are added we should create another type to allow partial setting updates (with all Maybe properties)
|
-- TODO when more settings are added we should create another type to allow partial setting updates (with all Maybe properties)
|
||||||
data ChatSettings = ChatSettings
|
data ChatSettings = ChatSettings
|
||||||
|
@ -322,35 +322,14 @@ responseToView user_ ChatConfig {logLevel, showReactions, showReceipts, testView
|
|||||||
testViewChat :: AChat -> [StyledString]
|
testViewChat :: AChat -> [StyledString]
|
||||||
testViewChat (AChat _ Chat {chatInfo, chatItems}) = [sShow $ map toChatView chatItems]
|
testViewChat (AChat _ Chat {chatInfo, chatItems}) = [sShow $ map toChatView chatItems]
|
||||||
where
|
where
|
||||||
toChatView :: CChatItem c -> ((Int, String, Text), Maybe (Int, String, Text), Maybe String)
|
toChatView :: CChatItem c -> ((Int, Text), Maybe (Int, Text), Maybe String)
|
||||||
toChatView ci@(CChatItem dir ChatItem {chatDir, quotedItem, file}) =
|
toChatView ci@(CChatItem dir ChatItem {quotedItem, file}) =
|
||||||
(item, qItem, fPath)
|
((msgDirectionInt $ toMsgDirection dir, testViewItem ci (chatInfoMembership chatInfo)), qItem, fPath)
|
||||||
where
|
where
|
||||||
item =
|
|
||||||
( msgDirectionInt $ toMsgDirection dir,
|
|
||||||
directMemberName,
|
|
||||||
testViewItem ci (chatInfoMembership chatInfo)
|
|
||||||
)
|
|
||||||
directMemberName = case chatDir of
|
|
||||||
CIGroupSnd (Just GroupMember {localDisplayName = n}) -> T.unpack n
|
|
||||||
CIGroupRcv GroupMember {localDisplayName = n} MSDirect -> T.unpack n
|
|
||||||
_ -> ""
|
|
||||||
qItem = case quotedItem of
|
qItem = case quotedItem of
|
||||||
Nothing -> Nothing
|
Nothing -> Nothing
|
||||||
Just CIQuote {chatDir = quoteDir, content} ->
|
Just CIQuote {chatDir = quoteDir, content} ->
|
||||||
Just
|
Just (msgDirectionInt $ quoteMsgDirection quoteDir, msgContentText content)
|
||||||
( msgDirectionInt $ quoteMsgDirection quoteDir,
|
|
||||||
qMsgScope,
|
|
||||||
msgContentText content
|
|
||||||
)
|
|
||||||
where
|
|
||||||
qMsgScope = case quoteDir of
|
|
||||||
CIQGroupSnd ms -> msgScopeText ms
|
|
||||||
CIQGroupRcv _ ms -> msgScopeText ms
|
|
||||||
_ -> ""
|
|
||||||
msgScopeText ms = case ms of
|
|
||||||
MSGroup -> "group"
|
|
||||||
MSDirect -> "direct"
|
|
||||||
fPath = case file of
|
fPath = case file of
|
||||||
Just CIFile {fileSource = Just (CryptoFile fp _)} -> Just fp
|
Just CIFile {fileSource = Just (CryptoFile fp _)} -> Just fp
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
@ -401,7 +380,7 @@ viewUsersList = mapMaybe userInfo . sortOn ldn
|
|||||||
muted :: ChatInfo c -> CIDirection c d -> Bool
|
muted :: ChatInfo c -> CIDirection c d -> Bool
|
||||||
muted chat chatDir = case (chat, chatDir) of
|
muted chat chatDir = case (chat, chatDir) of
|
||||||
(DirectChat Contact {chatSettings = DisableNtfs}, CIDirectRcv) -> True
|
(DirectChat Contact {chatSettings = DisableNtfs}, CIDirectRcv) -> True
|
||||||
(GroupChat GroupInfo {chatSettings = DisableNtfs}, CIGroupRcv _ _) -> True
|
(GroupChat GroupInfo {chatSettings = DisableNtfs}, CIGroupRcv _) -> True
|
||||||
_ -> False
|
_ -> False
|
||||||
|
|
||||||
viewGroupSubscribed :: GroupInfo -> [StyledString]
|
viewGroupSubscribed :: GroupInfo -> [StyledString]
|
||||||
@ -424,9 +403,8 @@ viewChats ts tz = concatMap chatPreview . reverse
|
|||||||
where
|
where
|
||||||
chatName = case chat of
|
chatName = case chat of
|
||||||
DirectChat ct -> [" " <> ttyToContact' ct]
|
DirectChat ct -> [" " <> ttyToContact' ct]
|
||||||
GroupChat g -> [" " <> ttyToGroup' g]
|
GroupChat g -> [" " <> ttyToGroup g]
|
||||||
_ -> []
|
_ -> []
|
||||||
ttyToGroup' g@GroupInfo {localDisplayName = n} = membershipIncognito g <> ttyTo ("#" <> n <> " ")
|
|
||||||
|
|
||||||
viewChatItem :: forall c d. MsgDirectionI d => ChatInfo c -> ChatItem c d -> Bool -> CurrentTime -> TimeZone -> [StyledString]
|
viewChatItem :: forall c d. MsgDirectionI d => ChatInfo c -> ChatItem c d -> Bool -> CurrentTime -> TimeZone -> [StyledString]
|
||||||
viewChatItem chat ci@ChatItem {chatDir, meta = meta, content, quotedItem, file} doShow ts tz =
|
viewChatItem chat ci@ChatItem {chatDir, meta = meta, content, quotedItem, file} doShow ts tz =
|
||||||
@ -448,20 +426,20 @@ viewChatItem chat ci@ChatItem {chatDir, meta = meta, content, quotedItem, file}
|
|||||||
where
|
where
|
||||||
quote = maybe [] (directQuote chatDir) quotedItem
|
quote = maybe [] (directQuote chatDir) quotedItem
|
||||||
GroupChat g -> case chatDir of
|
GroupChat g -> case chatDir of
|
||||||
CIGroupSnd directMember -> case content of
|
CIGroupSnd -> case content of
|
||||||
CISndMsgContent mc -> hideLive meta $ withSndFile to $ sndMsg to quote mc
|
CISndMsgContent mc -> hideLive meta $ withSndFile to $ sndMsg to quote mc
|
||||||
CISndGroupInvitation {} -> showSndItemProhibited to
|
CISndGroupInvitation {} -> showSndItemProhibited to
|
||||||
_ -> showSndItem to
|
_ -> showSndItem to
|
||||||
where
|
where
|
||||||
to = ttyToGroup g directMember
|
to = ttyToGroup g
|
||||||
CIGroupRcv m msgScope -> case content of
|
CIGroupRcv m -> case content of
|
||||||
CIRcvMsgContent mc -> withRcvFile from $ rcvMsg from quote mc
|
CIRcvMsgContent mc -> withRcvFile from $ rcvMsg from quote mc
|
||||||
CIRcvIntegrityError err -> viewRcvIntegrityError from err ts tz meta
|
CIRcvIntegrityError err -> viewRcvIntegrityError from err ts tz meta
|
||||||
CIRcvGroupInvitation {} -> showRcvItemProhibited from
|
CIRcvGroupInvitation {} -> showRcvItemProhibited from
|
||||||
CIRcvModerated {} -> receivedWithTime_ ts tz (ttyFromGroup g m msgScope) quote meta [plainContent content] False
|
CIRcvModerated {} -> receivedWithTime_ ts tz (ttyFromGroup g m) quote meta [plainContent content] False
|
||||||
_ -> showRcvItem from
|
_ -> showRcvItem from
|
||||||
where
|
where
|
||||||
from = ttyFromGroup g m msgScope
|
from = ttyFromGroup g m
|
||||||
where
|
where
|
||||||
quote = maybe [] (groupQuote g) quotedItem
|
quote = maybe [] (groupQuote g) quotedItem
|
||||||
_ -> []
|
_ -> []
|
||||||
@ -553,18 +531,18 @@ viewItemUpdate chat ChatItem {chatDir, meta = meta@CIMeta {itemEdited, itemLive}
|
|||||||
where
|
where
|
||||||
quote = maybe [] (directQuote chatDir) quotedItem
|
quote = maybe [] (directQuote chatDir) quotedItem
|
||||||
GroupChat g -> case chatDir of
|
GroupChat g -> case chatDir of
|
||||||
CIGroupRcv m msgScope -> case content of
|
CIGroupRcv m -> case content of
|
||||||
CIRcvMsgContent mc
|
CIRcvMsgContent mc
|
||||||
| itemLive == Just True && not liveItems -> []
|
| itemLive == Just True && not liveItems -> []
|
||||||
| otherwise -> viewReceivedUpdatedMessage from quote mc ts tz meta
|
| otherwise -> viewReceivedUpdatedMessage from quote mc ts tz meta
|
||||||
_ -> []
|
_ -> []
|
||||||
where
|
where
|
||||||
from = if itemEdited then ttyFromGroupEdited g m msgScope else ttyFromGroup g m msgScope
|
from = if itemEdited then ttyFromGroupEdited g m else ttyFromGroup g m
|
||||||
CIGroupSnd directMember -> case content of
|
CIGroupSnd -> case content of
|
||||||
CISndMsgContent mc -> hideLive meta $ viewSentMessage to quote mc ts tz meta
|
CISndMsgContent mc -> hideLive meta $ viewSentMessage to quote mc ts tz meta
|
||||||
_ -> []
|
_ -> []
|
||||||
where
|
where
|
||||||
to = if itemEdited then ttyToGroupEdited g directMember else ttyToGroup g directMember
|
to = if itemEdited then ttyToGroupEdited g else ttyToGroup g
|
||||||
where
|
where
|
||||||
quote = maybe [] (groupQuote g) quotedItem
|
quote = maybe [] (groupQuote g) quotedItem
|
||||||
_ -> []
|
_ -> []
|
||||||
@ -589,8 +567,7 @@ viewItemDelete chat ci@ChatItem {chatDir, meta, content = deletedContent} toItem
|
|||||||
GroupChat g -> case ciMsgContent deletedContent of
|
GroupChat g -> case ciMsgContent deletedContent of
|
||||||
Just mc ->
|
Just mc ->
|
||||||
let m = chatItemMember g ci
|
let m = chatItemMember g ci
|
||||||
msgScope = directMemberToMsgScope $ ciDirDirectMember chatDir
|
in viewReceivedMessage (ttyFromGroupDeleted g m deletedText_) [] mc ts tz meta
|
||||||
in viewReceivedMessage (ttyFromGroupDeleted g m msgScope deletedText_) [] mc ts tz meta
|
|
||||||
_ -> prohibited
|
_ -> prohibited
|
||||||
_ -> prohibited
|
_ -> prohibited
|
||||||
where
|
where
|
||||||
@ -609,14 +586,14 @@ viewItemReaction showReactions chat CIReaction {chatDir, chatItem = CChatItem md
|
|||||||
where
|
where
|
||||||
from = ttyFromContact c
|
from = ttyFromContact c
|
||||||
reactionMsg mc = quoteText mc $ if toMsgDirection md == MDSnd then ">>" else ">"
|
reactionMsg mc = quoteText mc $ if toMsgDirection md == MDSnd then ">>" else ">"
|
||||||
(GroupChat g, CIGroupRcv m messageScope) -> case ciMsgContent content of
|
(GroupChat g, CIGroupRcv m) -> case ciMsgContent content of
|
||||||
Just mc -> view from $ reactionMsg mc
|
Just mc -> view from $ reactionMsg mc
|
||||||
_ -> []
|
_ -> []
|
||||||
where
|
where
|
||||||
from = ttyFromGroup g m messageScope
|
from = ttyFromGroup g m
|
||||||
reactionMsg mc = quoteText mc . ttyQuotedMember . Just $ sentByMember' g itemDir
|
reactionMsg mc = quoteText mc . ttyQuotedMember . Just $ sentByMember' g itemDir
|
||||||
(_, CIDirectSnd) -> [sentText]
|
(_, CIDirectSnd) -> [sentText]
|
||||||
(_, CIGroupSnd _) -> [sentText]
|
(_, CIGroupSnd) -> [sentText]
|
||||||
where
|
where
|
||||||
view from msg
|
view from msg
|
||||||
| showReactions = viewReceivedReaction from msg reactionText ts tz sentAt
|
| showReactions = viewReceivedReaction from msg reactionText ts tz sentAt
|
||||||
@ -644,13 +621,13 @@ groupQuote g CIQuote {content = qmc, chatDir = quoteDir} = quoteText qmc . ttyQu
|
|||||||
|
|
||||||
sentByMember :: GroupInfo -> CIQDirection 'CTGroup -> Maybe GroupMember
|
sentByMember :: GroupInfo -> CIQDirection 'CTGroup -> Maybe GroupMember
|
||||||
sentByMember GroupInfo {membership} = \case
|
sentByMember GroupInfo {membership} = \case
|
||||||
CIQGroupSnd _ -> Just membership
|
CIQGroupSnd -> Just membership
|
||||||
CIQGroupRcv m _ -> m
|
CIQGroupRcv m -> m
|
||||||
|
|
||||||
sentByMember' :: GroupInfo -> CIDirection 'CTGroup d -> GroupMember
|
sentByMember' :: GroupInfo -> CIDirection 'CTGroup d -> GroupMember
|
||||||
sentByMember' GroupInfo {membership} = \case
|
sentByMember' GroupInfo {membership} = \case
|
||||||
CIGroupSnd _ -> membership
|
CIGroupSnd -> membership
|
||||||
CIGroupRcv m _ -> m
|
CIGroupRcv m -> m
|
||||||
|
|
||||||
quoteText :: MsgContent -> StyledString -> [StyledString]
|
quoteText :: MsgContent -> StyledString -> [StyledString]
|
||||||
quoteText qmc sentBy = prependFirst (sentBy <> " ") $ msgPreview qmc
|
quoteText qmc sentBy = prependFirst (sentBy <> " ") $ msgPreview qmc
|
||||||
@ -1342,9 +1319,8 @@ sendingFile_ status ft@SndFileTransfer {recipientDisplayName = c} =
|
|||||||
uploadingFile :: StyledString -> AChatItem -> [StyledString]
|
uploadingFile :: StyledString -> AChatItem -> [StyledString]
|
||||||
uploadingFile status (AChatItem _ _ (DirectChat Contact {localDisplayName = c}) ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIDirectSnd}) =
|
uploadingFile status (AChatItem _ _ (DirectChat Contact {localDisplayName = c}) ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIDirectSnd}) =
|
||||||
[status <> " uploading " <> fileTransferStr fileId fileName <> " for " <> ttyContact c]
|
[status <> " uploading " <> fileTransferStr fileId fileName <> " for " <> ttyContact c]
|
||||||
uploadingFile status (AChatItem _ _ (GroupChat g) ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIGroupSnd directMember}) =
|
uploadingFile status (AChatItem _ _ (GroupChat g) ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIGroupSnd}) =
|
||||||
let forMember = maybe "" (\GroupMember {localDisplayName = m} -> styled (colored Blue) $ " @" <> m <> " (direct)") directMember
|
[status <> " uploading " <> fileTransferStr fileId fileName <> " for " <> ttyGroup' g]
|
||||||
in [status <> " uploading " <> fileTransferStr fileId fileName <> " for " <> ttyGroup' g <> forMember]
|
|
||||||
uploadingFile status _ = [status <> " uploading file"] -- shouldn't happen
|
uploadingFile status _ = [status <> " uploading file"] -- shouldn't happen
|
||||||
|
|
||||||
sndFile :: SndFileTransfer -> StyledString
|
sndFile :: SndFileTransfer -> StyledString
|
||||||
@ -1376,7 +1352,7 @@ savingFile' :: Bool -> AChatItem -> [StyledString]
|
|||||||
savingFile' testView (AChatItem _ _ chat ChatItem {file = Just CIFile {fileId, fileSource = Just (CryptoFile filePath cfArgs_)}, chatDir}) =
|
savingFile' testView (AChatItem _ _ chat ChatItem {file = Just CIFile {fileId, fileSource = Just (CryptoFile filePath cfArgs_)}, chatDir}) =
|
||||||
let from = case (chat, chatDir) of
|
let from = case (chat, chatDir) of
|
||||||
(DirectChat Contact {localDisplayName = c}, CIDirectRcv) -> " from " <> ttyContact c
|
(DirectChat Contact {localDisplayName = c}, CIDirectRcv) -> " from " <> ttyContact c
|
||||||
(_, CIGroupRcv GroupMember {localDisplayName = m} _) -> " from " <> ttyContact m
|
(_, CIGroupRcv GroupMember {localDisplayName = m}) -> " from " <> ttyContact m
|
||||||
_ -> ""
|
_ -> ""
|
||||||
in ["saving file " <> sShow fileId <> from <> " to " <> plain filePath] <> cfArgsStr
|
in ["saving file " <> sShow fileId <> from <> " to " <> plain filePath] <> cfArgsStr
|
||||||
where
|
where
|
||||||
@ -1390,7 +1366,7 @@ savingFile' _ _ = ["saving file"] -- shouldn't happen
|
|||||||
receivingFile_' :: StyledString -> AChatItem -> [StyledString]
|
receivingFile_' :: StyledString -> AChatItem -> [StyledString]
|
||||||
receivingFile_' status (AChatItem _ _ (DirectChat Contact {localDisplayName = c}) ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIDirectRcv}) =
|
receivingFile_' status (AChatItem _ _ (DirectChat Contact {localDisplayName = c}) ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIDirectRcv}) =
|
||||||
[status <> " receiving " <> fileTransferStr fileId fileName <> " from " <> ttyContact c]
|
[status <> " receiving " <> fileTransferStr fileId fileName <> " from " <> ttyContact c]
|
||||||
receivingFile_' status (AChatItem _ _ _ ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIGroupRcv GroupMember {localDisplayName = m} _}) =
|
receivingFile_' status (AChatItem _ _ _ ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIGroupRcv GroupMember {localDisplayName = m}}) =
|
||||||
[status <> " receiving " <> fileTransferStr fileId fileName <> " from " <> ttyContact m]
|
[status <> " receiving " <> fileTransferStr fileId fileName <> " from " <> ttyContact m]
|
||||||
receivingFile_' status _ = [status <> " receiving file"] -- shouldn't happen
|
receivingFile_' status _ = [status <> " receiving file"] -- shouldn't happen
|
||||||
|
|
||||||
@ -1606,7 +1582,7 @@ viewChatError logLevel = \case
|
|||||||
CEXFTPSndFile fileId aFileId e -> ["error sending XFTP file " <> sShow fileId <> ", agent file id " <> sShow aFileId <> ": " <> sShow e | logLevel == CLLError]
|
CEXFTPSndFile fileId aFileId e -> ["error sending XFTP file " <> sShow fileId <> ", agent file id " <> sShow aFileId <> ": " <> sShow e | logLevel == CLLError]
|
||||||
CEFallbackToSMPProhibited fileId -> ["recipient tried to accept file " <> sShow fileId <> " via old protocol, prohibited"]
|
CEFallbackToSMPProhibited fileId -> ["recipient tried to accept file " <> sShow fileId <> " via old protocol, prohibited"]
|
||||||
CEInlineFileProhibited _ -> ["A small file sent without acceptance - you can enable receiving such files with -f option."]
|
CEInlineFileProhibited _ -> ["A small file sent without acceptance - you can enable receiving such files with -f option."]
|
||||||
CEInvalidQuote -> ["invalid message reply"]
|
CEInvalidQuote -> ["cannot reply to this message"]
|
||||||
CEInvalidChatItemUpdate -> ["cannot update this item"]
|
CEInvalidChatItemUpdate -> ["cannot update this item"]
|
||||||
CEInvalidChatItemDelete -> ["cannot delete this item"]
|
CEInvalidChatItemDelete -> ["cannot delete this item"]
|
||||||
CEHasCurrentCall -> ["call already in progress"]
|
CEHasCurrentCall -> ["call already in progress"]
|
||||||
@ -1621,7 +1597,6 @@ viewChatError logLevel = \case
|
|||||||
CEAgentCommandError e -> ["agent command error: " <> plain e]
|
CEAgentCommandError e -> ["agent command error: " <> plain e]
|
||||||
CEInvalidFileDescription e -> ["invalid file description: " <> plain e]
|
CEInvalidFileDescription e -> ["invalid file description: " <> plain e]
|
||||||
CEConnectionIncognitoChangeProhibited -> ["incognito mode change prohibited"]
|
CEConnectionIncognitoChangeProhibited -> ["incognito mode change prohibited"]
|
||||||
CEPeerChatVRangeIncompatible -> ["peer chat protocol version range incompatible"]
|
|
||||||
CEInternalError e -> ["internal chat error: " <> plain e]
|
CEInternalError e -> ["internal chat error: " <> plain e]
|
||||||
CEException e -> ["exception: " <> plain e]
|
CEException e -> ["exception: " <> plain e]
|
||||||
-- e -> ["chat error: " <> sShow e]
|
-- e -> ["chat error: " <> sShow e]
|
||||||
@ -1762,24 +1737,19 @@ ttyFullGroup :: GroupInfo -> StyledString
|
|||||||
ttyFullGroup GroupInfo {localDisplayName = g, groupProfile = GroupProfile {fullName}} =
|
ttyFullGroup GroupInfo {localDisplayName = g, groupProfile = GroupProfile {fullName}} =
|
||||||
ttyGroup g <> optFullName g fullName
|
ttyGroup g <> optFullName g fullName
|
||||||
|
|
||||||
ttyFromGroup :: GroupInfo -> GroupMember -> MessageScope -> StyledString
|
ttyFromGroup :: GroupInfo -> GroupMember -> StyledString
|
||||||
ttyFromGroup g m ms = membershipIncognito g <> ttyFrom (fromGroup_ g m ms)
|
ttyFromGroup g m = membershipIncognito g <> ttyFrom (fromGroup_ g m)
|
||||||
|
|
||||||
ttyFromGroupEdited :: GroupInfo -> GroupMember -> MessageScope -> StyledString
|
ttyFromGroupEdited :: GroupInfo -> GroupMember -> StyledString
|
||||||
ttyFromGroupEdited g m ms = membershipIncognito g <> ttyFrom (fromGroup_ g m ms <> "[edited] ")
|
ttyFromGroupEdited g m = membershipIncognito g <> ttyFrom (fromGroup_ g m <> "[edited] ")
|
||||||
|
|
||||||
ttyFromGroupDeleted :: GroupInfo -> GroupMember -> MessageScope -> Maybe Text -> StyledString
|
ttyFromGroupDeleted :: GroupInfo -> GroupMember -> Maybe Text -> StyledString
|
||||||
ttyFromGroupDeleted g m ms deletedText_ =
|
ttyFromGroupDeleted g m deletedText_ =
|
||||||
membershipIncognito g <> ttyFrom (fromGroup_ g m ms <> maybe "" (\t -> "[" <> t <> "] ") deletedText_)
|
membershipIncognito g <> ttyFrom (fromGroup_ g m <> maybe "" (\t -> "[" <> t <> "] ") deletedText_)
|
||||||
|
|
||||||
fromGroup_ :: GroupInfo -> GroupMember -> MessageScope -> Text
|
fromGroup_ :: GroupInfo -> GroupMember -> Text
|
||||||
fromGroup_ GroupInfo {localDisplayName = g} GroupMember {localDisplayName = m} ms =
|
fromGroup_ GroupInfo {localDisplayName = g} GroupMember {localDisplayName = m} =
|
||||||
"#" <> g <> " " <> m <> fromGroupScope ms <> "> "
|
"#" <> g <> " " <> m <> "> "
|
||||||
|
|
||||||
fromGroupScope :: MessageScope -> Text
|
|
||||||
fromGroupScope = \case
|
|
||||||
MSGroup -> ""
|
|
||||||
MSDirect -> " (direct)"
|
|
||||||
|
|
||||||
ttyFrom :: Text -> StyledString
|
ttyFrom :: Text -> StyledString
|
||||||
ttyFrom = styled $ colored Yellow
|
ttyFrom = styled $ colored Yellow
|
||||||
@ -1787,18 +1757,13 @@ ttyFrom = styled $ colored Yellow
|
|||||||
ttyTo :: Text -> StyledString
|
ttyTo :: Text -> StyledString
|
||||||
ttyTo = styled $ colored Cyan
|
ttyTo = styled $ colored Cyan
|
||||||
|
|
||||||
ttyToGroup :: GroupInfo -> Maybe GroupMember -> StyledString
|
ttyToGroup :: GroupInfo -> StyledString
|
||||||
ttyToGroup g@GroupInfo {localDisplayName = n} dirMem =
|
ttyToGroup g@GroupInfo {localDisplayName = n} =
|
||||||
membershipIncognito g <> ttyTo ("#" <> n <> toDirectMember dirMem <> " ")
|
membershipIncognito g <> ttyTo ("#" <> n <> " ")
|
||||||
|
|
||||||
ttyToGroupEdited :: GroupInfo -> Maybe GroupMember -> StyledString
|
ttyToGroupEdited :: GroupInfo -> StyledString
|
||||||
ttyToGroupEdited g@GroupInfo {localDisplayName = n} dirMem =
|
ttyToGroupEdited g@GroupInfo {localDisplayName = n} =
|
||||||
membershipIncognito g <> ttyTo ("#" <> n <> toDirectMember dirMem <> " [edited] ")
|
membershipIncognito g <> ttyTo ("#" <> n <> " [edited] ")
|
||||||
|
|
||||||
toDirectMember :: Maybe GroupMember -> Text
|
|
||||||
toDirectMember = \case
|
|
||||||
Nothing -> ""
|
|
||||||
Just GroupMember {localDisplayName = m} -> " @" <> m <> " (direct)"
|
|
||||||
|
|
||||||
ttyFilePath :: FilePath -> StyledString
|
ttyFilePath :: FilePath -> StyledString
|
||||||
ttyFilePath = plain
|
ttyFilePath = plain
|
||||||
|
@ -259,7 +259,7 @@ getTermLine cc =
|
|||||||
Just s -> do
|
Just s -> do
|
||||||
-- remove condition to always echo virtual terminal
|
-- remove condition to always echo virtual terminal
|
||||||
when (printOutput cc) $ do
|
when (printOutput cc) $ do
|
||||||
-- when True $ do
|
-- when True $ do
|
||||||
name <- userName cc
|
name <- userName cc
|
||||||
putStrLn $ name <> ": " <> s
|
putStrLn $ name <> ": " <> s
|
||||||
pure s
|
pure s
|
||||||
|
@ -8,9 +8,8 @@ import ChatTests.Utils
|
|||||||
import Control.Concurrent (threadDelay)
|
import Control.Concurrent (threadDelay)
|
||||||
import Control.Concurrent.Async (concurrently_)
|
import Control.Concurrent.Async (concurrently_)
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
import qualified Data.ByteString.Char8 as B
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Simplex.Chat.Controller (ChatConfig (..), XFTPFileConfig (..))
|
import Simplex.Chat.Controller (ChatConfig (..))
|
||||||
import Simplex.Chat.Protocol (supportedChatVRange)
|
import Simplex.Chat.Protocol (supportedChatVRange)
|
||||||
import Simplex.Chat.Store (agentStoreFile, chatStoreFile)
|
import Simplex.Chat.Store (agentStoreFile, chatStoreFile)
|
||||||
import Simplex.Chat.Types (GroupMemberRole (..))
|
import Simplex.Chat.Types (GroupMemberRole (..))
|
||||||
@ -82,21 +81,6 @@ chatGroupTests = do
|
|||||||
testNoDirect4 _1 _0 _1 False False False -- False False True
|
testNoDirect4 _1 _0 _1 False False False -- False False True
|
||||||
testNoDirect4 _1 _1 _0 False False False
|
testNoDirect4 _1 _1 _0 False False False
|
||||||
testNoDirect4 _1 _1 _1 False False False
|
testNoDirect4 _1 _1 _1 False False False
|
||||||
describe "group direct messages" $ do
|
|
||||||
it "should send group direct messages" testGroupDirectMessages
|
|
||||||
it "should create group direct messages chat items" testGroupDirectMessagesItems
|
|
||||||
it "should send group direct quotes" testGroupDirectQuotes
|
|
||||||
it "should create group direct quotes chat items" testGroupDirectQuotesItems
|
|
||||||
it "should send group direct XFTP files" testGroupDirectFilesXFTP
|
|
||||||
it "should send group direct SMP files" testGroupDirectFilesSMP
|
|
||||||
it "should cancel sent group direct XFTP file" testGroupDirectCancelFileXFTP
|
|
||||||
it "should send group direct quotes with files" testGroupDirectQuotesFiles
|
|
||||||
it "should update group direct message" testGroupDirectUpdate
|
|
||||||
it "should delete group direct message" testGroupDirectDelete
|
|
||||||
it "should send group direct live message" testGroupDirectLiveMessage
|
|
||||||
it "should send group direct message reactions" testGroupDirectReactions
|
|
||||||
it "should prohibit group direct messages based on preference" testGroupDirectProhibitPreference
|
|
||||||
it "should prohibit group direct messages if peer version doesn't support" testGroupDirectProhibitNotSupported
|
|
||||||
where
|
where
|
||||||
_0 = supportedChatVRange -- don't create direct connections
|
_0 = supportedChatVRange -- don't create direct connections
|
||||||
_1 = groupCreateDirectVRange
|
_1 = groupCreateDirectVRange
|
||||||
@ -820,7 +804,7 @@ testGroupMessageQuotedReply =
|
|||||||
(bob <# "#team alice> hello! how are you?")
|
(bob <# "#team alice> hello! how are you?")
|
||||||
(cath <# "#team alice> hello! how are you?")
|
(cath <# "#team alice> hello! how are you?")
|
||||||
threadDelay 1000000
|
threadDelay 1000000
|
||||||
bob `send` "> #team >@alice (hello) hello, all good, you?"
|
bob `send` "> #team @alice (hello) hello, all good, you?"
|
||||||
bob <# "#team > alice hello! how are you?"
|
bob <# "#team > alice hello! how are you?"
|
||||||
bob <## " hello, all good, you?"
|
bob <## " hello, all good, you?"
|
||||||
concurrently_
|
concurrently_
|
||||||
@ -835,7 +819,7 @@ testGroupMessageQuotedReply =
|
|||||||
bob #$> ("/_get chat #1 count=2", chat', [((0, "hello! how are you?"), Nothing), ((1, "hello, all good, you?"), Just (0, "hello! how are you?"))])
|
bob #$> ("/_get chat #1 count=2", chat', [((0, "hello! how are you?"), Nothing), ((1, "hello, all good, you?"), Just (0, "hello! how are you?"))])
|
||||||
alice #$> ("/_get chat #1 count=2", chat', [((1, "hello! how are you?"), Nothing), ((0, "hello, all good, you?"), Just (1, "hello! how are you?"))])
|
alice #$> ("/_get chat #1 count=2", chat', [((1, "hello! how are you?"), Nothing), ((0, "hello, all good, you?"), Just (1, "hello! how are you?"))])
|
||||||
cath #$> ("/_get chat #1 count=2", chat', [((0, "hello! how are you?"), Nothing), ((0, "hello, all good, you?"), Just (0, "hello! how are you?"))])
|
cath #$> ("/_get chat #1 count=2", chat', [((0, "hello! how are you?"), Nothing), ((0, "hello, all good, you?"), Just (0, "hello! how are you?"))])
|
||||||
bob `send` "> #team >@bob (hello, all good) will tell more"
|
bob `send` "> #team bob (hello, all good) will tell more"
|
||||||
bob <# "#team > bob hello, all good, you?"
|
bob <# "#team > bob hello, all good, you?"
|
||||||
bob <## " will tell more"
|
bob <## " will tell more"
|
||||||
concurrently_
|
concurrently_
|
||||||
@ -851,7 +835,7 @@ testGroupMessageQuotedReply =
|
|||||||
alice #$> ("/_get chat #1 count=1", chat', [((0, "will tell more"), Just (0, "hello, all good, you?"))])
|
alice #$> ("/_get chat #1 count=1", chat', [((0, "will tell more"), Just (0, "hello, all good, you?"))])
|
||||||
cath #$> ("/_get chat #1 count=1", chat', [((0, "will tell more"), Just (0, "hello, all good, you?"))])
|
cath #$> ("/_get chat #1 count=1", chat', [((0, "will tell more"), Just (0, "hello, all good, you?"))])
|
||||||
threadDelay 1000000
|
threadDelay 1000000
|
||||||
cath `send` "> #team >@bob (hello) hi there!"
|
cath `send` "> #team bob (hello) hi there!"
|
||||||
cath <# "#team > bob hello, all good, you?"
|
cath <# "#team > bob hello, all good, you?"
|
||||||
cath <## " hi there!"
|
cath <## " hi there!"
|
||||||
concurrently_
|
concurrently_
|
||||||
@ -907,7 +891,7 @@ testGroupMessageUpdate =
|
|||||||
|
|
||||||
threadDelay 1000000
|
threadDelay 1000000
|
||||||
-- alice, bob: msg id 6, cath: msg id 5
|
-- alice, bob: msg id 6, cath: msg id 5
|
||||||
bob `send` "> #team >@alice (hey) hi alice"
|
bob `send` "> #team @alice (hey) hi alice"
|
||||||
bob <# "#team > alice hey 👋"
|
bob <# "#team > alice hey 👋"
|
||||||
bob <## " hi alice"
|
bob <## " hi alice"
|
||||||
concurrently_
|
concurrently_
|
||||||
@ -934,7 +918,7 @@ testGroupMessageUpdate =
|
|||||||
alice #$> ("/_update item #1 " <> msgItemId2 <> " text updating bob's message", id, "cannot update this item")
|
alice #$> ("/_update item #1 " <> msgItemId2 <> " text updating bob's message", id, "cannot update this item")
|
||||||
|
|
||||||
threadDelay 1000000
|
threadDelay 1000000
|
||||||
cath `send` "> #team >@alice (greetings) greetings!"
|
cath `send` "> #team @alice (greetings) greetings!"
|
||||||
cath <# "#team > alice greetings 🤝"
|
cath <# "#team > alice greetings 🤝"
|
||||||
cath <## " greetings!"
|
cath <## " greetings!"
|
||||||
concurrently_
|
concurrently_
|
||||||
@ -1010,6 +994,7 @@ testGroupMessageEditHistory =
|
|||||||
|
|
||||||
alice ##> ("/_update item #1 " <> aliceItemId <> " text hey there")
|
alice ##> ("/_update item #1 " <> aliceItemId <> " text hey there")
|
||||||
alice <# "#team [edited] hey there"
|
alice <# "#team [edited] hey there"
|
||||||
|
bob <# "#team alice> [edited] hey there"
|
||||||
|
|
||||||
alice ##> "/item info #team hey"
|
alice ##> "/item info #team hey"
|
||||||
alice <##. "sent at: "
|
alice <##. "sent at: "
|
||||||
@ -1019,7 +1004,10 @@ testGroupMessageEditHistory =
|
|||||||
alice .<## ": hey 👋"
|
alice .<## ": hey 👋"
|
||||||
alice .<## ": hello!"
|
alice .<## ": hello!"
|
||||||
bob ##> "/item info #team hey"
|
bob ##> "/item info #team hey"
|
||||||
bob <## "message not found by text: hey"
|
bob <##. "sent at: "
|
||||||
|
bob <##. "received at: "
|
||||||
|
bob <## "message history:"
|
||||||
|
bob .<## ": hey there"
|
||||||
|
|
||||||
testGroupMessageDelete :: HasCallStack => FilePath -> IO ()
|
testGroupMessageDelete :: HasCallStack => FilePath -> IO ()
|
||||||
testGroupMessageDelete =
|
testGroupMessageDelete =
|
||||||
@ -1043,7 +1031,7 @@ testGroupMessageDelete =
|
|||||||
|
|
||||||
threadDelay 1000000
|
threadDelay 1000000
|
||||||
-- alice: msg id 5, bob: msg id 6, cath: msg id 5
|
-- alice: msg id 5, bob: msg id 6, cath: msg id 5
|
||||||
bob `send` "> #team >@alice (hello) hi alic"
|
bob `send` "> #team @alice (hello) hi alic"
|
||||||
bob <# "#team > alice hello!"
|
bob <# "#team > alice hello!"
|
||||||
bob <## " hi alic"
|
bob <## " hi alic"
|
||||||
concurrently_
|
concurrently_
|
||||||
@ -1072,10 +1060,14 @@ testGroupMessageDelete =
|
|||||||
bob ##> ("/_update item #1 " <> msgItemId3 <> " text hi alice")
|
bob ##> ("/_update item #1 " <> msgItemId3 <> " text hi alice")
|
||||||
bob <# "#team [edited] > alice hello!"
|
bob <# "#team [edited] > alice hello!"
|
||||||
bob <## " hi alice"
|
bob <## " hi alice"
|
||||||
cath <# "#team bob> [edited] > alice hello!"
|
concurrently_
|
||||||
cath <## " hi alice"
|
(alice <# "#team bob> [edited] hi alice")
|
||||||
|
( do
|
||||||
|
cath <# "#team bob> [edited] > alice hello!"
|
||||||
|
cath <## " hi alice"
|
||||||
|
)
|
||||||
|
|
||||||
alice #$> ("/_get chat #1 count=1", chat', [((0, "connected"), Nothing)])
|
alice #$> ("/_get chat #1 count=1", chat', [((0, "hi alice"), Nothing)])
|
||||||
bob #$> ("/_get chat #1 count=2", chat', [((0, "hello!"), Nothing), ((1, "hi alice"), Just (0, "hello!"))])
|
bob #$> ("/_get chat #1 count=2", chat', [((0, "hello!"), Nothing), ((1, "hi alice"), Just (0, "hello!"))])
|
||||||
cath #$> ("/_get chat #1 count=2", chat', [((0, "hello!"), Nothing), ((0, "hi alice"), Just (0, "hello!"))])
|
cath #$> ("/_get chat #1 count=2", chat', [((0, "hello!"), Nothing), ((0, "hi alice"), Just (0, "hello!"))])
|
||||||
|
|
||||||
@ -2694,534 +2686,3 @@ testNoGroupDirectConns4Members hostVRange mem2VRange mem3VRange mem4VRange noCon
|
|||||||
cc1 <## ("no contact " <> name2)
|
cc1 <## ("no contact " <> name2)
|
||||||
cc2 ##> ("@" <> name1 <> " hi")
|
cc2 ##> ("@" <> name1 <> " hi")
|
||||||
cc2 <## ("no contact " <> name1)
|
cc2 <## ("no contact " <> name1)
|
||||||
|
|
||||||
testGroupDirectMessages :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectMessages =
|
|
||||||
testChat4 aliceProfile bobProfile cathProfile danProfile $ \alice bob cath dan -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
connectUsers alice dan
|
|
||||||
addMember "team" alice dan GRMember
|
|
||||||
dan ##> "/j team"
|
|
||||||
concurrentlyN_
|
|
||||||
[ alice <## "#team: dan joined the group",
|
|
||||||
do
|
|
||||||
dan <## "#team: you joined the group"
|
|
||||||
dan
|
|
||||||
<### [ "#team: member bob (Bob) is connected",
|
|
||||||
"#team: member cath (Catherine) is connected"
|
|
||||||
],
|
|
||||||
aliceAddedDan bob,
|
|
||||||
aliceAddedDan cath
|
|
||||||
]
|
|
||||||
|
|
||||||
alice #> "#team hi"
|
|
||||||
bob <# "#team alice> hi"
|
|
||||||
cath <# "#team alice> hi"
|
|
||||||
dan <# "#team alice> hi"
|
|
||||||
|
|
||||||
alice `send` "#team @bob hi bob"
|
|
||||||
alice <# "#team @bob (direct) hi bob"
|
|
||||||
bob <# "#team alice (direct)> hi bob"
|
|
||||||
|
|
||||||
bob `send` "#team @alice hi alice"
|
|
||||||
bob <# "#team @alice (direct) hi alice"
|
|
||||||
alice <# "#team bob (direct)> hi alice"
|
|
||||||
|
|
||||||
dan #> "#team hello"
|
|
||||||
alice <# "#team dan> hello"
|
|
||||||
bob <# "#team dan> hello"
|
|
||||||
cath <# "#team dan> hello"
|
|
||||||
|
|
||||||
bob `send` "#team @cath hi cath"
|
|
||||||
bob <# "#team @cath (direct) hi cath"
|
|
||||||
cath <# "#team bob (direct)> hi cath"
|
|
||||||
|
|
||||||
cath `send` "#team @bob hello bob"
|
|
||||||
cath <# "#team @bob (direct) hello bob"
|
|
||||||
bob <# "#team cath (direct)> hello bob"
|
|
||||||
where
|
|
||||||
aliceAddedDan :: HasCallStack => TestCC -> IO ()
|
|
||||||
aliceAddedDan cc = do
|
|
||||||
cc <## "#team: alice added dan (Daniel) to the group (connecting...)"
|
|
||||||
cc <## "#team: new member dan is connected"
|
|
||||||
|
|
||||||
testGroupDirectMessagesItems :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectMessagesItems =
|
|
||||||
testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
alice #> "#team hi"
|
|
||||||
bob <# "#team alice> hi"
|
|
||||||
cath <# "#team alice> hi"
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
alice `send` "#team @bob hi bob"
|
|
||||||
alice <# "#team @bob (direct) hi bob"
|
|
||||||
bob <# "#team alice (direct)> hi bob"
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
bob `send` "#team @alice hi alice"
|
|
||||||
bob <# "#team @alice (direct) hi alice"
|
|
||||||
alice <# "#team bob (direct)> hi alice"
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
alice #$> ("/_get chat #1 count=4", mapChat, [(0, "", "connected"), (1, "", "hi"), (1, "bob", "hi bob"), (0, "bob", "hi alice")])
|
|
||||||
bob #$> ("/_get chat #1 count=4", mapChat, [(0, "", "connected"), (0, "", "hi"), (0, "alice", "hi bob"), (1, "alice", "hi alice")])
|
|
||||||
cath #$> ("/_get chat #1 count=2", mapChat, [(0, "", "connected"), (0, "", "hi")])
|
|
||||||
where
|
|
||||||
mapChat = map (\(a, _, _) -> a) . chat'''
|
|
||||||
|
|
||||||
testGroupDirectQuotes :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectQuotes =
|
|
||||||
testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
|
|
||||||
alice #> "#team 1-g-a"
|
|
||||||
bob <# "#team alice> 1-g-a"
|
|
||||||
cath <# "#team alice> 1-g-a"
|
|
||||||
|
|
||||||
bob #> "#team 2-g-b"
|
|
||||||
alice <# "#team bob> 2-g-b"
|
|
||||||
cath <# "#team bob> 2-g-b"
|
|
||||||
|
|
||||||
cath #> "#team 3-g-c"
|
|
||||||
alice <# "#team cath> 3-g-c"
|
|
||||||
bob <# "#team cath> 3-g-c"
|
|
||||||
|
|
||||||
alice `send` "#team @bob 4-p-ab"
|
|
||||||
alice <# "#team @bob (direct) 4-p-ab"
|
|
||||||
bob <# "#team alice (direct)> 4-p-ab"
|
|
||||||
|
|
||||||
bob `send` "#team @alice 5-p-ba"
|
|
||||||
bob <# "#team @alice (direct) 5-p-ba"
|
|
||||||
alice <# "#team bob (direct)> 5-p-ba"
|
|
||||||
|
|
||||||
alice `send` "#team @cath 6-p-ac"
|
|
||||||
alice <# "#team @cath (direct) 6-p-ac"
|
|
||||||
cath <# "#team alice (direct)> 6-p-ac"
|
|
||||||
|
|
||||||
cath `send` "#team @alice 7-p-ca"
|
|
||||||
cath <# "#team @alice (direct) 7-p-ca"
|
|
||||||
alice <# "#team cath (direct)> 7-p-ca"
|
|
||||||
|
|
||||||
-- quotes
|
|
||||||
|
|
||||||
alice `send` "> #team @bob (1-g-a) 8-pq-ab"
|
|
||||||
alice <# "#team @bob (direct) > alice 1-g-a"
|
|
||||||
alice <## " 8-pq-ab"
|
|
||||||
bob <# "#team alice (direct)> > alice 1-g-a"
|
|
||||||
bob <## " 8-pq-ab"
|
|
||||||
|
|
||||||
alice `send` "> #team @bob (2-g-b) 9-pq-ab"
|
|
||||||
alice <# "#team @bob (direct) > bob 2-g-b"
|
|
||||||
alice <## " 9-pq-ab"
|
|
||||||
bob <# "#team alice (direct)> > bob 2-g-b"
|
|
||||||
bob <## " 9-pq-ab"
|
|
||||||
|
|
||||||
alice `send` "> #team >@cath @bob (3-g-c) 10-pq-ab"
|
|
||||||
alice <# "#team @bob (direct) > cath 3-g-c"
|
|
||||||
alice <## " 10-pq-ab"
|
|
||||||
bob <# "#team alice (direct)> > cath 3-g-c"
|
|
||||||
bob <## " 10-pq-ab"
|
|
||||||
|
|
||||||
alice `send` "> #team @bob (4-p-ab) 11-pq-ab"
|
|
||||||
alice <# "#team @bob (direct) > alice 4-p-ab"
|
|
||||||
alice <## " 11-pq-ab"
|
|
||||||
bob <# "#team alice (direct)> > alice 4-p-ab"
|
|
||||||
bob <## " 11-pq-ab"
|
|
||||||
|
|
||||||
alice `send` "> #team >@bob @bob (5-p-ba) 12-pq-ab"
|
|
||||||
alice <# "#team @bob (direct) > bob 5-p-ba"
|
|
||||||
alice <## " 12-pq-ab"
|
|
||||||
bob <# "#team alice (direct)> > bob 5-p-ba"
|
|
||||||
bob <## " 12-pq-ab"
|
|
||||||
|
|
||||||
alice `send` "> #team @bob (6-p-ac) 13-pq-ab"
|
|
||||||
alice <## "> #team @bob (6-p-ac) 13-pq-ab"
|
|
||||||
alice <## "invalid message reply"
|
|
||||||
|
|
||||||
alice `send` "> #team @bob (7-p-ca) 14-pq-ab"
|
|
||||||
alice <## "> #team @bob (7-p-ca) 14-pq-ab"
|
|
||||||
alice <## "invalid message reply"
|
|
||||||
|
|
||||||
alice `send` "> #team (4-p-ab) 15-gq-a"
|
|
||||||
alice <## "> #team (4-p-ab) 15-gq-a"
|
|
||||||
alice <## "invalid message reply"
|
|
||||||
|
|
||||||
alice `send` "> #team (5-p-ba) 16-gq-a"
|
|
||||||
alice <## "> #team (5-p-ba) 16-gq-a"
|
|
||||||
alice <## "invalid message reply"
|
|
||||||
|
|
||||||
testGroupDirectQuotesItems :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectQuotesItems =
|
|
||||||
testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
|
|
||||||
alice #> "#team 1-g-a"
|
|
||||||
bob <# "#team alice> 1-g-a"
|
|
||||||
cath <# "#team alice> 1-g-a"
|
|
||||||
|
|
||||||
alice `send` "#team @bob 2-p-ab"
|
|
||||||
alice <# "#team @bob (direct) 2-p-ab"
|
|
||||||
bob <# "#team alice (direct)> 2-p-ab"
|
|
||||||
|
|
||||||
bob `send` "#team @alice 3-p-ba"
|
|
||||||
bob <# "#team @alice (direct) 3-p-ba"
|
|
||||||
alice <# "#team bob (direct)> 3-p-ba"
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
-- quotes
|
|
||||||
|
|
||||||
alice `send` "> #team @bob (1-g-a) 4-pq-ab"
|
|
||||||
alice <# "#team @bob (direct) > alice 1-g-a"
|
|
||||||
alice <## " 4-pq-ab"
|
|
||||||
bob <# "#team alice (direct)> > alice 1-g-a"
|
|
||||||
bob <## " 4-pq-ab"
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
alice `send` "> #team @bob (2-p-ab) 5-pq-ab"
|
|
||||||
alice <# "#team @bob (direct) > alice 2-p-ab"
|
|
||||||
alice <## " 5-pq-ab"
|
|
||||||
bob <# "#team alice (direct)> > alice 2-p-ab"
|
|
||||||
bob <## " 5-pq-ab"
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
alice `send` "> #team >@bob @bob (3-p-ba) 6-pq-ab"
|
|
||||||
alice <# "#team @bob (direct) > bob 3-p-ba"
|
|
||||||
alice <## " 6-pq-ab"
|
|
||||||
bob <# "#team alice (direct)> > bob 3-p-ba"
|
|
||||||
bob <## " 6-pq-ab"
|
|
||||||
|
|
||||||
alice
|
|
||||||
#$> ( "/_get chat #1 count=3",
|
|
||||||
mapChat,
|
|
||||||
[ ((1, "bob", "4-pq-ab"), Just (1, "group", "1-g-a")),
|
|
||||||
((1, "bob", "5-pq-ab"), Just (1, "direct", "2-p-ab")),
|
|
||||||
((1, "bob", "6-pq-ab"), Just (0, "direct", "3-p-ba"))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
bob
|
|
||||||
#$> ( "/_get chat #1 count=3",
|
|
||||||
mapChat,
|
|
||||||
[ ((0, "alice", "4-pq-ab"), Just (0, "group", "1-g-a")),
|
|
||||||
((0, "alice", "5-pq-ab"), Just (0, "direct", "2-p-ab")),
|
|
||||||
((0, "alice", "6-pq-ab"), Just (1, "direct", "3-p-ba"))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
where
|
|
||||||
mapChat = map (\(a, b, _) -> (a, b)) . chat'''
|
|
||||||
|
|
||||||
testGroupDirectFilesXFTP :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectFilesXFTP =
|
|
||||||
testChatCfg3 cfg aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
withXFTPServer $ do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
alice `send` "/f #team @bob ./tests/fixtures/test.pdf"
|
|
||||||
alice <# "/f #team @bob (direct) ./tests/fixtures/test.pdf"
|
|
||||||
alice <## "use /fc 1 to cancel sending"
|
|
||||||
bob <# "#team alice (direct)> sends file test.pdf (266.0 KiB / 272376 bytes)"
|
|
||||||
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
|
||||||
alice <## "completed uploading file 1 (test.pdf) for #team @bob (direct)"
|
|
||||||
|
|
||||||
bob ##> "/fr 1 ./tests/tmp"
|
|
||||||
bob
|
|
||||||
<### [ "saving file 1 from alice to ./tests/tmp/test.pdf",
|
|
||||||
"started receiving file 1 (test.pdf) from alice"
|
|
||||||
]
|
|
||||||
bob <## "completed receiving file 1 (test.pdf) from alice"
|
|
||||||
|
|
||||||
src <- B.readFile "./tests/fixtures/test.pdf"
|
|
||||||
dest <- B.readFile "./tests/tmp/test.pdf"
|
|
||||||
dest `shouldBe` src
|
|
||||||
|
|
||||||
cath <// 50000
|
|
||||||
cath ##> "/fr 1 ./tests/tmp"
|
|
||||||
cath <##. "chat db error: SEUserNotFoundByFileId"
|
|
||||||
|
|
||||||
alice `send` "/f #team @cath ./tests/fixtures/test.jpg"
|
|
||||||
alice <# "/f #team @cath (direct) ./tests/fixtures/test.jpg"
|
|
||||||
alice <## "use /fc 2 to cancel sending"
|
|
||||||
cath <# "#team alice (direct)> sends file test.jpg (136.5 KiB / 139737 bytes)"
|
|
||||||
cath <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
|
||||||
alice <## "completed uploading file 2 (test.jpg) for #team @cath (direct)"
|
|
||||||
|
|
||||||
cath ##> "/fr 1 ./tests/tmp"
|
|
||||||
cath
|
|
||||||
<### [ "saving file 1 from alice to ./tests/tmp/test.jpg",
|
|
||||||
"started receiving file 1 (test.jpg) from alice"
|
|
||||||
]
|
|
||||||
cath <## "completed receiving file 1 (test.jpg) from alice"
|
|
||||||
|
|
||||||
src2 <- B.readFile "./tests/fixtures/test.jpg"
|
|
||||||
dest2 <- B.readFile "./tests/tmp/test.jpg"
|
|
||||||
dest2 `shouldBe` src2
|
|
||||||
|
|
||||||
bob <// 50000
|
|
||||||
|
|
||||||
alice #$> ("/_get chat #1 count=2", mapChat, [((1, "bob", ""), Just "./tests/fixtures/test.pdf"), ((1, "cath", ""), Just "./tests/fixtures/test.jpg")])
|
|
||||||
bob #$> ("/_get chat #1 count=2", mapChat, [((0, "", "connected"), Nothing), ((0, "alice", ""), Just "./tests/tmp/test.pdf")])
|
|
||||||
cath #$> ("/_get chat #1 count=2", mapChat, [((0, "", "connected"), Nothing), ((0, "alice", ""), Just "./tests/tmp/test.jpg")])
|
|
||||||
where
|
|
||||||
cfg = testCfg {xftpFileConfig = Just $ XFTPFileConfig {minFileSize = 0}, tempDir = Just "./tests/tmp"}
|
|
||||||
mapChat = map (\(a, _, c) -> (a, c)) . chat'''
|
|
||||||
|
|
||||||
testGroupDirectFilesSMP :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectFilesSMP =
|
|
||||||
testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
alice `send` "/f #team @bob ./tests/fixtures/test.pdf"
|
|
||||||
alice <# "/f #team @bob (direct) ./tests/fixtures/test.pdf"
|
|
||||||
alice <## "use /fc 1 to cancel sending"
|
|
||||||
bob <# "#team alice (direct)> sends file test.pdf (266.0 KiB / 272376 bytes)"
|
|
||||||
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
|
||||||
bob ##> "/fr 1 ./tests/tmp"
|
|
||||||
bob <## "saving file 1 from alice to ./tests/tmp/test.pdf"
|
|
||||||
concurrently_
|
|
||||||
(alice <## "started sending file 1 (test.pdf) to bob")
|
|
||||||
(bob <## "started receiving file 1 (test.pdf) from alice")
|
|
||||||
concurrently_
|
|
||||||
(alice <## "completed sending file 1 (test.pdf) to bob")
|
|
||||||
(bob <## "completed receiving file 1 (test.pdf) from alice")
|
|
||||||
|
|
||||||
src <- B.readFile "./tests/fixtures/test.pdf"
|
|
||||||
dest <- B.readFile "./tests/tmp/test.pdf"
|
|
||||||
dest `shouldBe` src
|
|
||||||
|
|
||||||
cath <// 50000
|
|
||||||
cath ##> "/fr 1 ./tests/tmp"
|
|
||||||
cath <##. "chat db error: SEUserNotFoundByFileId"
|
|
||||||
|
|
||||||
alice `send` "/f #team @cath ./tests/fixtures/test.jpg"
|
|
||||||
alice <# "/f #team @cath (direct) ./tests/fixtures/test.jpg"
|
|
||||||
alice <## "use /fc 2 to cancel sending"
|
|
||||||
cath <# "#team alice (direct)> sends file test.jpg (136.5 KiB / 139737 bytes)"
|
|
||||||
cath <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
|
||||||
cath ##> "/fr 1 ./tests/tmp"
|
|
||||||
cath <## "saving file 1 from alice to ./tests/tmp/test.jpg"
|
|
||||||
concurrently_
|
|
||||||
(alice <## "started sending file 2 (test.jpg) to cath")
|
|
||||||
(cath <## "started receiving file 1 (test.jpg) from alice")
|
|
||||||
concurrently_
|
|
||||||
(alice <## "completed sending file 2 (test.jpg) to cath")
|
|
||||||
(cath <## "completed receiving file 1 (test.jpg) from alice")
|
|
||||||
|
|
||||||
src2 <- B.readFile "./tests/fixtures/test.jpg"
|
|
||||||
dest2 <- B.readFile "./tests/tmp/test.jpg"
|
|
||||||
dest2 `shouldBe` src2
|
|
||||||
|
|
||||||
bob <// 50000
|
|
||||||
|
|
||||||
alice #$> ("/_get chat #1 count=2", mapChat, [((1, "bob", ""), Just "./tests/fixtures/test.pdf"), ((1, "cath", ""), Just "./tests/fixtures/test.jpg")])
|
|
||||||
bob #$> ("/_get chat #1 count=2", mapChat, [((0, "", "connected"), Nothing), ((0, "alice", ""), Just "./tests/tmp/test.pdf")])
|
|
||||||
cath #$> ("/_get chat #1 count=2", mapChat, [((0, "", "connected"), Nothing), ((0, "alice", ""), Just "./tests/tmp/test.jpg")])
|
|
||||||
where
|
|
||||||
mapChat = map (\(a, _, c) -> (a, c)) . chat'''
|
|
||||||
|
|
||||||
testGroupDirectCancelFileXFTP :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectCancelFileXFTP =
|
|
||||||
testChatCfg3 cfg aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
withXFTPServer $ do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
|
|
||||||
alice `send` "/f #team @bob ./tests/fixtures/test.pdf"
|
|
||||||
alice <# "/f #team @bob (direct) ./tests/fixtures/test.pdf"
|
|
||||||
alice <## "use /fc 1 to cancel sending"
|
|
||||||
bob <# "#team alice (direct)> sends file test.pdf (266.0 KiB / 272376 bytes)"
|
|
||||||
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
|
||||||
alice <## "completed uploading file 1 (test.pdf) for #team @bob (direct)"
|
|
||||||
|
|
||||||
cath <// 50000
|
|
||||||
|
|
||||||
alice ##> "/fc 1"
|
|
||||||
alice <## "cancelled sending file 1 (test.pdf) to bob"
|
|
||||||
bob <## "alice cancelled sending file 1 (test.pdf)"
|
|
||||||
|
|
||||||
cath <// 50000
|
|
||||||
|
|
||||||
bob ##> "/fr 1 ./tests/tmp"
|
|
||||||
bob <## "file cancelled: test.pdf"
|
|
||||||
|
|
||||||
cath ##> "/fr 1 ./tests/tmp"
|
|
||||||
cath <##. "chat db error: SEUserNotFoundByFileId"
|
|
||||||
where
|
|
||||||
cfg = testCfg {xftpFileConfig = Just $ XFTPFileConfig {minFileSize = 0}, tempDir = Just "./tests/tmp"}
|
|
||||||
|
|
||||||
testGroupDirectQuotesFiles :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectQuotesFiles =
|
|
||||||
testChatCfg3 cfg aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
withXFTPServer $ do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
bob `send` "#team @alice hi alice"
|
|
||||||
bob <# "#team @alice (direct) hi alice"
|
|
||||||
alice <# "#team bob (direct)> hi alice"
|
|
||||||
threadDelay 1000000
|
|
||||||
|
|
||||||
msgItemId1 <- lastItemId alice
|
|
||||||
alice ##> ("/_send #1 @2 json {\"filePath\": \"./tests/fixtures/test.pdf\", \"quotedItemId\": " <> msgItemId1 <> ", \"msgContent\": {\"text\":\"hey bob\",\"type\":\"file\"}}")
|
|
||||||
alice <# "#team @bob (direct) > bob hi alice"
|
|
||||||
alice <## " hey bob"
|
|
||||||
alice <# "/f #team @bob (direct) ./tests/fixtures/test.pdf"
|
|
||||||
alice <## "use /fc 1 to cancel sending"
|
|
||||||
bob <# "#team alice (direct)> > bob hi alice"
|
|
||||||
bob <## " hey bob"
|
|
||||||
bob <# "#team alice (direct)> sends file test.pdf (266.0 KiB / 272376 bytes)"
|
|
||||||
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
|
|
||||||
alice <## "completed uploading file 1 (test.pdf) for #team @bob (direct)"
|
|
||||||
|
|
||||||
bob ##> "/fr 1 ./tests/tmp"
|
|
||||||
bob
|
|
||||||
<### [ "saving file 1 from alice to ./tests/tmp/test.pdf",
|
|
||||||
"started receiving file 1 (test.pdf) from alice"
|
|
||||||
]
|
|
||||||
bob <## "completed receiving file 1 (test.pdf) from alice"
|
|
||||||
|
|
||||||
src <- B.readFile "./tests/fixtures/test.pdf"
|
|
||||||
dest <- B.readFile "./tests/tmp/test.pdf"
|
|
||||||
dest `shouldBe` src
|
|
||||||
|
|
||||||
cath <// 50000
|
|
||||||
cath ##> "/fr 1 ./tests/tmp"
|
|
||||||
cath <##. "chat db error: SEUserNotFoundByFileId"
|
|
||||||
|
|
||||||
alice
|
|
||||||
#$> ( "/_get chat #1 count=2",
|
|
||||||
chat''',
|
|
||||||
[ ((0, "bob", "hi alice"), Nothing, Nothing),
|
|
||||||
((1, "bob", "hey bob"), Just (0, "direct", "hi alice"), Just "./tests/fixtures/test.pdf")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
bob
|
|
||||||
#$> ( "/_get chat #1 count=2",
|
|
||||||
chat''',
|
|
||||||
[ ((1, "alice", "hi alice"), Nothing, Nothing),
|
|
||||||
((0, "alice", "hey bob"), Just (1, "direct", "hi alice"), Just "./tests/tmp/test.pdf")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
cath #$> ("/_get chat #1 count=1", chat''', [((0, "", "connected"), Nothing, Nothing)])
|
|
||||||
where
|
|
||||||
cfg = testCfg {xftpFileConfig = Just $ XFTPFileConfig {minFileSize = 0}, tempDir = Just "./tests/tmp"}
|
|
||||||
|
|
||||||
testGroupDirectUpdate :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectUpdate =
|
|
||||||
testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
|
|
||||||
alice `send` "#team @bob hi bob"
|
|
||||||
alice <# "#team @bob (direct) hi bob"
|
|
||||||
bob <# "#team alice (direct)> hi bob"
|
|
||||||
|
|
||||||
msgItemId1 <- lastItemId alice
|
|
||||||
alice ##> ("/_update item #1 " <> msgItemId1 <> " text hey 👋")
|
|
||||||
alice <# "#team @bob (direct) [edited] hey 👋"
|
|
||||||
bob <# "#team alice (direct)> [edited] hey 👋"
|
|
||||||
cath <// 50000
|
|
||||||
|
|
||||||
alice ##> "! #team (hey 👋) hello there"
|
|
||||||
alice <# "#team @bob (direct) [edited] hello there"
|
|
||||||
bob <# "#team alice (direct)> [edited] hello there"
|
|
||||||
cath <// 50000
|
|
||||||
|
|
||||||
testGroupDirectDelete :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectDelete =
|
|
||||||
testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
|
|
||||||
alice `send` "#team @bob hi bob"
|
|
||||||
alice <# "#team @bob (direct) hi bob"
|
|
||||||
bob <# "#team alice (direct)> hi bob"
|
|
||||||
|
|
||||||
msgItemId1 <- lastItemId alice
|
|
||||||
alice #$> ("/_delete item #1 " <> msgItemId1 <> " broadcast", id, "message marked deleted")
|
|
||||||
bob <# "#team alice (direct)> [marked deleted] hi bob"
|
|
||||||
cath <// 50000
|
|
||||||
|
|
||||||
testGroupDirectLiveMessage :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectLiveMessage =
|
|
||||||
testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
|
|
||||||
alice `send` "/live #team @bob hello"
|
|
||||||
msgItemId1 <- lastItemId alice
|
|
||||||
bob <#. "#team alice (direct)> [LIVE started]"
|
|
||||||
alice ##> ("/_update item #1 " <> msgItemId1 <> " text hello there")
|
|
||||||
alice <# "#team @bob (direct) [LIVE] hello there"
|
|
||||||
bob <# "#team alice (direct)> [LIVE ended] hello there"
|
|
||||||
cath <// 50000
|
|
||||||
|
|
||||||
testGroupDirectReactions :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectReactions =
|
|
||||||
testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
|
|
||||||
alice `send` "#team @bob hi bob"
|
|
||||||
alice <# "#team @bob (direct) hi bob"
|
|
||||||
bob <# "#team alice (direct)> hi bob"
|
|
||||||
|
|
||||||
bob ##> "+1 #team hi"
|
|
||||||
bob <## "added 👍"
|
|
||||||
alice <# "#team bob (direct)> > alice hi bob"
|
|
||||||
alice <## " + 👍"
|
|
||||||
cath <// 50000
|
|
||||||
|
|
||||||
alice ##> "+^ #team hi"
|
|
||||||
alice <## "added 🚀"
|
|
||||||
bob <# "#team alice (direct)> > alice hi bob"
|
|
||||||
bob <## " + 🚀"
|
|
||||||
cath <// 50000
|
|
||||||
|
|
||||||
testGroupDirectProhibitPreference :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectProhibitPreference =
|
|
||||||
testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do
|
|
||||||
createGroup3' "team" alice bob cath GRMember
|
|
||||||
|
|
||||||
alice ##> "/set direct #team off"
|
|
||||||
alice <## "updated group preferences:"
|
|
||||||
alice <## "Direct messages: off"
|
|
||||||
directProhibited bob
|
|
||||||
directProhibited cath
|
|
||||||
|
|
||||||
bob ##> "#team @cath hi cath"
|
|
||||||
bob <## "bad chat command: direct messages not allowed"
|
|
||||||
|
|
||||||
cath ##> "#team @bob hi cath"
|
|
||||||
cath <## "bad chat command: direct messages not allowed"
|
|
||||||
|
|
||||||
alice ##> "/mr team bob admin"
|
|
||||||
alice <## "#team: you changed the role of bob from member to admin"
|
|
||||||
concurrentlyN_
|
|
||||||
[ bob <## "#team: alice changed your role from member to admin",
|
|
||||||
cath <## "#team: alice changed the role of bob from member to admin"
|
|
||||||
]
|
|
||||||
|
|
||||||
-- admin can send & can send to admin
|
|
||||||
|
|
||||||
bob `send` "#team @cath hi cath, as admin"
|
|
||||||
bob <# "#team @cath (direct) hi cath, as admin"
|
|
||||||
cath <# "#team bob (direct)> hi cath, as admin"
|
|
||||||
|
|
||||||
cath `send` "#team @bob hi bob, to admin"
|
|
||||||
cath <# "#team @bob (direct) hi bob, to admin"
|
|
||||||
bob <# "#team cath (direct)> hi bob, to admin"
|
|
||||||
where
|
|
||||||
directProhibited :: HasCallStack => TestCC -> IO ()
|
|
||||||
directProhibited cc = do
|
|
||||||
cc <## "alice updated group #team:"
|
|
||||||
cc <## "updated group preferences:"
|
|
||||||
cc <## "Direct messages: off"
|
|
||||||
|
|
||||||
testGroupDirectProhibitNotSupported :: HasCallStack => FilePath -> IO ()
|
|
||||||
testGroupDirectProhibitNotSupported tmp =
|
|
||||||
withNewTestChat tmp "alice" aliceProfile $ \alice -> do
|
|
||||||
withNewTestChat tmp "bob" bobProfile $ \bob -> do
|
|
||||||
withNewTestChatCfg tmp testCfg {chatVRange = mkVersionRange 1 1} "cath" cathProfile $ \cath -> do
|
|
||||||
createGroup3 "team" alice bob cath
|
|
||||||
|
|
||||||
bob ##> "#team @cath hi cath"
|
|
||||||
bob <## "peer chat protocol version range incompatible"
|
|
||||||
|
@ -181,12 +181,7 @@ chatF :: String -> [((Int, String), Maybe String)]
|
|||||||
chatF = map (\(a, _, c) -> (a, c)) . chat''
|
chatF = map (\(a, _, c) -> (a, c)) . chat''
|
||||||
|
|
||||||
chat'' :: String -> [((Int, String), Maybe (Int, String), Maybe String)]
|
chat'' :: String -> [((Int, String), Maybe (Int, String), Maybe String)]
|
||||||
chat'' = map (\(a, b, c) -> (mapNoDirect a, mapNoDirect <$> b, c)) . chat'''
|
chat'' = read
|
||||||
where
|
|
||||||
mapNoDirect (a1, _, a3) = (a1, a3)
|
|
||||||
|
|
||||||
chat''' :: String -> [((Int, String, String), Maybe (Int, String, String), Maybe String)]
|
|
||||||
chat''' = read
|
|
||||||
|
|
||||||
chatFeatures :: [(Int, String)]
|
chatFeatures :: [(Int, String)]
|
||||||
chatFeatures = map (\(a, _, _) -> a) chatFeatures''
|
chatFeatures = map (\(a, _, _) -> a) chatFeatures''
|
||||||
@ -461,33 +456,27 @@ showName (TestCC ChatController {currentUser} _ _ _ _ _) = do
|
|||||||
pure . T.unpack $ localDisplayName <> optionalFullName localDisplayName fullName
|
pure . T.unpack $ localDisplayName <> optionalFullName localDisplayName fullName
|
||||||
|
|
||||||
createGroup2 :: HasCallStack => String -> TestCC -> TestCC -> IO ()
|
createGroup2 :: HasCallStack => String -> TestCC -> TestCC -> IO ()
|
||||||
createGroup2 gName cc1 cc2 = createGroup2' gName cc1 cc2 GRAdmin
|
createGroup2 gName cc1 cc2 = do
|
||||||
|
|
||||||
createGroup2' :: HasCallStack => String -> TestCC -> TestCC -> GroupMemberRole -> IO ()
|
|
||||||
createGroup2' gName cc1 cc2 memberRole = do
|
|
||||||
connectUsers cc1 cc2
|
connectUsers cc1 cc2
|
||||||
name2 <- userName cc2
|
name2 <- userName cc2
|
||||||
cc1 ##> ("/g " <> gName)
|
cc1 ##> ("/g " <> gName)
|
||||||
cc1 <## ("group #" <> gName <> " is created")
|
cc1 <## ("group #" <> gName <> " is created")
|
||||||
cc1 <## ("to add members use /a " <> gName <> " <name> or /create link #" <> gName)
|
cc1 <## ("to add members use /a " <> gName <> " <name> or /create link #" <> gName)
|
||||||
addMember gName cc1 cc2 memberRole
|
addMember gName cc1 cc2 GRAdmin
|
||||||
cc2 ##> ("/j " <> gName)
|
cc2 ##> ("/j " <> gName)
|
||||||
concurrently_
|
concurrently_
|
||||||
(cc1 <## ("#" <> gName <> ": " <> name2 <> " joined the group"))
|
(cc1 <## ("#" <> gName <> ": " <> name2 <> " joined the group"))
|
||||||
(cc2 <## ("#" <> gName <> ": you joined the group"))
|
(cc2 <## ("#" <> gName <> ": you joined the group"))
|
||||||
|
|
||||||
createGroup3 :: HasCallStack => String -> TestCC -> TestCC -> TestCC -> IO ()
|
createGroup3 :: HasCallStack => String -> TestCC -> TestCC -> TestCC -> IO ()
|
||||||
createGroup3 gName cc1 cc2 cc3 = createGroup3' gName cc1 cc2 cc3 GRAdmin
|
createGroup3 gName cc1 cc2 cc3 = do
|
||||||
|
createGroup2 gName cc1 cc2
|
||||||
createGroup3' :: HasCallStack => String -> TestCC -> TestCC -> TestCC -> GroupMemberRole -> IO ()
|
|
||||||
createGroup3' gName cc1 cc2 cc3 memberRole = do
|
|
||||||
createGroup2' gName cc1 cc2 memberRole
|
|
||||||
connectUsers cc1 cc3
|
connectUsers cc1 cc3
|
||||||
name1 <- userName cc1
|
name1 <- userName cc1
|
||||||
name3 <- userName cc3
|
name3 <- userName cc3
|
||||||
sName2 <- showName cc2
|
sName2 <- showName cc2
|
||||||
sName3 <- showName cc3
|
sName3 <- showName cc3
|
||||||
addMember gName cc1 cc3 memberRole
|
addMember gName cc1 cc3 GRAdmin
|
||||||
cc3 ##> ("/j " <> gName)
|
cc3 ##> ("/j " <> gName)
|
||||||
concurrentlyN_
|
concurrentlyN_
|
||||||
[ cc1 <## ("#" <> gName <> ": " <> name3 <> " joined the group"),
|
[ cc1 <## ("#" <> gName <> ": " <> name3 <> " joined the group"),
|
||||||
|
@ -57,7 +57,7 @@ testConnReq = CRInvitationUri connReqData testE2ERatchetParams
|
|||||||
quotedMsg :: QuotedMsg
|
quotedMsg :: QuotedMsg
|
||||||
quotedMsg =
|
quotedMsg =
|
||||||
QuotedMsg
|
QuotedMsg
|
||||||
(MsgRef (Just $ SharedMsgId "\5\6\7\8") (systemToUTCTime $ MkSystemTime 1 1) True Nothing Nothing)
|
(MsgRef (Just $ SharedMsgId "\5\6\7\8") (systemToUTCTime $ MkSystemTime 1 1) True Nothing)
|
||||||
$ MCText "hello there!"
|
$ MCText "hello there!"
|
||||||
|
|
||||||
(==##) :: MsgEncodingI e => ByteString -> ChatMessage e -> Expectation
|
(==##) :: MsgEncodingI e => ByteString -> ChatMessage e -> Expectation
|
||||||
@ -105,13 +105,10 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
|||||||
#==# XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing))
|
#==# XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing))
|
||||||
it "x.msg.new simple text - timed message TTL" $
|
it "x.msg.new simple text - timed message TTL" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"ttl\":3600}}"
|
"{\"v\":\"1\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"ttl\":3600}}"
|
||||||
#==# XMsgNew (MCSimple (ExtMsgContent (MCText "hello") Nothing (Just 3600) Nothing Nothing))
|
#==# XMsgNew (MCSimple (ExtMsgContent (MCText "hello") Nothing (Just 3600) Nothing))
|
||||||
it "x.msg.new simple text - live message" $
|
it "x.msg.new simple text - live message" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"live\":true}}"
|
"{\"v\":\"1\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"live\":true}}"
|
||||||
#==# XMsgNew (MCSimple (ExtMsgContent (MCText "hello") Nothing Nothing (Just True) Nothing))
|
#==# XMsgNew (MCSimple (ExtMsgContent (MCText "hello") Nothing Nothing (Just True)))
|
||||||
it "x.msg.new simple text - direct message scope" $
|
|
||||||
"{\"v\":\"1\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"scope\":\"direct\"}}"
|
|
||||||
#==# XMsgNew (MCSimple (ExtMsgContent (MCText "hello") Nothing Nothing Nothing (Just MSDirect)))
|
|
||||||
it "x.msg.new simple link" $
|
it "x.msg.new simple link" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"https://simplex.chat\",\"type\":\"link\",\"preview\":{\"description\":\"SimpleX Chat\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgA\",\"title\":\"SimpleX Chat\",\"uri\":\"https://simplex.chat\"}}}}"
|
"{\"v\":\"1\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"https://simplex.chat\",\"type\":\"link\",\"preview\":{\"description\":\"SimpleX Chat\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgA\",\"title\":\"SimpleX Chat\",\"uri\":\"https://simplex.chat\"}}}}"
|
||||||
#==# XMsgNew (MCSimple (extMsgContent (MCLink "https://simplex.chat" $ LinkPreview {uri = "https://simplex.chat", title = "SimpleX Chat", description = "SimpleX Chat", image = ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgA", content = Nothing}) Nothing))
|
#==# XMsgNew (MCSimple (extMsgContent (MCLink "https://simplex.chat" $ LinkPreview {uri = "https://simplex.chat", title = "SimpleX Chat", description = "SimpleX Chat", image = ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgA", content = Nothing}) Nothing))
|
||||||
@ -133,41 +130,27 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
|||||||
chatInitialVRange
|
chatInitialVRange
|
||||||
(Just $ SharedMsgId "\1\2\3\4")
|
(Just $ SharedMsgId "\1\2\3\4")
|
||||||
(XMsgNew (MCQuote quotedMsg (extMsgContent (MCText "hello to you too") Nothing)))
|
(XMsgNew (MCQuote quotedMsg (extMsgContent (MCText "hello to you too") Nothing)))
|
||||||
it "x.msg.new quote - direct referenced message scope" $
|
|
||||||
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\",\"msgScope\":\"direct\"}}}}"
|
|
||||||
##==## ChatMessage
|
|
||||||
chatInitialVRange
|
|
||||||
(Just $ SharedMsgId "\1\2\3\4")
|
|
||||||
( XMsgNew
|
|
||||||
( MCQuote
|
|
||||||
( QuotedMsg
|
|
||||||
(MsgRef (Just $ SharedMsgId "\5\6\7\8") (systemToUTCTime $ MkSystemTime 1 1) True Nothing (Just MSDirect))
|
|
||||||
$ MCText "hello there!"
|
|
||||||
)
|
|
||||||
(extMsgContent (MCText "hello to you too") Nothing)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
it "x.msg.new quote - timed message TTL" $
|
it "x.msg.new quote - timed message TTL" $
|
||||||
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}},\"ttl\":3600}}"
|
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}},\"ttl\":3600}}"
|
||||||
##==## ChatMessage
|
##==## ChatMessage
|
||||||
chatInitialVRange
|
chatInitialVRange
|
||||||
(Just $ SharedMsgId "\1\2\3\4")
|
(Just $ SharedMsgId "\1\2\3\4")
|
||||||
(XMsgNew (MCQuote quotedMsg (ExtMsgContent (MCText "hello to you too") Nothing (Just 3600) Nothing Nothing)))
|
(XMsgNew (MCQuote quotedMsg (ExtMsgContent (MCText "hello to you too") Nothing (Just 3600) Nothing)))
|
||||||
it "x.msg.new quote - live message" $
|
it "x.msg.new quote - live message" $
|
||||||
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}},\"live\":true}}"
|
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}},\"live\":true}}"
|
||||||
##==## ChatMessage
|
##==## ChatMessage
|
||||||
chatInitialVRange
|
chatInitialVRange
|
||||||
(Just $ SharedMsgId "\1\2\3\4")
|
(Just $ SharedMsgId "\1\2\3\4")
|
||||||
(XMsgNew (MCQuote quotedMsg (ExtMsgContent (MCText "hello to you too") Nothing Nothing (Just True) Nothing)))
|
(XMsgNew (MCQuote quotedMsg (ExtMsgContent (MCText "hello to you too") Nothing Nothing (Just True))))
|
||||||
it "x.msg.new forward" $
|
it "x.msg.new forward" $
|
||||||
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true}}"
|
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true}}"
|
||||||
##==## ChatMessage chatInitialVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (extMsgContent (MCText "hello") Nothing))
|
##==## ChatMessage chatInitialVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (extMsgContent (MCText "hello") Nothing))
|
||||||
it "x.msg.new forward - timed message TTL" $
|
it "x.msg.new forward - timed message TTL" $
|
||||||
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true,\"ttl\":3600}}"
|
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true,\"ttl\":3600}}"
|
||||||
##==## ChatMessage chatInitialVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (ExtMsgContent (MCText "hello") Nothing (Just 3600) Nothing Nothing))
|
##==## ChatMessage chatInitialVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (ExtMsgContent (MCText "hello") Nothing (Just 3600) Nothing))
|
||||||
it "x.msg.new forward - live message" $
|
it "x.msg.new forward - live message" $
|
||||||
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true,\"live\":true}}"
|
"{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true,\"live\":true}}"
|
||||||
##==## ChatMessage chatInitialVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (ExtMsgContent (MCText "hello") Nothing Nothing (Just True) Nothing))
|
##==## ChatMessage chatInitialVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (ExtMsgContent (MCText "hello") Nothing Nothing (Just True)))
|
||||||
it "x.msg.new simple text with file" $
|
it "x.msg.new simple text with file" $
|
||||||
"{\"v\":\"1\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"file\":{\"fileSize\":12345,\"fileName\":\"photo.jpg\"}}}"
|
"{\"v\":\"1\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"file\":{\"fileSize\":12345,\"fileName\":\"photo.jpg\"}}}"
|
||||||
#==# XMsgNew (MCSimple (extMsgContent (MCText "hello") (Just FileInvitation {fileName = "photo.jpg", fileSize = 12345, fileDigest = Nothing, fileConnReq = Nothing, fileInline = Nothing, fileDescr = Nothing})))
|
#==# XMsgNew (MCSimple (extMsgContent (MCText "hello") (Just FileInvitation {fileName = "photo.jpg", fileSize = 12345, fileDigest = Nothing, fileConnReq = Nothing, fileInline = Nothing, fileDescr = Nothing})))
|
||||||
|
Loading…
Reference in New Issue
Block a user