diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 3965b6087..8360fb081 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -284,7 +284,8 @@ processChatCommand = \case CTGroup -> CRApiChat . AChat SCTGroup <$> withStore (\db -> getGroupChat db user cId pagination search) CTContactRequest -> pure $ chatCmdError "not implemented" CTContactConnection -> pure $ chatCmdError "not supported" - APIGetChatItems _pagination -> pure $ chatCmdError "not implemented" + APIGetChatItems pagination search -> withUser $ \user -> withStore $ \db -> + CRApiChatItems <$> getAllChatItems db user pagination search APISendMessage (ChatRef cType chatId) (ComposedMessage file_ quotedItemId_ mc) -> withUser $ \user@User {userId} -> withChatLock "sendMessage" $ case cType of CTDirect -> do ct@Contact {localDisplayName = c, contactUsed} <- withStore $ \db -> getContact db user chatId @@ -1035,9 +1036,9 @@ processChatCommand = \case processChatCommand . APISendMessage (ChatRef CTGroup groupId) $ ComposedMessage Nothing (Just quotedItemId) mc LastMessages (Just chatName) count search -> withUser $ \user -> do chatRef <- getChatRef user chatName - CRLastMessages . aChatItems . chat <$> processChatCommand (APIGetChat chatRef (CPLast count) search) - LastMessages Nothing count search -> withUser $ \user -> withStore $ \db -> - CRLastMessages <$> getAllChatItems db user (CPLast count) search + CRApiChatItems . aChatItems . chat <$> processChatCommand (APIGetChat chatRef (CPLast count) search) + LastMessages Nothing count search -> + processChatCommand (APIGetChatItems (CPLast count) search) SendFile chatName f -> withUser $ \user -> do chatRef <- getChatRef user chatName processChatCommand . APISendMessage chatRef $ ComposedMessage (Just f) Nothing (MCFile "") @@ -3182,7 +3183,7 @@ chatCommandP = "/sql agent " *> (ExecAgentStoreSQL <$> textP), "/_get chats" *> (APIGetChats <$> (" pcc=on" $> True <|> " pcc=off" $> False <|> pure False)), "/_get chat " *> (APIGetChat <$> chatRefP <* A.space <*> chatPaginationP <*> optional (" search=" *> stringP)), - "/_get items count=" *> (APIGetChatItems <$> A.decimal), + "/_get items " *> (APIGetChatItems <$> chatPaginationP <*> optional (" search=" *> stringP)), "/_send " *> (APISendMessage <$> chatRefP <*> (" json " *> jsonP <|> " text " *> (ComposedMessage Nothing Nothing <$> mcTextP))), "/_update item " *> (APIUpdateChatItem <$> chatRefP <* A.space <*> A.decimal <* A.space <*> msgContentP), "/_delete item " *> (APIDeleteChatItem <$> chatRefP <* A.space <*> A.decimal <* A.space <*> ciDeleteMode), diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index fbc0015a5..7a4b180af 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -147,7 +147,7 @@ data ChatCommand | ExecAgentStoreSQL Text | APIGetChats {pendingConnections :: Bool} | APIGetChat ChatRef ChatPagination (Maybe String) - | APIGetChatItems Int + | APIGetChatItems ChatPagination (Maybe String) | APISendMessage ChatRef ComposedMessage | APIUpdateChatItem ChatRef ChatItemId MsgContent | APIDeleteChatItem ChatRef ChatItemId CIDeleteMode @@ -261,7 +261,7 @@ data ChatResponse | CRChatSuspended | CRApiChats {chats :: [AChat]} | CRApiChat {chat :: AChat} - | CRLastMessages {chatItems :: [AChatItem]} + | CRApiChatItems {chatItems :: [AChatItem]} | CRApiParsedMarkdown {formattedText :: Maybe MarkdownList} | CRUserSMPServers {smpServers :: NonEmpty ServerCfg, presetSMPServers :: NonEmpty SMPServerWithAuth} | CRSmpTestResult {smpTestFailure :: Maybe SMPTestFailure} diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index b244e3ec5..99671aa88 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -3707,8 +3707,8 @@ getAllChatItems db user pagination search_ = do let search = fromMaybe "" search_ case pagination of CPLast count -> getAllChatItemsLast_ db user count search - CPAfter _afterId _count -> throwError $ SEInternalError "not implemented" - CPBefore _beforeId _count -> throwError $ SEInternalError "not implemented" + CPAfter afterId count -> getAllChatItemsAfter_ db user afterId count search + CPBefore beforeId count -> getAllChatItemsBefore_ db user beforeId count search getAllChatItemsLast_ :: DB.Connection -> User -> Int -> String -> ExceptT StoreError IO [AChatItem] getAllChatItemsLast_ db user@User {userId} count search = do @@ -3720,13 +3720,59 @@ getAllChatItemsLast_ db user@User {userId} count search = do [sql| SELECT chat_item_id, contact_id, group_id FROM chat_items - WHERE user_id = ? AND item_text LIKE '%' || ? || '%' + WHERE user_id = ? AND item_deleted != 1 AND item_text LIKE '%' || ? || '%' ORDER BY item_ts DESC, chat_item_id DESC LIMIT ? |] (userId, search, count) mapM (uncurry $ getAChatItem_ db user) itemRefs +getAllChatItemsAfter_ :: DB.Connection -> User -> Int64 -> Int -> String -> ExceptT StoreError IO [AChatItem] +getAllChatItemsAfter_ db user@User {userId} afterItemId count search = do + AChatItem _ _ _ afterItem <- getAChatItem db user afterItemId + itemRefs <- liftIO $ getChatItemRefsAfter_ (chatItemTs' afterItem) + mapM (uncurry $ getAChatItem_ db user) itemRefs + where + getChatItemRefsAfter_ afterItemTs = + rights . map toChatItemRef + <$> DB.query + db + [sql| + SELECT chat_item_id, contact_id, group_id + FROM chat_items + WHERE user_id = ? AND item_deleted != 1 AND item_text LIKE '%' || ? || '%' + AND (item_ts > ? OR (item_ts = ? AND chat_item_id > ?)) + ORDER BY item_ts ASC, chat_item_id ASC + LIMIT ? + |] + (userId, search, afterItemTs, afterItemTs, afterItemId, count) + +getAllChatItemsBefore_ :: DB.Connection -> User -> Int64 -> Int -> String -> ExceptT StoreError IO [AChatItem] +getAllChatItemsBefore_ db user@User {userId} beforeItemId count search = do + AChatItem _ _ _ beforeItem <- getAChatItem db user beforeItemId + itemRefs <- liftIO $ getChatItemRefsBefore_ (chatItemTs' beforeItem) + mapM (uncurry $ getAChatItem_ db user) itemRefs + where + getChatItemRefsBefore_ beforeItemTs = + reverse . rights . map toChatItemRef + <$> DB.query + db + [sql| + SELECT chat_item_id, contact_id, group_id + FROM chat_items + WHERE user_id = ? AND item_deleted != 1 AND item_text LIKE '%' || ? || '%' + AND (item_ts < ? OR (item_ts = ? AND chat_item_id < ?)) + ORDER BY item_ts DESC, chat_item_id DESC + LIMIT ? + |] + (userId, search, beforeItemTs, beforeItemTs, beforeItemId, count) + +getAChatItem :: DB.Connection -> User -> ChatItemId -> ExceptT StoreError IO AChatItem +getAChatItem db user@User {userId} itemId = do + afterItemRef <- ExceptT $ firstRow' toChatItemRef (SEChatItemNotFound itemId) $ + DB.query db "SELECT chat_item_id, contact_id, group_id FROM chat_items WHERE user_id = ? AND chat_item_id = ?" (userId, itemId) + uncurry (getAChatItem_ db user) afterItemRef + getGroupIdByName :: DB.Connection -> User -> GroupName -> ExceptT StoreError IO GroupId getGroupIdByName db User {userId} gName = ExceptT . firstRow fromOnly (SEGroupNotFoundByName gName) $ diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 916bff024..f90b68b96 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -75,7 +75,7 @@ responseToView user_ testView ts = \case CRContactSwitch ct progress -> viewContactSwitch ct progress CRGroupMemberSwitch g m progress -> viewGroupMemberSwitch g m progress CRNewChatItem (AChatItem _ _ chat item) -> unmuted chat item $ viewChatItem chat item False ts - CRLastMessages chatItems -> concatMap (\(AChatItem _ _ chat item) -> viewChatItem chat item True ts) chatItems + CRApiChatItems chatItems -> concatMap (\(AChatItem _ _ chat item) -> viewChatItem chat item True ts) chatItems CRChatItemStatusUpdated _ -> [] CRChatItemUpdated (AChatItem _ _ chat item) -> unmuted chat item $ viewItemUpdate chat item ts CRChatItemDeleted (AChatItem _ _ chat deletedItem) (AChatItem _ _ _ toItem) -> unmuted chat deletedItem $ viewItemDelete chat deletedItem toItem ts