mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 05:24:44 +00:00
core, ui: support chat item TTL per chat and group aliases (#5415)
* core: support chat item TTL per chat * ios: UI mockup * core: chat time to live and group local alias support (#5533) * functions and type placeholders * simplify * queries to make tests pass * set chat queries * fetch queries * get local aliases for groups * local alias support for groups * simplify * fix tests * fix --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com> * migration * add test for expiration * expireChatItems * refactor queries, read objects inside the loop * add groupId to query * fix updateGroupAlias * ios group alias * ttl * changes * fixes and test * new types for ttl * chat and groups ttl in ios * accurate alert * label * progress indicator, disable interactions while api running * just call expire chat items * android, desktop: add local alias to groups (#5544) * android, desktop: add local alias to groups * different placeholder for chats vs contacts * improvements and fixes * only expire chat items, not all items, when chat ttl changes * refactor, fix conditions * refactor * refactor ChatTTLOption * text * fix * make ttl state * fix crash/remove warnings * fix for current? --------- Co-authored-by: Diogo <diogofncunha@gmail.com>
This commit is contained in:
@@ -342,6 +342,7 @@ data ChatCommand
|
||||
| APIUpdateProfile UserId Profile
|
||||
| APISetContactPrefs ContactId Preferences
|
||||
| APISetContactAlias ContactId LocalAlias
|
||||
| APISetGroupAlias GroupId LocalAlias
|
||||
| APISetConnectionAlias Int64 LocalAlias
|
||||
| APISetUserUIThemes UserId (Maybe UIThemeEntityOverrides)
|
||||
| APISetChatUIThemes ChatRef (Maybe UIThemeEntityOverrides)
|
||||
@@ -379,10 +380,13 @@ data ChatCommand
|
||||
| APIGetUsageConditions
|
||||
| APISetConditionsNotified Int64
|
||||
| APIAcceptConditions Int64 (NonEmpty Int64)
|
||||
| APISetChatItemTTL UserId (Maybe Int64)
|
||||
| SetChatItemTTL (Maybe Int64)
|
||||
| APISetChatItemTTL UserId Int64
|
||||
| SetChatItemTTL Int64
|
||||
| APIGetChatItemTTL UserId
|
||||
| GetChatItemTTL
|
||||
| APISetChatTTL UserId ChatRef (Maybe Int64)
|
||||
| SetChatTTL ChatName (Maybe Int64)
|
||||
| GetChatTTL ChatName
|
||||
| APISetNetworkConfig NetworkConfig
|
||||
| APIGetNetworkConfig
|
||||
| SetNetworkConfig SimpleNetCfg
|
||||
@@ -720,6 +724,7 @@ data ChatResponse
|
||||
| CRUserProfileUpdated {user :: User, fromProfile :: Profile, toProfile :: Profile, updateSummary :: UserProfileUpdateSummary}
|
||||
| CRUserProfileImage {user :: User, profile :: Profile}
|
||||
| CRContactAliasUpdated {user :: User, toContact :: Contact}
|
||||
| CRGroupAliasUpdated {user :: User, toGroup :: GroupInfo}
|
||||
| CRConnectionAliasUpdated {user :: User, toConnection :: PendingContactConnection}
|
||||
| CRContactPrefsUpdated {user :: User, fromContact :: Contact, toContact :: Contact}
|
||||
| CRContactConnecting {user :: User, contact :: Contact}
|
||||
|
||||
@@ -176,7 +176,7 @@ startChatController mainApp enableSndFiles = do
|
||||
startXFTP xftpStartWorkers
|
||||
void $ forkIO $ startFilesToReceive users
|
||||
startCleanupManager
|
||||
void $ forkIO $ startExpireCIs users
|
||||
void $ forkIO $ mapM_ startExpireCIs users
|
||||
else when enableSndFiles $ startXFTP xftpStartSndWorkers
|
||||
pure a1
|
||||
startXFTP startWorkers = do
|
||||
@@ -191,12 +191,15 @@ startChatController mainApp enableSndFiles = do
|
||||
a <- Just <$> async (void $ runExceptT cleanupManager)
|
||||
atomically $ writeTVar cleanupAsync a
|
||||
_ -> pure ()
|
||||
startExpireCIs users =
|
||||
forM_ users $ \user -> do
|
||||
ttl <- fromRight Nothing <$> runExceptT (withStore' (`getChatItemTTL` user))
|
||||
forM_ ttl $ \_ -> do
|
||||
startExpireCIThread user
|
||||
setExpireCIFlag user True
|
||||
startExpireCIs user = whenM shouldExpireChats $ do
|
||||
startExpireCIThread user
|
||||
setExpireCIFlag user True
|
||||
where
|
||||
shouldExpireChats =
|
||||
fmap (fromRight False) $ runExceptT $ withStore' $ \db -> do
|
||||
ttl <- getChatItemTTL db user
|
||||
ttlCount <- getChatTTLCount db user
|
||||
pure $ ttl > 0 || ttlCount > 0
|
||||
|
||||
subscribeUsers :: Bool -> [User] -> CM' ()
|
||||
subscribeUsers onlyNeeded users = do
|
||||
@@ -1256,6 +1259,11 @@ processChatCommand' vr = \case
|
||||
ct <- getContact db vr user contactId
|
||||
liftIO $ updateContactAlias db userId ct localAlias
|
||||
pure $ CRContactAliasUpdated user ct'
|
||||
APISetGroupAlias gId localAlias -> withUser $ \user@User {userId} -> do
|
||||
gInfo' <- withFastStore $ \db -> do
|
||||
gInfo <- getGroupInfo db vr user gId
|
||||
liftIO $ updateGroupAlias db userId gInfo localAlias
|
||||
pure $ CRGroupAliasUpdated user gInfo'
|
||||
APISetConnectionAlias connId localAlias -> withUser $ \user@User {userId} -> do
|
||||
conn' <- withFastStore $ \db -> do
|
||||
conn <- getPendingContactConnection db userId connId
|
||||
@@ -1401,27 +1409,55 @@ processChatCommand' vr = \case
|
||||
currentTs <- liftIO getCurrentTime
|
||||
acceptConditions db condId opIds currentTs
|
||||
CRServerOperatorConditions <$> getServerOperators db
|
||||
APISetChatItemTTL userId newTTL_ -> withUserId userId $ \user ->
|
||||
APISetChatTTL userId (ChatRef cType chatId) newTTL_ ->
|
||||
withUserId userId $ \user -> checkStoreNotChanged $ withChatLock "setChatTTL" $ do
|
||||
(oldTTL_, globalTTL, ttlCount) <- withStore' $ \db ->
|
||||
(,,) <$> getSetChatTTL db <*> getChatItemTTL db user <*> getChatTTLCount db user
|
||||
let newTTL = fromMaybe globalTTL newTTL_
|
||||
oldTTL = fromMaybe globalTTL oldTTL_
|
||||
when (newTTL > 0 && (newTTL < oldTTL || oldTTL == 0)) $ do
|
||||
lift $ setExpireCIFlag user False
|
||||
expireChat user globalTTL `catchChatError` (toView . CRChatError (Just user))
|
||||
lift $ setChatItemsExpiration user globalTTL ttlCount
|
||||
ok user
|
||||
where
|
||||
getSetChatTTL db = case cType of
|
||||
CTDirect -> getDirectChatTTL db chatId <* setDirectChatTTL db chatId newTTL_
|
||||
CTGroup -> getGroupChatTTL db chatId <* setGroupChatTTL db chatId newTTL_
|
||||
_ -> pure Nothing
|
||||
expireChat user globalTTL = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
case cType of
|
||||
CTDirect -> expireContactChatItems user vr globalTTL chatId
|
||||
CTGroup ->
|
||||
let createdAtCutoff = addUTCTime (-43200 :: NominalDiffTime) currentTs
|
||||
in expireGroupChatItems user vr globalTTL createdAtCutoff chatId
|
||||
_ -> throwChatError $ CECommandError "not supported"
|
||||
SetChatTTL chatName newTTL -> withUser' $ \user@User {userId} -> do
|
||||
chatRef <- getChatRef user chatName
|
||||
processChatCommand $ APISetChatTTL userId chatRef newTTL
|
||||
GetChatTTL chatName -> withUser' $ \user -> do
|
||||
ChatRef cType chatId <- getChatRef user chatName
|
||||
ttl <- case cType of
|
||||
CTDirect -> withFastStore' (`getDirectChatTTL` chatId)
|
||||
CTGroup -> withFastStore' (`getGroupChatTTL` chatId)
|
||||
_ -> throwChatError $ CECommandError "not supported"
|
||||
pure $ CRChatItemTTL user ttl
|
||||
APISetChatItemTTL userId newTTL -> withUserId userId $ \user ->
|
||||
checkStoreNotChanged $
|
||||
withChatLock "setChatItemTTL" $ do
|
||||
case newTTL_ of
|
||||
Nothing -> do
|
||||
withFastStore' $ \db -> setChatItemTTL db user newTTL_
|
||||
lift $ setExpireCIFlag user False
|
||||
Just newTTL -> do
|
||||
oldTTL <- withFastStore' (`getChatItemTTL` user)
|
||||
when (maybe True (newTTL <) oldTTL) $ do
|
||||
lift $ setExpireCIFlag user False
|
||||
expireChatItems user newTTL True
|
||||
withFastStore' $ \db -> setChatItemTTL db user newTTL_
|
||||
lift $ startExpireCIThread user
|
||||
lift . whenM chatStarted $ setExpireCIFlag user True
|
||||
(oldTTL, ttlCount) <- withFastStore' $ \db ->
|
||||
(,) <$> getChatItemTTL db user <* setChatItemTTL db user newTTL <*> getChatTTLCount db user
|
||||
when (newTTL > 0 && (newTTL < oldTTL || oldTTL == 0)) $ do
|
||||
lift $ setExpireCIFlag user False
|
||||
expireChatItems user newTTL True
|
||||
lift $ setChatItemsExpiration user newTTL ttlCount
|
||||
ok user
|
||||
SetChatItemTTL newTTL_ -> withUser' $ \User {userId} -> do
|
||||
processChatCommand $ APISetChatItemTTL userId newTTL_
|
||||
APIGetChatItemTTL userId -> withUserId' userId $ \user -> do
|
||||
ttl <- withFastStore' (`getChatItemTTL` user)
|
||||
pure $ CRChatItemTTL user ttl
|
||||
pure $ CRChatItemTTL user (Just ttl)
|
||||
GetChatItemTTL -> withUser' $ \User {userId} -> do
|
||||
processChatCommand $ APIGetChatItemTTL userId
|
||||
APISetNetworkConfig cfg -> withUser' $ \_ -> lift (withAgent' (`setNetworkConfig` cfg)) >> ok_
|
||||
@@ -3246,9 +3282,16 @@ startExpireCIThread user@User {userId} = do
|
||||
atomically $ TM.lookup userId expireFlags >>= \b -> unless (b == Just True) retry
|
||||
lift waitChatStartedAndActivated
|
||||
ttl <- withStore' (`getChatItemTTL` user)
|
||||
forM_ ttl $ \t -> expireChatItems user t False
|
||||
expireChatItems user ttl False
|
||||
liftIO $ threadDelay' interval
|
||||
|
||||
setChatItemsExpiration :: User -> Int64 -> Int -> CM' ()
|
||||
setChatItemsExpiration user newTTL ttlCount
|
||||
| newTTL > 0 || ttlCount > 0 = do
|
||||
startExpireCIThread user
|
||||
whenM chatStarted $ setExpireCIFlag user True
|
||||
| otherwise = setExpireCIFlag user False
|
||||
|
||||
setExpireCIFlag :: User -> Bool -> CM' ()
|
||||
setExpireCIFlag User {userId} b = do
|
||||
expireFlags <- asks expireCIFlags
|
||||
@@ -3496,20 +3539,19 @@ cleanupManager = do
|
||||
withStore' (`deleteOldProbes` cutoffTs)
|
||||
|
||||
expireChatItems :: User -> Int64 -> Bool -> CM ()
|
||||
expireChatItems user@User {userId} ttl sync = do
|
||||
expireChatItems user@User {userId} globalTTL sync = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
vr <- chatVersionRange
|
||||
let expirationDate = addUTCTime (-1 * fromIntegral ttl) currentTs
|
||||
-- this is to keep group messages created during last 12 hours even if they're expired according to item_ts
|
||||
createdAtCutoff = addUTCTime (-43200 :: NominalDiffTime) currentTs
|
||||
-- this is to keep group messages created during last 12 hours even if they're expired according to item_ts
|
||||
let createdAtCutoff = addUTCTime (-43200 :: NominalDiffTime) currentTs
|
||||
lift waitChatStartedAndActivated
|
||||
contacts <- withStore' $ \db -> getUserContacts db vr user
|
||||
loop contacts $ processContact expirationDate
|
||||
contactIds <- withStore' $ \db -> getUserContactsToExpire db user globalTTL
|
||||
loop contactIds $ expireContactChatItems user vr globalTTL
|
||||
lift waitChatStartedAndActivated
|
||||
groups <- withStore' $ \db -> getUserGroupDetails db vr user Nothing Nothing
|
||||
loop groups $ processGroup vr expirationDate createdAtCutoff
|
||||
groupIds <- withStore' $ \db -> getUserGroupsToExpire db user globalTTL
|
||||
loop groupIds $ expireGroupChatItems user vr globalTTL createdAtCutoff
|
||||
where
|
||||
loop :: [a] -> (a -> CM ()) -> CM ()
|
||||
loop :: [Int64] -> (Int64 -> CM ()) -> CM ()
|
||||
loop [] _ = pure ()
|
||||
loop (a : as) process = continue $ do
|
||||
process a `catchChatError` (toView . CRChatError (Just user))
|
||||
@@ -3522,22 +3564,40 @@ expireChatItems user@User {userId} ttl sync = do
|
||||
expireFlags <- asks expireCIFlags
|
||||
expire <- atomically $ TM.lookup userId expireFlags
|
||||
when (expire == Just True) $ threadDelay 100000 >> a
|
||||
processContact :: UTCTime -> Contact -> CM ()
|
||||
processContact expirationDate ct = do
|
||||
lift waitChatStartedAndActivated
|
||||
filesInfo <- withStore' $ \db -> getContactExpiredFileInfo db user ct expirationDate
|
||||
cancelFilesInProgress user filesInfo
|
||||
deleteFilesLocally filesInfo
|
||||
withStore' $ \db -> deleteContactExpiredCIs db user ct expirationDate
|
||||
processGroup :: VersionRangeChat -> UTCTime -> UTCTime -> GroupInfo -> CM ()
|
||||
processGroup vr expirationDate createdAtCutoff gInfo = do
|
||||
lift waitChatStartedAndActivated
|
||||
filesInfo <- withStore' $ \db -> getGroupExpiredFileInfo db user gInfo expirationDate createdAtCutoff
|
||||
cancelFilesInProgress user filesInfo
|
||||
deleteFilesLocally filesInfo
|
||||
withStore' $ \db -> deleteGroupExpiredCIs db user gInfo expirationDate createdAtCutoff
|
||||
membersToDelete <- withStore' $ \db -> getGroupMembersForExpiration db vr user gInfo
|
||||
forM_ membersToDelete $ \m -> withStore' $ \db -> deleteGroupMember db user m
|
||||
|
||||
expireContactChatItems :: User -> VersionRangeChat -> Int64 -> ContactId -> CM ()
|
||||
expireContactChatItems user vr globalTTL ctId =
|
||||
-- reading contacts and groups inside the loop,
|
||||
-- to allow ttl changing while processing and to reduce memory usage
|
||||
tryChatError (withStore $ \db -> getContact db vr user ctId) >>= mapM_ process
|
||||
where
|
||||
process ct@Contact {chatItemTTL} =
|
||||
withExpirationDate globalTTL chatItemTTL $ \expirationDate -> do
|
||||
lift waitChatStartedAndActivated
|
||||
filesInfo <- withStore' $ \db -> getContactExpiredFileInfo db user ct expirationDate
|
||||
cancelFilesInProgress user filesInfo
|
||||
deleteFilesLocally filesInfo
|
||||
withStore' $ \db -> deleteContactExpiredCIs db user ct expirationDate
|
||||
|
||||
expireGroupChatItems :: User -> VersionRangeChat -> Int64 -> UTCTime -> GroupId -> CM ()
|
||||
expireGroupChatItems user vr globalTTL createdAtCutoff groupId =
|
||||
tryChatError (withStore $ \db -> getGroupInfo db vr user groupId) >>= mapM_ process
|
||||
where
|
||||
process gInfo@GroupInfo {chatItemTTL} =
|
||||
withExpirationDate globalTTL chatItemTTL $ \expirationDate -> do
|
||||
lift waitChatStartedAndActivated
|
||||
filesInfo <- withStore' $ \db -> getGroupExpiredFileInfo db user gInfo expirationDate createdAtCutoff
|
||||
cancelFilesInProgress user filesInfo
|
||||
deleteFilesLocally filesInfo
|
||||
withStore' $ \db -> deleteGroupExpiredCIs db user gInfo expirationDate createdAtCutoff
|
||||
membersToDelete <- withStore' $ \db -> getGroupMembersForExpiration db vr user gInfo
|
||||
forM_ membersToDelete $ \m -> withStore' $ \db -> deleteGroupMember db user m
|
||||
|
||||
withExpirationDate :: Int64 -> Maybe Int64 -> (UTCTime -> CM ()) -> CM ()
|
||||
withExpirationDate globalTTL chatItemTTL action = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
let ttl = fromMaybe globalTTL chatItemTTL
|
||||
when (ttl > 0) $ action $ addUTCTime (-1 * fromIntegral ttl) currentTs
|
||||
|
||||
chatCommandP :: Parser ChatCommand
|
||||
chatCommandP =
|
||||
@@ -3653,6 +3713,7 @@ chatCommandP =
|
||||
"/_network_statuses" $> APIGetNetworkStatuses,
|
||||
"/_profile " *> (APIUpdateProfile <$> A.decimal <* A.space <*> jsonP),
|
||||
"/_set alias @" *> (APISetContactAlias <$> A.decimal <*> (A.space *> textP <|> pure "")),
|
||||
"/_set alias #" *> (APISetGroupAlias <$> A.decimal <*> (A.space *> textP <|> pure "")),
|
||||
"/_set alias :" *> (APISetConnectionAlias <$> A.decimal <*> (A.space *> textP <|> pure "")),
|
||||
"/_set prefs @" *> (APISetContactPrefs <$> A.decimal <* A.space <*> jsonP),
|
||||
"/_set theme user " *> (APISetUserUIThemes <$> A.decimal <*> optional (A.space *> jsonP)),
|
||||
@@ -3688,10 +3749,13 @@ chatCommandP =
|
||||
"/_conditions" $> APIGetUsageConditions,
|
||||
"/_conditions_notified " *> (APISetConditionsNotified <$> A.decimal),
|
||||
"/_accept_conditions " *> (APIAcceptConditions <$> A.decimal <*> _strP),
|
||||
"/_ttl " *> (APISetChatItemTTL <$> A.decimal <* A.space <*> ciTTLDecimal),
|
||||
"/ttl " *> (SetChatItemTTL <$> ciTTL),
|
||||
"/_ttl " *> (APISetChatItemTTL <$> A.decimal <* A.space <*> A.decimal),
|
||||
"/_ttl " *> (APISetChatTTL <$> A.decimal <* A.space <*> chatRefP <* A.space <*> ciTTLDecimal),
|
||||
"/_ttl " *> (APIGetChatItemTTL <$> A.decimal),
|
||||
"/ttl " *> (SetChatItemTTL <$> ciTTL),
|
||||
"/ttl" $> GetChatItemTTL,
|
||||
"/ttl " *> (SetChatTTL <$> chatNameP <* A.space <*> (("default" $> Nothing) <|> (Just <$> ciTTL))),
|
||||
"/ttl " *> (GetChatTTL <$> chatNameP),
|
||||
"/_network info " *> (APISetNetworkInfo <$> jsonP),
|
||||
"/_network " *> (APISetNetworkConfig <$> jsonP),
|
||||
("/network " <|> "/net ") *> (SetNetworkConfig <$> netCfgP),
|
||||
@@ -3982,12 +4046,13 @@ chatCommandP =
|
||||
chatNameP' = ChatName <$> (chatTypeP <|> pure CTDirect) <*> displayNameP
|
||||
chatRefP = ChatRef <$> chatTypeP <*> A.decimal
|
||||
msgCountP = A.space *> A.decimal <|> pure 10
|
||||
ciTTLDecimal = ("none" $> Nothing) <|> (Just <$> A.decimal)
|
||||
ciTTLDecimal = ("default" $> Nothing) <|> (Just <$> A.decimal)
|
||||
ciTTL =
|
||||
("day" $> Just 86400)
|
||||
<|> ("week" $> Just (7 * 86400))
|
||||
<|> ("month" $> Just (30 * 86400))
|
||||
<|> ("none" $> Nothing)
|
||||
("day" $> 86400)
|
||||
<|> ("week" $> (7 * 86400))
|
||||
<|> ("month" $> (30 * 86400))
|
||||
<|> ("year" $> (365 * 86400))
|
||||
<|> ("none" $> 0)
|
||||
timedTTLP =
|
||||
("30s" $> 30)
|
||||
<|> ("5min" $> 300)
|
||||
|
||||
@@ -110,19 +110,19 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
[sql|
|
||||
SELECT
|
||||
c.contact_profile_id, c.local_display_name, c.via_group, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
|
||||
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.contact_group_member_id, c.contact_grp_inv_sent, c.ui_themes, c.chat_deleted, c.custom_data
|
||||
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.contact_group_member_id, c.contact_grp_inv_sent, c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl
|
||||
FROM contacts c
|
||||
JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id
|
||||
WHERE c.user_id = ? AND c.contact_id = ? AND c.deleted = 0
|
||||
|]
|
||||
(userId, contactId)
|
||||
toContact' :: Int64 -> Connection -> [ChatTagId] -> ContactRow' -> Contact
|
||||
toContact' contactId conn chatTags ((profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. (contactGroupMemberId, BI contactGrpInvSent, uiThemes, BI chatDeleted, customData)) =
|
||||
toContact' contactId conn chatTags ((profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. (contactGroupMemberId, BI contactGrpInvSent, uiThemes, BI chatDeleted, customData, chatItemTTL)) =
|
||||
let profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito conn
|
||||
activeConn = Just conn
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, contactGroupMemberId, contactGrpInvSent, chatTags, uiThemes, chatDeleted, customData}
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, contactGroupMemberId, contactGrpInvSent, chatTags, chatItemTTL, uiThemes, chatDeleted, customData}
|
||||
getGroupAndMember_ :: Int64 -> Connection -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
getGroupAndMember_ groupMemberId c = do
|
||||
gm <-
|
||||
@@ -133,9 +133,9 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, 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, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
-- GroupInfo {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.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
|
||||
@@ -82,6 +82,9 @@ module Simplex.Chat.Store.Direct
|
||||
setContactChatDeleted,
|
||||
getDirectChatTags,
|
||||
updateDirectChatTags,
|
||||
setDirectChatTTL,
|
||||
getDirectChatTTL,
|
||||
getUserContactsToExpire
|
||||
)
|
||||
where
|
||||
|
||||
@@ -198,7 +201,7 @@ getContactByConnReqHash db vr user@User {userId} cReqHash = do
|
||||
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, ct.ui_themes, ct.chat_deleted, ct.custom_data,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
-- 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.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
@@ -263,6 +266,7 @@ createDirectContact db user@User {userId} conn@Connection {connId, localAlias} p
|
||||
contactGroupMemberId = Nothing,
|
||||
contactGrpInvSent = False,
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
chatDeleted = False,
|
||||
customData = Nothing
|
||||
@@ -659,7 +663,7 @@ createOrUpdateContactRequest db vr user@User {userId, userContactId} userContact
|
||||
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, ct.ui_themes, ct.chat_deleted, ct.custom_data,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
-- 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.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
@@ -838,6 +842,7 @@ createAcceptedContact db user@User {userId, profile = LocalProfile {preferences}
|
||||
contactGroupMemberId = Nothing,
|
||||
contactGrpInvSent = False,
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
chatDeleted = False,
|
||||
customData = Nothing
|
||||
@@ -873,7 +878,7 @@ getContact_ db vr user@User {userId} contactId deleted = do
|
||||
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, ct.ui_themes, ct.chat_deleted, ct.custom_data,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
|
||||
-- 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.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
@@ -1078,3 +1083,19 @@ addDirectChatTags :: DB.Connection -> Contact -> IO Contact
|
||||
addDirectChatTags db ct = do
|
||||
chatTags <- getDirectChatTags db $ contactId' ct
|
||||
pure (ct :: Contact) {chatTags}
|
||||
|
||||
setDirectChatTTL :: DB.Connection -> ContactId -> Maybe Int64 -> IO ()
|
||||
setDirectChatTTL db ctId ttl = do
|
||||
updatedAt <- getCurrentTime
|
||||
DB.execute db "UPDATE contacts SET chat_item_ttl = ?, updated_at = ? WHERE contact_id = ?" (ttl, updatedAt, ctId)
|
||||
|
||||
getDirectChatTTL :: DB.Connection -> ContactId -> IO (Maybe Int64)
|
||||
getDirectChatTTL db ctId =
|
||||
fmap join . maybeFirstRow fromOnly $
|
||||
DB.query db "SELECT chat_item_ttl FROM contacts WHERE contact_id = ? LIMIT 1" (Only ctId)
|
||||
|
||||
getUserContactsToExpire :: DB.Connection -> User -> Int64 -> IO [ContactId]
|
||||
getUserContactsToExpire db User {userId} globalTTL =
|
||||
map fromOnly <$> DB.query db ("SELECT contact_id FROM contacts WHERE user_id = ? AND chat_item_ttl > 0" <> cond) (Only userId)
|
||||
where
|
||||
cond = if globalTTL == 0 then "" else " OR chat_item_ttl IS NULL"
|
||||
|
||||
@@ -126,6 +126,10 @@ module Simplex.Chat.Store.Groups
|
||||
setGroupUIThemes,
|
||||
updateGroupChatTags,
|
||||
getGroupChatTags,
|
||||
setGroupChatTTL,
|
||||
getGroupChatTTL,
|
||||
getUserGroupsToExpire,
|
||||
updateGroupAlias,
|
||||
)
|
||||
where
|
||||
|
||||
@@ -160,13 +164,9 @@ import Simplex.Messaging.Protocol (SubscriptionMode (..))
|
||||
import Simplex.Messaging.Util (eitherToMaybe, ($>>=), (<$$>))
|
||||
import Simplex.Messaging.Version
|
||||
import UnliftIO.STM
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..), Query, (:.) (..))
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
#else
|
||||
|
||||
import Database.SQLite.Simple (Only (..), Query, (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
type MaybeGroupMemberRow = ((Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId, Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe ImageData, Maybe ConnReqContact, Maybe LocalAlias, Maybe Preferences))
|
||||
|
||||
@@ -268,9 +268,9 @@ getGroupAndMember db User {userId, userContactId} groupMemberId vr = do
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, 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, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
-- GroupInfo {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.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -337,6 +337,7 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
|
||||
{ groupId,
|
||||
localDisplayName = ldn,
|
||||
groupProfile,
|
||||
localAlias = "",
|
||||
businessChat = Nothing,
|
||||
fullGroupPreferences,
|
||||
membership,
|
||||
@@ -347,6 +348,7 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
|
||||
chatTs = Just currentTs,
|
||||
userMemberProfileSentAt = Just currentTs,
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
customData = Nothing
|
||||
}
|
||||
@@ -406,6 +408,7 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
|
||||
{ groupId,
|
||||
localDisplayName,
|
||||
groupProfile,
|
||||
localAlias = "",
|
||||
businessChat = Nothing,
|
||||
fullGroupPreferences,
|
||||
membership,
|
||||
@@ -416,6 +419,7 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
|
||||
chatTs = Just currentTs,
|
||||
userMemberProfileSentAt = Just currentTs,
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
customData = Nothing
|
||||
},
|
||||
@@ -646,9 +650,9 @@ getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, 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, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
mu.group_member_id, g.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.member_restriction,
|
||||
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
|
||||
FROM groups g
|
||||
@@ -1388,9 +1392,9 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = do
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, 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, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
-- GroupInfo {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.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -2074,7 +2078,7 @@ createMemberContact
|
||||
quotaErrCounter = 0
|
||||
}
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn
|
||||
pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, viaGroup = Nothing, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False, chatTags = [], uiThemes = Nothing, chatDeleted = False, customData = Nothing}
|
||||
pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, viaGroup = Nothing, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False, chatTags = [], chatItemTTL = Nothing, uiThemes = Nothing, chatDeleted = False, customData = Nothing}
|
||||
|
||||
getMemberContact :: DB.Connection -> VersionRangeChat -> User -> ContactId -> ExceptT StoreError IO (GroupInfo, GroupMember, Contact, ConnReqInvitation)
|
||||
getMemberContact db vr user contactId = do
|
||||
@@ -2111,7 +2115,7 @@ createMemberContactInvited
|
||||
contactId <- createContactUpdateMember currentTs userPreferences
|
||||
ctConn <- createMemberContactConn_ db user connIds gInfo mConn contactId subMode
|
||||
let mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn
|
||||
mCt' = Contact {contactId, localDisplayName = memberLDN, profile = memberProfile, activeConn = Just ctConn, viaGroup = Nothing, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, contactGroupMemberId = Nothing, contactGrpInvSent = False, chatTags = [], uiThemes = Nothing, chatDeleted = False, customData = Nothing}
|
||||
mCt' = Contact {contactId, localDisplayName = memberLDN, profile = memberProfile, activeConn = Just ctConn, viaGroup = Nothing, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, contactGroupMemberId = Nothing, contactGrpInvSent = False, chatTags = [], chatItemTTL = Nothing, uiThemes = Nothing, chatDeleted = False, customData = Nothing}
|
||||
m' = m {memberContactId = Just contactId}
|
||||
pure (mCt', m')
|
||||
where
|
||||
@@ -2350,3 +2354,28 @@ untagGroupChat db groupId tId =
|
||||
WHERE group_id = ? AND chat_tag_id = ?
|
||||
|]
|
||||
(groupId, tId)
|
||||
|
||||
setGroupChatTTL :: DB.Connection -> GroupId -> Maybe Int64 -> IO ()
|
||||
setGroupChatTTL db gId ttl = do
|
||||
updatedAt <- getCurrentTime
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE groups SET chat_item_ttl = ?, updated_at = ? WHERE group_id = ?"
|
||||
(ttl, updatedAt, gId)
|
||||
|
||||
getGroupChatTTL :: DB.Connection -> GroupId -> IO (Maybe Int64)
|
||||
getGroupChatTTL db gId =
|
||||
fmap join . maybeFirstRow fromOnly $
|
||||
DB.query db "SELECT chat_item_ttl FROM groups WHERE group_id = ? LIMIT 1" (Only gId)
|
||||
|
||||
getUserGroupsToExpire :: DB.Connection -> User -> Int64 -> IO [GroupId]
|
||||
getUserGroupsToExpire db User {userId} globalTTL =
|
||||
map fromOnly <$> DB.query db ("SELECT group_id FROM groups WHERE user_id = ? AND chat_item_ttl > 0" <> cond) (Only userId)
|
||||
where
|
||||
cond = if globalTTL == 0 then "" else " OR chat_item_ttl IS NULL"
|
||||
|
||||
updateGroupAlias :: DB.Connection -> UserId -> GroupInfo -> LocalAlias -> IO GroupInfo
|
||||
updateGroupAlias db userId g@GroupInfo {groupId} localAlias = do
|
||||
updatedAt <- getCurrentTime
|
||||
DB.execute db "UPDATE groups SET local_alias = ?, updated_at = ? WHERE user_id = ? AND group_id = ?" (localAlias, updatedAt, userId, groupId)
|
||||
pure (g :: GroupInfo) {localAlias = localAlias}
|
||||
|
||||
@@ -107,6 +107,7 @@ module Simplex.Chat.Store.Messages
|
||||
getTimedItems,
|
||||
getChatItemTTL,
|
||||
setChatItemTTL,
|
||||
getChatTTLCount,
|
||||
getContactExpiredFileInfo,
|
||||
deleteContactExpiredCIs,
|
||||
getGroupExpiredFileInfo,
|
||||
@@ -2885,11 +2886,12 @@ getTimedItems db User {userId} startTimedThreadCutoff =
|
||||
(itemId, Nothing, Just groupId, deleteAt) -> Just ((ChatRef CTGroup groupId, itemId), deleteAt)
|
||||
_ -> Nothing
|
||||
|
||||
getChatItemTTL :: DB.Connection -> User -> IO (Maybe Int64)
|
||||
getChatItemTTL :: DB.Connection -> User -> IO Int64
|
||||
getChatItemTTL db User {userId} =
|
||||
fmap join . maybeFirstRow fromOnly $ DB.query db "SELECT chat_item_ttl FROM settings WHERE user_id = ? LIMIT 1" (Only userId)
|
||||
fmap (fromMaybe 0 . join) . maybeFirstRow fromOnly $
|
||||
DB.query db "SELECT chat_item_ttl FROM settings WHERE user_id = ? LIMIT 1" (Only userId)
|
||||
|
||||
setChatItemTTL :: DB.Connection -> User -> Maybe Int64 -> IO ()
|
||||
setChatItemTTL :: DB.Connection -> User -> Int64 -> IO ()
|
||||
setChatItemTTL db User {userId} chatItemTTL = do
|
||||
currentTs <- getCurrentTime
|
||||
r :: (Maybe Int64) <- maybeFirstRow fromOnly $ DB.query db "SELECT 1 FROM settings WHERE user_id = ? LIMIT 1" (Only userId)
|
||||
@@ -2905,6 +2907,14 @@ setChatItemTTL db User {userId} chatItemTTL = do
|
||||
"INSERT INTO settings (user_id, chat_item_ttl, created_at, updated_at) VALUES (?,?,?,?)"
|
||||
(userId, chatItemTTL, currentTs, currentTs)
|
||||
|
||||
getChatTTLCount :: DB.Connection -> User -> IO Int
|
||||
getChatTTLCount db User {userId} = do
|
||||
contactCount <- getCount "SELECT COUNT(1) FROM contacts WHERE user_id = ? AND chat_item_ttl > 0"
|
||||
groupCount <- getCount "SELECT COUNT(1) FROM groups WHERE user_id = ? AND chat_item_ttl > 0"
|
||||
pure $ contactCount + groupCount
|
||||
where
|
||||
getCount q = fromOnly . head <$> DB.query db q (Only userId)
|
||||
|
||||
getContactExpiredFileInfo :: DB.Connection -> User -> Contact -> UTCTime -> IO [CIFileInfo]
|
||||
getContactExpiredFileInfo db User {userId} Contact {contactId} expirationDate =
|
||||
map toFileInfo
|
||||
|
||||
@@ -82,6 +82,7 @@ CREATE TABLE contacts(
|
||||
custom_data BYTEA,
|
||||
ui_themes TEXT,
|
||||
chat_deleted SMALLINT NOT NULL DEFAULT 0,
|
||||
chat_item_ttl BIGINT,
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
@@ -140,6 +141,8 @@ CREATE TABLE groups(
|
||||
business_chat TEXT NULL,
|
||||
business_xcontact_id BYTEA NULL,
|
||||
customer_member_id BYTEA NULL,
|
||||
chat_item_ttl BIGINT,
|
||||
local_alias TEXT DEFAULT '',
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
|
||||
@@ -123,6 +123,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20241222_operator_conditions
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241223_chat_tags
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241230_reports
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250105_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250115_chat_ttl
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||
@@ -245,7 +246,8 @@ schemaMigrations =
|
||||
("20241222_operator_conditions", m20241222_operator_conditions, Just down_m20241222_operator_conditions),
|
||||
("20241223_chat_tags", m20241223_chat_tags, Just down_m20241223_chat_tags),
|
||||
("20241230_reports", m20241230_reports, Just down_m20241230_reports),
|
||||
("20250105_indexes", m20250105_indexes, Just down_m20250105_indexes)
|
||||
("20250105_indexes", m20250105_indexes, Just down_m20250105_indexes),
|
||||
("20250115_chat_ttl", m20250115_chat_ttl, Just down_m20250115_chat_ttl)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20250115_chat_ttl where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20250115_chat_ttl :: Query
|
||||
m20250115_chat_ttl =
|
||||
[sql|
|
||||
ALTER TABLE contacts ADD COLUMN chat_item_ttl INTEGER;
|
||||
ALTER TABLE groups ADD COLUMN chat_item_ttl INTEGER;
|
||||
ALTER TABLE groups ADD COLUMN local_alias TEXT DEFAULT '';
|
||||
|]
|
||||
|
||||
down_m20250115_chat_ttl :: Query
|
||||
down_m20250115_chat_ttl =
|
||||
[sql|
|
||||
ALTER TABLE contacts DROP COLUMN chat_item_ttl;
|
||||
ALTER TABLE groups DROP COLUMN chat_item_ttl;
|
||||
ALTER TABLE groups DROP COLUMN local_alias;
|
||||
|]
|
||||
@@ -78,6 +78,7 @@ CREATE TABLE contacts(
|
||||
custom_data BLOB,
|
||||
ui_themes TEXT,
|
||||
chat_deleted INTEGER NOT NULL DEFAULT 0,
|
||||
chat_item_ttl INTEGER,
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
@@ -131,7 +132,9 @@ CREATE TABLE groups(
|
||||
business_member_id BLOB NULL,
|
||||
business_chat TEXT NULL,
|
||||
business_xcontact_id BLOB NULL,
|
||||
customer_member_id BLOB NULL, -- received
|
||||
customer_member_id BLOB NULL,
|
||||
chat_item_ttl INTEGER,
|
||||
local_alias TEXT DEFAULT '', -- received
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
|
||||
@@ -414,18 +414,18 @@ deleteUnusedIncognitoProfileById_ db User {userId} profileId =
|
||||
|]
|
||||
(userId, profileId, userId, profileId, userId, profileId)
|
||||
|
||||
type ContactRow' = (ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. (Maybe GroupMemberId, BoolInt, Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData)
|
||||
type ContactRow' = (ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. (Maybe GroupMemberId, BoolInt, Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64)
|
||||
|
||||
type ContactRow = Only ContactId :. ContactRow'
|
||||
|
||||
toContact :: VersionRangeChat -> User -> [ChatTagId] -> ContactRow :. MaybeConnectionRow -> Contact
|
||||
toContact vr user chatTags ((Only contactId :. (profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. (contactGroupMemberId, BI contactGrpInvSent, uiThemes, BI chatDeleted, customData)) :. connRow) =
|
||||
toContact vr user chatTags ((Only contactId :. (profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. (contactGroupMemberId, BI contactGrpInvSent, uiThemes, BI chatDeleted, customData, chatItemTTL)) :. connRow) =
|
||||
let profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
|
||||
activeConn = toMaybeConnection vr connRow
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
incognito = maybe False connIncognito activeConn
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences incognito
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, contactGroupMemberId, contactGrpInvSent, chatTags, uiThemes, chatDeleted, customData}
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, contactGroupMemberId, contactGrpInvSent, chatTags, chatItemTTL, uiThemes, chatDeleted, customData}
|
||||
|
||||
getProfileById :: DB.Connection -> UserId -> Int64 -> ExceptT StoreError IO LocalProfile
|
||||
getProfileById db userId profileId =
|
||||
@@ -575,18 +575,18 @@ safeDeleteLDN db User {userId} localDisplayName = do
|
||||
|
||||
type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe MemberId)
|
||||
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Maybe ImageData, Maybe ProfileId, Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData) :. GroupMemberRow
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Text, Maybe Text, Maybe ImageData, Maybe ProfileId, Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData, Maybe Int64) :. GroupMemberRow
|
||||
|
||||
type GroupMemberRow = ((Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId, ProfileId, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, LocalAlias, Maybe Preferences))
|
||||
|
||||
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
|
||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, description, image, hostConnCustomUserProfileId, enableNtfs_, sendRcpts, BI favorite, groupPreferences) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. businessRow :. (uiThemes, customData) :. userMemberRow) =
|
||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, localAlias, description, image, hostConnCustomUserProfileId, enableNtfs_, sendRcpts, BI favorite, groupPreferences) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. businessRow :. (uiThemes, customData, chatItemTTL) :. userMemberRow) =
|
||||
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||
groupProfile = GroupProfile {displayName, fullName, description, image, groupPreferences}
|
||||
businessChat = toBusinessChatInfo businessRow
|
||||
in GroupInfo {groupId, localDisplayName, groupProfile, businessChat, fullGroupPreferences, membership, hostConnCustomUserProfileId, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, chatTags, uiThemes, customData}
|
||||
in GroupInfo {groupId, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, hostConnCustomUserProfileId, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, chatTags, chatItemTTL, uiThemes, customData}
|
||||
|
||||
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
|
||||
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId, profileId, displayName, fullName, image, contactLink, localAlias, preferences)) =
|
||||
@@ -607,9 +607,9 @@ groupInfoQuery =
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, 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, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
-- 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.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
|
||||
@@ -188,6 +188,7 @@ data Contact = Contact
|
||||
contactGroupMemberId :: Maybe GroupMemberId,
|
||||
contactGrpInvSent :: Bool,
|
||||
chatTags :: [ChatTagId],
|
||||
chatItemTTL :: Maybe Int64,
|
||||
uiThemes :: Maybe UIThemeEntityOverrides,
|
||||
chatDeleted :: Bool,
|
||||
customData :: Maybe CustomData
|
||||
@@ -381,6 +382,7 @@ data GroupInfo = GroupInfo
|
||||
{ groupId :: GroupId,
|
||||
localDisplayName :: GroupName,
|
||||
groupProfile :: GroupProfile,
|
||||
localAlias :: Text,
|
||||
businessChat :: Maybe BusinessChatInfo,
|
||||
fullGroupPreferences :: FullGroupPreferences,
|
||||
membership :: GroupMember,
|
||||
@@ -391,6 +393,7 @@ data GroupInfo = GroupInfo
|
||||
chatTs :: Maybe UTCTime,
|
||||
userMemberProfileSentAt :: Maybe UTCTime,
|
||||
chatTags :: [ChatTagId],
|
||||
chatItemTTL :: Maybe Int64,
|
||||
uiThemes :: Maybe UIThemeEntityOverrides,
|
||||
customData :: Maybe CustomData
|
||||
}
|
||||
|
||||
@@ -237,6 +237,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRUserProfileImage u p -> ttyUser u $ viewUserProfileImage p
|
||||
CRContactPrefsUpdated {user = u, fromContact, toContact} -> ttyUser u $ viewUserContactPrefsUpdated u fromContact toContact
|
||||
CRContactAliasUpdated u c -> ttyUser u $ viewContactAliasUpdated c
|
||||
CRGroupAliasUpdated u g -> ttyUser u $ viewGroupAliasUpdated g
|
||||
CRConnectionAliasUpdated u c -> ttyUser u $ viewConnectionAliasUpdated c
|
||||
CRContactUpdated {user = u, fromContact = c, toContact = c'} -> ttyUser u $ viewContactUpdated c c' <> viewContactPrefsUpdated u c c'
|
||||
CRGroupMemberUpdated {} -> []
|
||||
@@ -1182,7 +1183,7 @@ viewGroupsList gs = map groupSS $ sortOn (ldn_ . fst) gs
|
||||
groupSS (g@GroupInfo {membership, chatSettings = ChatSettings {enableNtfs}}, GroupSummary {currentMembers}) =
|
||||
case memberStatus membership of
|
||||
GSMemInvited -> groupInvitation' g
|
||||
s -> membershipIncognito g <> ttyFullGroup g <> viewMemberStatus s
|
||||
s -> membershipIncognito g <> ttyFullGroup g <> viewMemberStatus s <> alias g
|
||||
where
|
||||
viewMemberStatus = \case
|
||||
GSMemRemoved -> delete "you are removed"
|
||||
@@ -1197,6 +1198,9 @@ viewGroupsList gs = map groupSS $ sortOn (ldn_ . fst) gs
|
||||
unmute = "you can " <> highlight ("/unmute #" <> viewGroupName g)
|
||||
delete reason = " (" <> reason <> ", delete local copy: " <> highlight ("/d #" <> viewGroupName g) <> ")"
|
||||
memberCount = sShow currentMembers <> " member" <> if currentMembers == 1 then "" else "s"
|
||||
alias GroupInfo {localAlias}
|
||||
| localAlias == "" = ""
|
||||
| otherwise = " (alias: " <> plain localAlias <> ")"
|
||||
|
||||
groupInvitation' :: GroupInfo -> StyledString
|
||||
groupInvitation' g@GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName}} =
|
||||
@@ -1359,11 +1363,13 @@ viewUsageConditions current accepted_ =
|
||||
|
||||
viewChatItemTTL :: Maybe Int64 -> [StyledString]
|
||||
viewChatItemTTL = \case
|
||||
Nothing -> ["old messages are not being deleted"]
|
||||
Nothing -> ["old messages are set to delete according to default user config"]
|
||||
Just ttl
|
||||
| ttl == 0 -> ["old messages are not being deleted"]
|
||||
| ttl == 86400 -> deletedAfter "one day"
|
||||
| ttl == 7 * 86400 -> deletedAfter "one week"
|
||||
| ttl == 30 * 86400 -> deletedAfter "one month"
|
||||
| ttl == 365 * 86400 -> deletedAfter "one year"
|
||||
| otherwise -> deletedAfter $ sShow ttl <> " second(s)"
|
||||
where
|
||||
deletedAfter ttlStr = ["old messages are set to be deleted after: " <> ttlStr]
|
||||
@@ -1626,6 +1632,11 @@ viewContactAliasUpdated ct@Contact {profile = LocalProfile {localAlias}}
|
||||
| localAlias == "" = ["contact " <> ttyContact' ct <> " alias removed"]
|
||||
| otherwise = ["contact " <> ttyContact' ct <> " alias updated: " <> plain localAlias]
|
||||
|
||||
viewGroupAliasUpdated :: GroupInfo -> [StyledString]
|
||||
viewGroupAliasUpdated g@GroupInfo {localAlias}
|
||||
| localAlias == "" = ["group " <> ttyGroup' g <> " alias removed"]
|
||||
| otherwise = ["group " <> ttyGroup' g <> " alias updated: " <> plain localAlias]
|
||||
|
||||
viewConnectionAliasUpdated :: PendingContactConnection -> [StyledString]
|
||||
viewConnectionAliasUpdated PendingContactConnection {pccConnId, localAlias}
|
||||
| localAlias == "" = ["connection " <> sShow pccConnId <> " alias removed"]
|
||||
|
||||
Reference in New Issue
Block a user