|
|
|
@@ -1,6 +1,7 @@
|
|
|
|
|
{-# LANGUAGE DataKinds #-}
|
|
|
|
|
{-# LANGUAGE DuplicateRecordFields #-}
|
|
|
|
|
{-# LANGUAGE GADTs #-}
|
|
|
|
|
{-# LANGUAGE KindSignatures #-}
|
|
|
|
|
{-# LANGUAGE LambdaCase #-}
|
|
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
@@ -109,14 +110,15 @@ import Data.Bifunctor (first)
|
|
|
|
|
import Data.ByteString.Char8 (ByteString)
|
|
|
|
|
import Data.Either (fromRight, rights)
|
|
|
|
|
import Data.Int (Int64)
|
|
|
|
|
import Data.List (sortOn)
|
|
|
|
|
import Data.List (sortBy)
|
|
|
|
|
import Data.Maybe (fromMaybe, isJust, mapMaybe)
|
|
|
|
|
import Data.Ord (Down (..))
|
|
|
|
|
import Data.Ord (Down (..), comparing)
|
|
|
|
|
import Data.Text (Text)
|
|
|
|
|
import Data.Time (addUTCTime)
|
|
|
|
|
import Data.Time.Clock (UTCTime (..), getCurrentTime)
|
|
|
|
|
import Database.SQLite.Simple (NamedParam (..), Only (..), (:.) (..))
|
|
|
|
|
import Database.SQLite.Simple (NamedParam (..), Only (..), Query, (:.) (..))
|
|
|
|
|
import Database.SQLite.Simple.QQ (sql)
|
|
|
|
|
import Simplex.Chat.Controller (ChatListQuery (..), ChatPagination (..), PaginationByTime (..))
|
|
|
|
|
import Simplex.Chat.Markdown
|
|
|
|
|
import Simplex.Chat.Messages
|
|
|
|
|
import Simplex.Chat.Messages.CIContent
|
|
|
|
@@ -467,7 +469,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
|
|
|
|
<$> DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
[sql|
|
|
|
|
|
SELECT i.chat_item_id,
|
|
|
|
|
SELECT i.chat_item_id,
|
|
|
|
|
-- GroupMember
|
|
|
|
|
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
|
|
|
|
|
m.member_status, m.show_messages, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
|
|
|
@@ -486,209 +488,402 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
|
|
|
|
ciQuoteGroup [] = ciQuote Nothing $ CIQGroupRcv Nothing
|
|
|
|
|
ciQuoteGroup ((Only itemId :. memberRow) : _) = ciQuote itemId . CIQGroupRcv . Just $ toGroupMember userContactId memberRow
|
|
|
|
|
|
|
|
|
|
getChatPreviews :: DB.Connection -> User -> Bool -> IO [AChat]
|
|
|
|
|
getChatPreviews db user withPCC = do
|
|
|
|
|
directChats <- getDirectChatPreviews_ db user
|
|
|
|
|
groupChats <- getGroupChatPreviews_ db user
|
|
|
|
|
cReqChats <- getContactRequestChatPreviews_ db user
|
|
|
|
|
connChats <- getContactConnectionChatPreviews_ db user withPCC
|
|
|
|
|
pure $ sortOn (Down . ts) (directChats <> groupChats <> cReqChats <> connChats)
|
|
|
|
|
getChatPreviews :: DB.Connection -> User -> Bool -> PaginationByTime -> ChatListQuery -> IO [Either StoreError AChat]
|
|
|
|
|
getChatPreviews db user withPCC pagination query = do
|
|
|
|
|
directChats <- findDirectChatPreviews_ db user pagination query
|
|
|
|
|
groupChats <- findGroupChatPreviews_ db user pagination query
|
|
|
|
|
cReqChats <- getContactRequestChatPreviews_ db user pagination query
|
|
|
|
|
connChats <- if withPCC then getContactConnectionChatPreviews_ db user pagination query else pure []
|
|
|
|
|
let refs = sortTake $ concat [directChats, groupChats, cReqChats, connChats]
|
|
|
|
|
mapM (runExceptT <$> getChatPreview) refs
|
|
|
|
|
where
|
|
|
|
|
ts :: AChat -> UTCTime
|
|
|
|
|
ts (AChat _ Chat {chatInfo, chatItems}) = case chatInfoChatTs chatInfo of
|
|
|
|
|
Just chatTs -> chatTs
|
|
|
|
|
Nothing -> case chatItems of
|
|
|
|
|
ci : _ -> max (chatItemTs ci) (chatInfoUpdatedAt chatInfo)
|
|
|
|
|
_ -> chatInfoUpdatedAt chatInfo
|
|
|
|
|
ts :: AChatPreviewData -> UTCTime
|
|
|
|
|
ts (ACPD _ cpd) = case cpd of
|
|
|
|
|
(DirectChatPD t _ _) -> t
|
|
|
|
|
(GroupChatPD t _ _) -> t
|
|
|
|
|
(ContactRequestPD t _) -> t
|
|
|
|
|
(ContactConnectionPD t _) -> t
|
|
|
|
|
sortTake = case pagination of
|
|
|
|
|
PTLast count -> take count . sortBy (comparing $ Down . ts)
|
|
|
|
|
PTAfter _ count -> reverse . take count . sortBy (comparing ts)
|
|
|
|
|
PTBefore _ count -> take count . sortBy (comparing $ Down . ts)
|
|
|
|
|
getChatPreview :: AChatPreviewData -> ExceptT StoreError IO AChat
|
|
|
|
|
getChatPreview (ACPD cType cpd) = case cType of
|
|
|
|
|
SCTDirect -> getDirectChatPreview_ db user cpd
|
|
|
|
|
SCTGroup -> getGroupChatPreview_ db user cpd
|
|
|
|
|
SCTContactRequest -> let (ContactRequestPD _ chat) = cpd in pure chat
|
|
|
|
|
SCTContactConnection -> let (ContactConnectionPD _ chat) = cpd in pure chat
|
|
|
|
|
|
|
|
|
|
getDirectChatPreviews_ :: DB.Connection -> User -> IO [AChat]
|
|
|
|
|
getDirectChatPreviews_ db user@User {userId} = do
|
|
|
|
|
currentTs <- getCurrentTime
|
|
|
|
|
map (toDirectChatPreview currentTs)
|
|
|
|
|
<$> DB.query
|
|
|
|
|
db
|
|
|
|
|
[sql|
|
|
|
|
|
SELECT
|
|
|
|
|
-- Contact
|
|
|
|
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
|
|
|
|
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent,
|
|
|
|
|
-- Connection
|
|
|
|
|
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
|
|
|
|
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.auth_err_counter,
|
|
|
|
|
c.peer_chat_min_version, c.peer_chat_max_version,
|
|
|
|
|
-- ChatStats
|
|
|
|
|
COALESCE(ChatStats.UnreadCount, 0), COALESCE(ChatStats.MinUnread, 0), ct.unread_chat,
|
|
|
|
|
-- ChatItem
|
|
|
|
|
i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.shared_msg_id, i.item_deleted, i.item_deleted_ts, i.item_edited, i.created_at, i.updated_at, i.timed_ttl, i.timed_delete_at, i.item_live,
|
|
|
|
|
-- CIFile
|
|
|
|
|
f.file_id, f.file_name, f.file_size, f.file_path, f.file_crypto_key, f.file_crypto_nonce, f.ci_file_status, f.protocol,
|
|
|
|
|
-- DirectQuote
|
|
|
|
|
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent
|
|
|
|
|
FROM contacts ct
|
|
|
|
|
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
|
|
|
|
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
|
|
|
|
LEFT JOIN (
|
|
|
|
|
SELECT contact_id, chat_item_id, MAX(created_at)
|
|
|
|
|
FROM chat_items
|
|
|
|
|
GROUP BY contact_id
|
|
|
|
|
) LastItems ON LastItems.contact_id = ct.contact_id
|
|
|
|
|
LEFT JOIN chat_items i ON i.contact_id = LastItems.contact_id
|
|
|
|
|
AND i.chat_item_id = LastItems.chat_item_id
|
|
|
|
|
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
|
|
|
|
|
LEFT JOIN (
|
|
|
|
|
SELECT contact_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE item_status = ?
|
|
|
|
|
GROUP BY contact_id
|
|
|
|
|
) ChatStats ON ChatStats.contact_id = ct.contact_id
|
|
|
|
|
LEFT JOIN chat_items ri ON ri.user_id = i.user_id AND ri.contact_id = i.contact_id AND ri.shared_msg_id = i.quoted_shared_msg_id
|
|
|
|
|
WHERE ct.user_id = ?
|
|
|
|
|
AND ct.is_user = 0
|
|
|
|
|
AND ct.deleted = 0
|
|
|
|
|
AND (
|
|
|
|
|
(
|
|
|
|
|
((c.conn_level = 0 AND c.via_group_link = 0) OR ct.contact_used = 1)
|
|
|
|
|
AND c.connection_id = (
|
|
|
|
|
SELECT cc_connection_id FROM (
|
|
|
|
|
SELECT
|
|
|
|
|
cc.connection_id AS cc_connection_id,
|
|
|
|
|
cc.created_at AS cc_created_at,
|
|
|
|
|
(CASE WHEN cc.conn_status = ? OR cc.conn_status = ? THEN 1 ELSE 0 END) AS cc_conn_status_ord
|
|
|
|
|
FROM connections cc
|
|
|
|
|
WHERE cc.user_id = ct.user_id AND cc.contact_id = ct.contact_id
|
|
|
|
|
ORDER BY cc_conn_status_ord DESC, cc_created_at DESC
|
|
|
|
|
LIMIT 1
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
OR c.connection_id IS NULL
|
|
|
|
|
data ChatPreviewData (c :: ChatType) where
|
|
|
|
|
DirectChatPD :: UTCTime -> ContactId -> Maybe ChatStats -> ChatPreviewData 'CTDirect
|
|
|
|
|
GroupChatPD :: UTCTime -> GroupId -> Maybe ChatStats -> ChatPreviewData 'CTGroup
|
|
|
|
|
ContactRequestPD :: UTCTime -> AChat -> ChatPreviewData 'CTContactRequest
|
|
|
|
|
ContactConnectionPD :: UTCTime -> AChat -> ChatPreviewData 'CTContactConnection
|
|
|
|
|
|
|
|
|
|
data AChatPreviewData = forall c. ChatTypeI c => ACPD (SChatType c) (ChatPreviewData c)
|
|
|
|
|
|
|
|
|
|
paginationByTimeFilter :: PaginationByTime -> (Query, [NamedParam])
|
|
|
|
|
paginationByTimeFilter = \case
|
|
|
|
|
PTLast count -> ("\nORDER BY ts DESC LIMIT :count", [":count" := count])
|
|
|
|
|
PTAfter ts count -> ("\nAND ts > :ts ORDER BY ts ASC LIMIT :count", [":ts" := ts, ":count" := count])
|
|
|
|
|
PTBefore ts count -> ("\nAND ts < :ts ORDER BY ts DESC LIMIT :count", [":ts" := ts, ":count" := count])
|
|
|
|
|
|
|
|
|
|
type MaybeChatStatsRow = (Maybe Int, Maybe ChatItemId, Maybe Bool)
|
|
|
|
|
|
|
|
|
|
toMaybeChatStats :: MaybeChatStatsRow -> Maybe ChatStats
|
|
|
|
|
toMaybeChatStats (Just unreadCount, Just minUnreadItemId, Just unreadChat) = Just ChatStats {unreadCount, minUnreadItemId, unreadChat}
|
|
|
|
|
toMaybeChatStats _ = Nothing
|
|
|
|
|
|
|
|
|
|
findDirectChatPreviews_ :: DB.Connection -> User -> PaginationByTime -> ChatListQuery -> IO [AChatPreviewData]
|
|
|
|
|
findDirectChatPreviews_ db User {userId} pagination clq =
|
|
|
|
|
map toPreview <$> getPreviews
|
|
|
|
|
where
|
|
|
|
|
toPreview :: (ContactId, UTCTime) :. MaybeChatStatsRow -> AChatPreviewData
|
|
|
|
|
toPreview ((contactId, ts) :. statsRow_) =
|
|
|
|
|
ACPD SCTDirect $ DirectChatPD ts contactId (toMaybeChatStats statsRow_)
|
|
|
|
|
(pagQuery, pagParams) = paginationByTimeFilter pagination
|
|
|
|
|
getPreviews = case clq of
|
|
|
|
|
CLQFilters {favorite = False, unread = False} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT ct.contact_id, ct.chat_ts as ts, NULL, NULL, NULL
|
|
|
|
|
FROM contacts ct
|
|
|
|
|
WHERE ct.user_id = :user_id AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
ORDER BY i.item_ts DESC
|
|
|
|
|
|]
|
|
|
|
|
(CISRcvNew, userId, ConnReady, ConnSndReady)
|
|
|
|
|
where
|
|
|
|
|
toDirectChatPreview :: UTCTime -> ContactRow :. MaybeConnectionRow :. ChatStatsRow :. MaybeChatItemRow :. QuoteRow -> AChat
|
|
|
|
|
toDirectChatPreview currentTs (contactRow :. connRow :. statsRow :. ciRow_) =
|
|
|
|
|
let contact = toContact user $ contactRow :. connRow
|
|
|
|
|
ci_ = toDirectChatItemList currentTs ciRow_
|
|
|
|
|
stats = toChatStats statsRow
|
|
|
|
|
in AChat SCTDirect $ Chat (DirectChat contact) ci_ stats
|
|
|
|
|
([":user_id" := userId] <> pagParams)
|
|
|
|
|
CLQFilters {favorite = True, unread = False} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT ct.contact_id, ct.chat_ts as ts, NULL, NULL, NULL
|
|
|
|
|
FROM contacts ct
|
|
|
|
|
WHERE ct.user_id = :user_id AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used
|
|
|
|
|
AND ct.favorite = 1
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId] <> pagParams)
|
|
|
|
|
CLQFilters {favorite = False, unread = True} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT ct.contact_id, ct.chat_ts as ts, COALESCE(ChatStats.UnreadCount, 0), COALESCE(ChatStats.MinUnread, 0), ct.unread_chat
|
|
|
|
|
FROM contacts ct
|
|
|
|
|
LEFT JOIN (
|
|
|
|
|
SELECT contact_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE item_status = :rcv_new
|
|
|
|
|
GROUP BY contact_id
|
|
|
|
|
) ChatStats ON ChatStats.contact_id = ct.contact_id
|
|
|
|
|
WHERE ct.user_id = :user_id AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used
|
|
|
|
|
AND (ct.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId, ":rcv_new" := CISRcvNew] <> pagParams)
|
|
|
|
|
CLQFilters {favorite = True, unread = True} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT ct.contact_id, ct.chat_ts as ts, COALESCE(ChatStats.UnreadCount, 0), COALESCE(ChatStats.MinUnread, 0), ct.unread_chat
|
|
|
|
|
FROM contacts ct
|
|
|
|
|
LEFT JOIN (
|
|
|
|
|
SELECT contact_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE item_status = :rcv_new
|
|
|
|
|
GROUP BY contact_id
|
|
|
|
|
) ChatStats ON ChatStats.contact_id = ct.contact_id
|
|
|
|
|
WHERE ct.user_id = :user_id AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used
|
|
|
|
|
AND (ct.favorite = 1
|
|
|
|
|
OR ct.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId, ":rcv_new" := CISRcvNew] <> pagParams)
|
|
|
|
|
CLQSearch {search} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT ct.contact_id, ct.chat_ts as ts, NULL, NULL, NULL
|
|
|
|
|
FROM contacts ct
|
|
|
|
|
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
|
|
|
|
WHERE ct.user_id = :user_id AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used
|
|
|
|
|
AND (
|
|
|
|
|
ct.local_display_name LIKE '%' || :search || '%'
|
|
|
|
|
OR cp.display_name LIKE '%' || :search || '%'
|
|
|
|
|
OR cp.full_name LIKE '%' || :search || '%'
|
|
|
|
|
OR cp.local_alias LIKE '%' || :search || '%'
|
|
|
|
|
)
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId, ":search" := search] <> pagParams)
|
|
|
|
|
|
|
|
|
|
getGroupChatPreviews_ :: DB.Connection -> User -> IO [AChat]
|
|
|
|
|
getGroupChatPreviews_ db User {userId, userContactId} = do
|
|
|
|
|
currentTs <- getCurrentTime
|
|
|
|
|
map (toGroupChatPreview currentTs)
|
|
|
|
|
<$> DB.query
|
|
|
|
|
db
|
|
|
|
|
[sql|
|
|
|
|
|
SELECT
|
|
|
|
|
-- GroupInfo
|
|
|
|
|
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image, g.host_conn_custom_user_profile_id, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, g.created_at, g.updated_at, g.chat_ts,
|
|
|
|
|
-- GroupMember - membership
|
|
|
|
|
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
|
|
|
|
mu.member_status, mu.show_messages, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
|
|
|
|
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
|
|
|
|
-- ChatStats
|
|
|
|
|
COALESCE(ChatStats.UnreadCount, 0), COALESCE(ChatStats.MinUnread, 0), g.unread_chat,
|
|
|
|
|
-- ChatItem
|
|
|
|
|
i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.shared_msg_id, i.item_deleted, i.item_deleted_ts, i.item_edited, i.created_at, i.updated_at, i.timed_ttl, i.timed_delete_at, i.item_live,
|
|
|
|
|
-- CIFile
|
|
|
|
|
f.file_id, f.file_name, f.file_size, f.file_path, f.file_crypto_key, f.file_crypto_nonce, f.ci_file_status, f.protocol,
|
|
|
|
|
-- CIMeta forwardedByMember
|
|
|
|
|
i.forwarded_by_group_member_id,
|
|
|
|
|
-- Maybe GroupMember - sender
|
|
|
|
|
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
|
|
|
|
|
m.member_status, m.show_messages, m.invited_by, m.invited_by_group_member_id, 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,
|
|
|
|
|
-- quoted ChatItem
|
|
|
|
|
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
|
|
|
|
|
-- quoted GroupMember
|
|
|
|
|
rm.group_member_id, rm.group_id, rm.member_id, rm.peer_chat_min_version, rm.peer_chat_max_version, rm.member_role, rm.member_category,
|
|
|
|
|
rm.member_status, rm.show_messages, rm.invited_by, rm.invited_by_group_member_id, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
|
|
|
|
|
rp.display_name, rp.full_name, rp.image, rp.contact_link, rp.local_alias, rp.preferences,
|
|
|
|
|
-- deleted by GroupMember
|
|
|
|
|
dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
|
|
|
|
|
dbm.member_status, dbm.show_messages, dbm.invited_by, dbm.invited_by_group_member_id, 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
|
|
|
|
|
FROM groups g
|
|
|
|
|
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 contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
|
|
|
|
LEFT JOIN (
|
|
|
|
|
SELECT group_id, chat_item_id, MAX(item_ts)
|
|
|
|
|
FROM chat_items
|
|
|
|
|
GROUP BY group_id
|
|
|
|
|
) LastItems ON LastItems.group_id = g.group_id
|
|
|
|
|
LEFT JOIN chat_items i ON i.group_id = LastItems.group_id
|
|
|
|
|
AND i.chat_item_id = LastItems.chat_item_id
|
|
|
|
|
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
|
|
|
|
|
LEFT JOIN (
|
|
|
|
|
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE item_status = ?
|
|
|
|
|
GROUP BY group_id
|
|
|
|
|
) ChatStats ON ChatStats.group_id = g.group_id
|
|
|
|
|
LEFT JOIN group_members m ON m.group_member_id = i.group_member_id
|
|
|
|
|
LEFT JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
|
|
|
|
LEFT JOIN chat_items ri ON ri.shared_msg_id = i.quoted_shared_msg_id AND ri.group_id = i.group_id
|
|
|
|
|
LEFT JOIN group_members rm ON rm.group_member_id = ri.group_member_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 contact_profiles dbp ON dbp.contact_profile_id = COALESCE(dbm.member_profile_id, dbm.contact_profile_id)
|
|
|
|
|
WHERE g.user_id = ? AND mu.contact_id = ?
|
|
|
|
|
ORDER BY i.item_ts DESC
|
|
|
|
|
|]
|
|
|
|
|
(CISRcvNew, userId, userContactId)
|
|
|
|
|
getDirectChatPreview_ :: DB.Connection -> User -> ChatPreviewData 'CTDirect -> ExceptT StoreError IO AChat
|
|
|
|
|
getDirectChatPreview_ db user (DirectChatPD _ contactId stats_) = do
|
|
|
|
|
contact <- getContact db user contactId
|
|
|
|
|
lastItem <- getLastItem
|
|
|
|
|
stats <- maybe getChatStats pure stats_
|
|
|
|
|
pure $ AChat SCTDirect (Chat (DirectChat contact) lastItem stats)
|
|
|
|
|
where
|
|
|
|
|
toGroupChatPreview :: UTCTime -> GroupInfoRow :. ChatStatsRow :. MaybeGroupChatItemRow -> AChat
|
|
|
|
|
toGroupChatPreview currentTs (groupInfoRow :. statsRow :. ciRow_) =
|
|
|
|
|
let groupInfo = toGroupInfo userContactId groupInfoRow
|
|
|
|
|
ci_ = toGroupChatItemList currentTs userContactId ciRow_
|
|
|
|
|
stats = toChatStats statsRow
|
|
|
|
|
in AChat SCTGroup $ Chat (GroupChat groupInfo) ci_ stats
|
|
|
|
|
getLastItem :: ExceptT StoreError IO [CChatItem 'CTDirect]
|
|
|
|
|
getLastItem =
|
|
|
|
|
liftIO getLastItemId >>= \case
|
|
|
|
|
Nothing -> pure []
|
|
|
|
|
Just lastItemId -> (: []) <$> getDirectChatItem db user contactId lastItemId
|
|
|
|
|
getLastItemId :: IO (Maybe ChatItemId)
|
|
|
|
|
getLastItemId =
|
|
|
|
|
maybeFirstRow fromOnly $
|
|
|
|
|
DB.query
|
|
|
|
|
db
|
|
|
|
|
[sql|
|
|
|
|
|
SELECT chat_item_id FROM (
|
|
|
|
|
SELECT contact_id, chat_item_id, MAX(created_at)
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE contact_id = ?
|
|
|
|
|
GROUP BY contact_id
|
|
|
|
|
)
|
|
|
|
|
|]
|
|
|
|
|
(Only contactId)
|
|
|
|
|
getChatStats :: ExceptT StoreError IO ChatStats
|
|
|
|
|
getChatStats = do
|
|
|
|
|
r_ <- liftIO getUnreadStats
|
|
|
|
|
let (unreadCount, minUnreadItemId) = maybe (0, 0) (\(_, unreadCnt, minId) -> (unreadCnt, minId)) r_
|
|
|
|
|
-- unread_chat could be read into contact to not search twice
|
|
|
|
|
unreadChat <-
|
|
|
|
|
ExceptT . firstRow fromOnly (SEInternalError $ "unread_chat not found for contact " <> show contactId) $
|
|
|
|
|
DB.query db "SELECT unread_chat FROM contacts WHERE contact_id = ?" (Only contactId)
|
|
|
|
|
pure ChatStats {unreadCount, minUnreadItemId, unreadChat}
|
|
|
|
|
getUnreadStats :: IO (Maybe (ContactId, Int, ChatItemId))
|
|
|
|
|
getUnreadStats =
|
|
|
|
|
maybeFirstRow id $
|
|
|
|
|
DB.query
|
|
|
|
|
db
|
|
|
|
|
[sql|
|
|
|
|
|
SELECT contact_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE contact_id = ? AND item_status = ?
|
|
|
|
|
GROUP BY contact_id
|
|
|
|
|
|]
|
|
|
|
|
(contactId, CISRcvNew)
|
|
|
|
|
|
|
|
|
|
getContactRequestChatPreviews_ :: DB.Connection -> User -> IO [AChat]
|
|
|
|
|
getContactRequestChatPreviews_ db User {userId} =
|
|
|
|
|
map toContactRequestChatPreview
|
|
|
|
|
<$> DB.query
|
|
|
|
|
db
|
|
|
|
|
[sql|
|
|
|
|
|
SELECT
|
|
|
|
|
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id, cr.user_contact_link_id,
|
|
|
|
|
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id, p.preferences, cr.created_at, cr.updated_at,
|
|
|
|
|
cr.peer_chat_min_version, cr.peer_chat_max_version
|
|
|
|
|
FROM contact_requests cr
|
|
|
|
|
JOIN connections c ON c.user_contact_link_id = cr.user_contact_link_id
|
|
|
|
|
JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id
|
|
|
|
|
JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id
|
|
|
|
|
WHERE cr.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|
|
|
|
|
|]
|
|
|
|
|
(userId, userId)
|
|
|
|
|
findGroupChatPreviews_ :: DB.Connection -> User -> PaginationByTime -> ChatListQuery -> IO [AChatPreviewData]
|
|
|
|
|
findGroupChatPreviews_ db User {userId} pagination clq =
|
|
|
|
|
map toPreview <$> getPreviews
|
|
|
|
|
where
|
|
|
|
|
toContactRequestChatPreview :: ContactRequestRow -> AChat
|
|
|
|
|
toContactRequestChatPreview cReqRow =
|
|
|
|
|
let cReq = toContactRequest cReqRow
|
|
|
|
|
toPreview :: (GroupId, UTCTime) :. MaybeChatStatsRow -> AChatPreviewData
|
|
|
|
|
toPreview ((groupId, ts) :. statsRow_) =
|
|
|
|
|
ACPD SCTGroup $ GroupChatPD ts groupId (toMaybeChatStats statsRow_)
|
|
|
|
|
(pagQuery, pagParams) = paginationByTimeFilter pagination
|
|
|
|
|
getPreviews = case clq of
|
|
|
|
|
CLQFilters {favorite = False, unread = False} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT g.group_id, g.chat_ts as ts, NULL, NULL, NULL
|
|
|
|
|
FROM groups g
|
|
|
|
|
WHERE g.user_id = :user_id
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId] <> pagParams)
|
|
|
|
|
CLQFilters {favorite = True, unread = False} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT g.group_id, g.chat_ts as ts, NULL, NULL, NULL
|
|
|
|
|
FROM groups g
|
|
|
|
|
WHERE g.user_id = :user_id
|
|
|
|
|
AND g.favorite = 1
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId] <> pagParams)
|
|
|
|
|
CLQFilters {favorite = False, unread = True} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT g.group_id, g.chat_ts as ts, COALESCE(ChatStats.UnreadCount, 0), COALESCE(ChatStats.MinUnread, 0), g.unread_chat
|
|
|
|
|
FROM groups g
|
|
|
|
|
LEFT JOIN (
|
|
|
|
|
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE item_status = :rcv_new
|
|
|
|
|
GROUP BY group_id
|
|
|
|
|
) ChatStats ON ChatStats.group_id = g.group_id
|
|
|
|
|
WHERE g.user_id = :user_id
|
|
|
|
|
AND (g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId, ":rcv_new" := CISRcvNew] <> pagParams)
|
|
|
|
|
CLQFilters {favorite = True, unread = True} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT g.group_id, g.chat_ts as ts, COALESCE(ChatStats.UnreadCount, 0), COALESCE(ChatStats.MinUnread, 0), g.unread_chat
|
|
|
|
|
FROM groups g
|
|
|
|
|
LEFT JOIN (
|
|
|
|
|
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE item_status = :rcv_new
|
|
|
|
|
GROUP BY group_id
|
|
|
|
|
) ChatStats ON ChatStats.group_id = g.group_id
|
|
|
|
|
WHERE g.user_id = :user_id
|
|
|
|
|
AND (g.favorite = 1
|
|
|
|
|
OR g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId, ":rcv_new" := CISRcvNew] <> pagParams)
|
|
|
|
|
CLQSearch {search} ->
|
|
|
|
|
DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT g.group_id, g.chat_ts as ts, NULL, NULL, NULL
|
|
|
|
|
FROM groups g
|
|
|
|
|
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
|
|
|
|
WHERE g.user_id = :user_id
|
|
|
|
|
AND (
|
|
|
|
|
g.local_display_name LIKE '%' || :search || '%'
|
|
|
|
|
OR gp.display_name LIKE '%' || :search || '%'
|
|
|
|
|
OR gp.full_name LIKE '%' || :search || '%'
|
|
|
|
|
OR gp.description LIKE '%' || :search || '%'
|
|
|
|
|
)
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId, ":search" := search] <> pagParams)
|
|
|
|
|
|
|
|
|
|
getGroupChatPreview_ :: DB.Connection -> User -> ChatPreviewData 'CTGroup -> ExceptT StoreError IO AChat
|
|
|
|
|
getGroupChatPreview_ db user (GroupChatPD _ groupId stats_) = do
|
|
|
|
|
groupInfo <- getGroupInfo db user groupId
|
|
|
|
|
lastItem <- getLastItem
|
|
|
|
|
stats <- maybe getChatStats pure stats_
|
|
|
|
|
pure $ AChat SCTGroup (Chat (GroupChat groupInfo) lastItem stats)
|
|
|
|
|
where
|
|
|
|
|
getLastItem :: ExceptT StoreError IO [CChatItem 'CTGroup]
|
|
|
|
|
getLastItem =
|
|
|
|
|
liftIO getLastItemId >>= \case
|
|
|
|
|
Nothing -> pure []
|
|
|
|
|
Just lastItemId -> (: []) <$> getGroupChatItem db user groupId lastItemId
|
|
|
|
|
getLastItemId :: IO (Maybe ChatItemId)
|
|
|
|
|
getLastItemId =
|
|
|
|
|
maybeFirstRow fromOnly $
|
|
|
|
|
DB.query
|
|
|
|
|
db
|
|
|
|
|
[sql|
|
|
|
|
|
SELECT chat_item_id FROM (
|
|
|
|
|
SELECT group_id, chat_item_id, MAX(item_ts)
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE group_id = ?
|
|
|
|
|
GROUP BY group_id
|
|
|
|
|
)
|
|
|
|
|
|]
|
|
|
|
|
(Only groupId)
|
|
|
|
|
getChatStats :: ExceptT StoreError IO ChatStats
|
|
|
|
|
getChatStats = do
|
|
|
|
|
r_ <- liftIO getUnreadStats
|
|
|
|
|
let (unreadCount, minUnreadItemId) = maybe (0, 0) (\(_, unreadCnt, minId) -> (unreadCnt, minId)) r_
|
|
|
|
|
-- unread_chat could be read into group to not search twice
|
|
|
|
|
unreadChat <-
|
|
|
|
|
ExceptT . firstRow fromOnly (SEInternalError $ "unread_chat not found for group " <> show groupId) $
|
|
|
|
|
DB.query db "SELECT unread_chat FROM groups WHERE group_id = ?" (Only groupId)
|
|
|
|
|
pure ChatStats {unreadCount, minUnreadItemId, unreadChat}
|
|
|
|
|
getUnreadStats :: IO (Maybe (GroupId, Int, ChatItemId))
|
|
|
|
|
getUnreadStats =
|
|
|
|
|
maybeFirstRow id $
|
|
|
|
|
DB.query
|
|
|
|
|
db
|
|
|
|
|
[sql|
|
|
|
|
|
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
|
|
|
|
FROM chat_items
|
|
|
|
|
WHERE group_id = ? AND item_status = ?
|
|
|
|
|
GROUP BY group_id
|
|
|
|
|
|]
|
|
|
|
|
(groupId, CISRcvNew)
|
|
|
|
|
|
|
|
|
|
getContactRequestChatPreviews_ :: DB.Connection -> User -> PaginationByTime -> ChatListQuery -> IO [AChatPreviewData]
|
|
|
|
|
getContactRequestChatPreviews_ db User {userId} pagination clq = case clq of
|
|
|
|
|
CLQFilters {favorite = False, unread = False} -> query ""
|
|
|
|
|
CLQFilters {favorite = True, unread = False} -> pure []
|
|
|
|
|
CLQFilters {favorite = False, unread = True} -> query ""
|
|
|
|
|
CLQFilters {favorite = True, unread = True} -> query ""
|
|
|
|
|
CLQSearch {search} -> query search
|
|
|
|
|
where
|
|
|
|
|
(pagQuery, pagParams) = paginationByTimeFilter pagination
|
|
|
|
|
query search =
|
|
|
|
|
map toPreview
|
|
|
|
|
<$> DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT
|
|
|
|
|
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id, cr.user_contact_link_id,
|
|
|
|
|
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id, p.preferences,
|
|
|
|
|
cr.created_at, cr.updated_at as ts,
|
|
|
|
|
cr.peer_chat_min_version, cr.peer_chat_max_version
|
|
|
|
|
FROM contact_requests cr
|
|
|
|
|
JOIN connections c ON c.user_contact_link_id = cr.user_contact_link_id
|
|
|
|
|
JOIN contact_profiles p ON p.contact_profile_id = cr.contact_profile_id
|
|
|
|
|
JOIN user_contact_links uc ON uc.user_contact_link_id = cr.user_contact_link_id
|
|
|
|
|
WHERE cr.user_id = :user_id
|
|
|
|
|
AND uc.user_id = :user_id
|
|
|
|
|
AND uc.local_display_name = ''
|
|
|
|
|
AND uc.group_id IS NULL
|
|
|
|
|
AND (
|
|
|
|
|
cr.local_display_name LIKE '%' || :search || '%'
|
|
|
|
|
OR p.display_name LIKE '%' || :search || '%'
|
|
|
|
|
OR p.full_name LIKE '%' || :search || '%'
|
|
|
|
|
)
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId, ":search" := search] <> pagParams)
|
|
|
|
|
toPreview :: ContactRequestRow -> AChatPreviewData
|
|
|
|
|
toPreview cReqRow =
|
|
|
|
|
let cReq@UserContactRequest {updatedAt} = toContactRequest cReqRow
|
|
|
|
|
stats = ChatStats {unreadCount = 0, minUnreadItemId = 0, unreadChat = False}
|
|
|
|
|
in AChat SCTContactRequest $ Chat (ContactRequest cReq) [] stats
|
|
|
|
|
aChat = AChat SCTContactRequest $ Chat (ContactRequest cReq) [] stats
|
|
|
|
|
in ACPD SCTContactRequest $ ContactRequestPD updatedAt aChat
|
|
|
|
|
|
|
|
|
|
getContactConnectionChatPreviews_ :: DB.Connection -> User -> Bool -> IO [AChat]
|
|
|
|
|
getContactConnectionChatPreviews_ _ _ False = pure []
|
|
|
|
|
getContactConnectionChatPreviews_ db User {userId} _ =
|
|
|
|
|
map toContactConnectionChatPreview
|
|
|
|
|
<$> DB.query
|
|
|
|
|
db
|
|
|
|
|
[sql|
|
|
|
|
|
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
|
|
|
|
FROM connections
|
|
|
|
|
WHERE user_id = ? AND conn_type = ? AND contact_id IS NULL AND conn_level = 0 AND via_contact IS NULL AND (via_group_link = 0 || (via_group_link = 1 AND group_link_id IS NOT NULL))
|
|
|
|
|
|]
|
|
|
|
|
(userId, ConnContact)
|
|
|
|
|
getContactConnectionChatPreviews_ :: DB.Connection -> User -> PaginationByTime -> ChatListQuery -> IO [AChatPreviewData]
|
|
|
|
|
getContactConnectionChatPreviews_ db User {userId} pagination clq = case clq of
|
|
|
|
|
CLQFilters {favorite = False, unread = False} -> query ""
|
|
|
|
|
CLQFilters {favorite = True, unread = False} -> pure []
|
|
|
|
|
CLQFilters {favorite = False, unread = True} -> pure []
|
|
|
|
|
CLQFilters {favorite = True, unread = True} -> pure []
|
|
|
|
|
CLQSearch {search} -> query search
|
|
|
|
|
where
|
|
|
|
|
toContactConnectionChatPreview :: (Int64, ConnId, ConnStatus, Maybe ByteString, Maybe Int64, Maybe GroupLinkId, Maybe Int64, Maybe ConnReqInvitation, LocalAlias, UTCTime, UTCTime) -> AChat
|
|
|
|
|
toContactConnectionChatPreview connRow =
|
|
|
|
|
let conn = toPendingContactConnection connRow
|
|
|
|
|
(pagQuery, pagParams) = paginationByTimeFilter pagination
|
|
|
|
|
query search =
|
|
|
|
|
map toPreview
|
|
|
|
|
<$> DB.queryNamed
|
|
|
|
|
db
|
|
|
|
|
( [sql|
|
|
|
|
|
SELECT
|
|
|
|
|
connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id,
|
|
|
|
|
custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at as ts
|
|
|
|
|
FROM connections
|
|
|
|
|
WHERE user_id = :user_id
|
|
|
|
|
AND conn_type = :conn_contact
|
|
|
|
|
AND contact_id IS NULL
|
|
|
|
|
AND conn_level = 0
|
|
|
|
|
AND via_contact IS NULL
|
|
|
|
|
AND (via_group_link = 0 || (via_group_link = 1 AND group_link_id IS NOT NULL))
|
|
|
|
|
AND local_alias LIKE '%' || :search || '%'
|
|
|
|
|
|]
|
|
|
|
|
<> pagQuery
|
|
|
|
|
)
|
|
|
|
|
([":user_id" := userId, ":conn_contact" := ConnContact, ":search" := search] <> pagParams)
|
|
|
|
|
toPreview :: (Int64, ConnId, ConnStatus, Maybe ByteString, Maybe Int64, Maybe GroupLinkId, Maybe Int64, Maybe ConnReqInvitation, LocalAlias, UTCTime, UTCTime) -> AChatPreviewData
|
|
|
|
|
toPreview connRow =
|
|
|
|
|
let conn@PendingContactConnection {updatedAt} = toPendingContactConnection connRow
|
|
|
|
|
stats = ChatStats {unreadCount = 0, minUnreadItemId = 0, unreadChat = False}
|
|
|
|
|
in AChat SCTContactConnection $ Chat (ContactConnection conn) [] stats
|
|
|
|
|
aChat = AChat SCTContactConnection $ Chat (ContactConnection conn) [] stats
|
|
|
|
|
in ACPD SCTContactConnection $ ContactConnectionPD updatedAt aChat
|
|
|
|
|
|
|
|
|
|
getDirectChat :: DB.Connection -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTDirect)
|
|
|
|
|
getDirectChat db user contactId pagination search_ = do
|
|
|
|
@@ -993,19 +1188,12 @@ setGroupChatItemDeleteAt db User {userId} groupId chatItemId deleteAt =
|
|
|
|
|
"UPDATE chat_items SET timed_delete_at = ? WHERE user_id = ? AND group_id = ? AND chat_item_id = ?"
|
|
|
|
|
(deleteAt, userId, groupId, chatItemId)
|
|
|
|
|
|
|
|
|
|
type ChatStatsRow = (Int, ChatItemId, Bool)
|
|
|
|
|
|
|
|
|
|
toChatStats :: ChatStatsRow -> ChatStats
|
|
|
|
|
toChatStats (unreadCount, minUnreadItemId, unreadChat) = ChatStats {unreadCount, minUnreadItemId, unreadChat}
|
|
|
|
|
|
|
|
|
|
type MaybeCIFIleRow = (Maybe Int64, Maybe String, Maybe Integer, Maybe FilePath, Maybe C.SbKey, Maybe C.CbNonce, Maybe ACIFileStatus, Maybe FileProtocol)
|
|
|
|
|
|
|
|
|
|
type ChatItemModeRow = (Maybe Int, Maybe UTCTime, Maybe Bool)
|
|
|
|
|
|
|
|
|
|
type ChatItemRow = (Int64, ChatItemTs, AMsgDirection, Text, Text, ACIStatus, Maybe SharedMsgId) :. (Int, Maybe UTCTime, Maybe Bool, UTCTime, UTCTime) :. ChatItemModeRow :. MaybeCIFIleRow
|
|
|
|
|
|
|
|
|
|
type MaybeChatItemRow = (Maybe Int64, Maybe ChatItemTs, Maybe AMsgDirection, Maybe Text, Maybe Text, Maybe ACIStatus, Maybe SharedMsgId) :. (Maybe Int, Maybe UTCTime, Maybe Bool, Maybe UTCTime, Maybe UTCTime) :. ChatItemModeRow :. MaybeCIFIleRow
|
|
|
|
|
|
|
|
|
|
type QuoteRow = (Maybe ChatItemId, Maybe SharedMsgId, Maybe UTCTime, Maybe MsgContent, Maybe Bool)
|
|
|
|
|
|
|
|
|
|
toDirectQuote :: QuoteRow -> Maybe (CIQuote 'CTDirect)
|
|
|
|
@@ -1055,15 +1243,8 @@ toDirectChatItem currentTs (((itemId, itemTs, AMsgDirection msgDir, itemContentT
|
|
|
|
|
ciTimed :: Maybe CITimed
|
|
|
|
|
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
|
|
|
|
|
|
|
|
|
toDirectChatItemList :: UTCTime -> MaybeChatItemRow :. QuoteRow -> [CChatItem 'CTDirect]
|
|
|
|
|
toDirectChatItemList currentTs (((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) :. quoteRow) =
|
|
|
|
|
either (const []) (: []) $ toDirectChatItem currentTs (((itemId, itemTs, msgDir, itemContent, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. fileRow) :. quoteRow)
|
|
|
|
|
toDirectChatItemList _ _ = []
|
|
|
|
|
|
|
|
|
|
type GroupQuoteRow = QuoteRow :. MaybeGroupMemberRow
|
|
|
|
|
|
|
|
|
|
type MaybeGroupChatItemRow = MaybeChatItemRow :. Only (Maybe GroupMemberId) :. MaybeGroupMemberRow :. GroupQuoteRow :. MaybeGroupMemberRow
|
|
|
|
|
|
|
|
|
|
toGroupQuote :: QuoteRow -> Maybe GroupMember -> Maybe (CIQuote 'CTGroup)
|
|
|
|
|
toGroupQuote qr@(_, _, _, _, quotedSent) quotedMember_ = toQuote qr $ direction quotedSent quotedMember_
|
|
|
|
|
where
|
|
|
|
@@ -1114,11 +1295,6 @@ toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir,
|
|
|
|
|
ciTimed :: Maybe CITimed
|
|
|
|
|
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
|
|
|
|
|
|
|
|
|
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) :. forwardedByMember :. 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) :. forwardedByMember :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_)
|
|
|
|
|
toGroupChatItemList _ _ _ = []
|
|
|
|
|
|
|
|
|
|
getAllChatItems :: DB.Connection -> User -> ChatPagination -> Maybe String -> ExceptT StoreError IO [AChatItem]
|
|
|
|
|
getAllChatItems db user@User {userId} pagination search_ = do
|
|
|
|
|
itemRefs <-
|
|
|
|
|