{-# LANGUAGE CPP #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ScopedTypeVariables #-} module Simplex.Chat.Store.NoteFolders where import Control.Monad.Except (ExceptT (..), throwError) import Control.Monad.IO.Class (liftIO) import Data.Time (getCurrentTime) import Simplex.Chat.Store.Shared (StoreError (..)) import Simplex.Chat.Types (NoteFolder (..), NoteFolderId, User (..)) import Simplex.Messaging.Agent.Protocol (UserId) import Simplex.Messaging.Agent.Store.AgentStore (firstRow) import Simplex.Messaging.Agent.Store.DB (BoolInt (..)) import qualified Simplex.Messaging.Agent.Store.DB as DB #if defined(dbPostgres) import Database.PostgreSQL.Simple (Only (..)) import Database.PostgreSQL.Simple.SqlQQ (sql) #else import Database.SQLite.Simple (Only (..)) import Database.SQLite.Simple.QQ (sql) #endif createNoteFolder :: DB.Connection -> User -> ExceptT StoreError IO () createNoteFolder :: Connection -> User -> ExceptT StoreError IO () createNoteFolder Connection db User {UserId userId :: UserId userId :: User -> UserId userId} = do IO [Only UserId] -> ExceptT StoreError IO [Only UserId] forall a. IO a -> ExceptT StoreError IO a forall (m :: * -> *) a. MonadIO m => IO a -> m a liftIO (Connection -> Query -> Only UserId -> IO [Only UserId] forall q r. (ToRow q, FromRow r) => Connection -> Query -> q -> IO [r] DB.query Connection db Query "SELECT note_folder_id FROM note_folders WHERE user_id = ? LIMIT 1" (Only UserId -> IO [Only UserId]) -> Only UserId -> IO [Only UserId] forall a b. (a -> b) -> a -> b $ UserId -> Only UserId forall a. a -> Only a Only UserId userId) ExceptT StoreError IO [Only UserId] -> ([Only UserId] -> ExceptT StoreError IO ()) -> ExceptT StoreError IO () forall a b. ExceptT StoreError IO a -> (a -> ExceptT StoreError IO b) -> ExceptT StoreError IO b forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= \case [] -> IO () -> ExceptT StoreError IO () forall a. IO a -> ExceptT StoreError IO a forall (m :: * -> *) a. MonadIO m => IO a -> m a liftIO (IO () -> ExceptT StoreError IO ()) -> IO () -> ExceptT StoreError IO () forall a b. (a -> b) -> a -> b $ Connection -> Query -> Only UserId -> IO () forall q. ToRow q => Connection -> Query -> q -> IO () DB.execute Connection db Query "INSERT INTO note_folders (user_id) VALUES (?)" (UserId -> Only UserId forall a. a -> Only a Only UserId userId) Only UserId noteFolderId : [Only UserId] _ -> StoreError -> ExceptT StoreError IO () forall a. StoreError -> ExceptT StoreError IO a forall e (m :: * -> *) a. MonadError e m => e -> m a throwError (StoreError -> ExceptT StoreError IO ()) -> StoreError -> ExceptT StoreError IO () forall a b. (a -> b) -> a -> b $ UserId -> StoreError SENoteFolderAlreadyExists UserId noteFolderId getUserNoteFolderId :: DB.Connection -> User -> ExceptT StoreError IO NoteFolderId getUserNoteFolderId :: Connection -> User -> ExceptT StoreError IO UserId getUserNoteFolderId Connection db User {UserId userId :: User -> UserId userId :: UserId userId} = IO (Either StoreError UserId) -> ExceptT StoreError IO UserId forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a ExceptT (IO (Either StoreError UserId) -> ExceptT StoreError IO UserId) -> (IO [Only UserId] -> IO (Either StoreError UserId)) -> IO [Only UserId] -> ExceptT StoreError IO UserId forall b c a. (b -> c) -> (a -> b) -> a -> c . (Only UserId -> UserId) -> StoreError -> IO [Only UserId] -> IO (Either StoreError UserId) forall a b e. (a -> b) -> e -> IO [a] -> IO (Either e b) firstRow Only UserId -> UserId forall a. Only a -> a fromOnly StoreError SEUserNoteFolderNotFound (IO [Only UserId] -> ExceptT StoreError IO UserId) -> IO [Only UserId] -> ExceptT StoreError IO UserId forall a b. (a -> b) -> a -> b $ Connection -> Query -> Only UserId -> IO [Only UserId] forall q r. (ToRow q, FromRow r) => Connection -> Query -> q -> IO [r] DB.query Connection db Query "SELECT note_folder_id FROM note_folders WHERE user_id = ?" (UserId -> Only UserId forall a. a -> Only a Only UserId userId) getNoteFolder :: DB.Connection -> User -> NoteFolderId -> ExceptT StoreError IO NoteFolder getNoteFolder :: Connection -> User -> UserId -> ExceptT StoreError IO NoteFolder getNoteFolder Connection db User {UserId userId :: User -> UserId userId :: UserId userId} UserId noteFolderId = IO (Either StoreError NoteFolder) -> ExceptT StoreError IO NoteFolder forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a ExceptT (IO (Either StoreError NoteFolder) -> ExceptT StoreError IO NoteFolder) -> (IO [(UTCTime, UTCTime, UTCTime, BoolInt, BoolInt)] -> IO (Either StoreError NoteFolder)) -> IO [(UTCTime, UTCTime, UTCTime, BoolInt, BoolInt)] -> ExceptT StoreError IO NoteFolder forall b c a. (b -> c) -> (a -> b) -> a -> c . ((UTCTime, UTCTime, UTCTime, BoolInt, BoolInt) -> NoteFolder) -> StoreError -> IO [(UTCTime, UTCTime, UTCTime, BoolInt, BoolInt)] -> IO (Either StoreError NoteFolder) forall a b e. (a -> b) -> e -> IO [a] -> IO (Either e b) firstRow (UTCTime, UTCTime, UTCTime, BoolInt, BoolInt) -> NoteFolder toNoteFolder (UserId -> StoreError SENoteFolderNotFound UserId noteFolderId) (IO [(UTCTime, UTCTime, UTCTime, BoolInt, BoolInt)] -> ExceptT StoreError IO NoteFolder) -> IO [(UTCTime, UTCTime, UTCTime, BoolInt, BoolInt)] -> ExceptT StoreError IO NoteFolder forall a b. (a -> b) -> a -> b $ Connection -> Query -> (UserId, UserId) -> IO [(UTCTime, UTCTime, UTCTime, BoolInt, BoolInt)] forall q r. (ToRow q, FromRow r) => Connection -> Query -> q -> IO [r] DB.query Connection db [sql| SELECT created_at, updated_at, chat_ts, favorite, unread_chat FROM note_folders WHERE user_id = ? AND note_folder_id = ? |] (UserId userId, UserId noteFolderId) where toNoteFolder :: (UTCTime, UTCTime, UTCTime, BoolInt, BoolInt) -> NoteFolder toNoteFolder (UTCTime createdAt, UTCTime updatedAt, UTCTime chatTs, BI Bool favorite, BI Bool unread) = NoteFolder {UserId noteFolderId :: UserId noteFolderId :: UserId noteFolderId, UserId userId :: UserId userId :: UserId userId, UTCTime createdAt :: UTCTime createdAt :: UTCTime createdAt, UTCTime updatedAt :: UTCTime updatedAt :: UTCTime updatedAt, UTCTime chatTs :: UTCTime chatTs :: UTCTime chatTs, Bool favorite :: Bool favorite :: Bool favorite, Bool unread :: Bool unread :: Bool unread} updateNoteFolderUnreadChat :: DB.Connection -> User -> NoteFolder -> Bool -> IO () updateNoteFolderUnreadChat :: Connection -> User -> NoteFolder -> Bool -> IO () updateNoteFolderUnreadChat Connection db User {UserId userId :: User -> UserId userId :: UserId userId} NoteFolder {UserId noteFolderId :: NoteFolder -> UserId noteFolderId :: UserId noteFolderId} Bool unreadChat = do UTCTime updatedAt <- IO UTCTime getCurrentTime Connection -> Query -> (BoolInt, UTCTime, UserId, UserId) -> IO () forall q. ToRow q => Connection -> Query -> q -> IO () DB.execute Connection db Query "UPDATE note_folders SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND note_folder_id = ?" (Bool -> BoolInt BI Bool unreadChat, UTCTime updatedAt, UserId userId, UserId noteFolderId) deleteNoteFolderFiles :: DB.Connection -> UserId -> NoteFolder -> IO () deleteNoteFolderFiles :: Connection -> UserId -> NoteFolder -> IO () deleteNoteFolderFiles Connection db UserId userId NoteFolder {UserId noteFolderId :: NoteFolder -> UserId noteFolderId :: UserId noteFolderId} = do Connection -> Query -> (UserId, UserId, UserId) -> IO () forall q. ToRow q => Connection -> Query -> q -> IO () DB.execute Connection db [sql| DELETE FROM files WHERE user_id = ? AND chat_item_id IN ( SELECT chat_item_id FROM chat_items WHERE user_id = ? AND note_folder_id = ? ) |] (UserId userId, UserId userId, UserId noteFolderId) deleteNoteFolderCIs :: DB.Connection -> User -> NoteFolder -> IO () deleteNoteFolderCIs :: Connection -> User -> NoteFolder -> IO () deleteNoteFolderCIs Connection db User {UserId userId :: User -> UserId userId :: UserId userId} NoteFolder {UserId noteFolderId :: NoteFolder -> UserId noteFolderId :: UserId noteFolderId} = Connection -> Query -> (UserId, UserId) -> IO () forall q. ToRow q => Connection -> Query -> q -> IO () DB.execute Connection db Query "DELETE FROM chat_items WHERE user_id = ? AND note_folder_id = ?" (UserId userId, UserId noteFolderId)