From 34e08b2058d2ff808bf5cd617ef67aac38695f73 Mon Sep 17 00:00:00 2001 From: JRoberts <8711996+jr-simplex@users.noreply.github.com> Date: Tue, 20 Dec 2022 22:00:46 +0400 Subject: [PATCH] core: confirm ttl change to ensure consistent setting (#1587) * core: confirm ttl change to ensure consistent setting * wip * confirm_pref_pending * xInfo * test api * send confirmPrefProfile * refactor * don't return contact * refactor profile update * refactor further * refactor further * refactor xInfo * refactor xInfo further * refactor --- simplex-chat.cabal | 1 + src/Simplex/Chat.hs | 77 ++++++++++++++++--- ...M20221216_contacts_confirm_pref_pending.hs | 16 ++++ src/Simplex/Chat/Migrations/chat_schema.sql | 1 + src/Simplex/Chat/Store.hs | 53 ++++++++----- src/Simplex/Chat/Types.hs | 1 + tests/ChatTests.hs | 5 +- 7 files changed, 119 insertions(+), 35 deletions(-) create mode 100644 src/Simplex/Chat/Migrations/M20221216_contacts_confirm_pref_pending.hs diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 10a2b667ca..43247ab400 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -70,6 +70,7 @@ library Simplex.Chat.Migrations.M20221211_group_description Simplex.Chat.Migrations.M20221212_chat_items_timed Simplex.Chat.Migrations.M20221214_live_message + Simplex.Chat.Migrations.M20221216_contacts_confirm_pref_pending Simplex.Chat.Mobile Simplex.Chat.Options Simplex.Chat.ProfileGenerator diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 5adecc9d29..9574e5520f 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -1296,23 +1296,38 @@ processChatCommand = \case <$> withStore' (`getUserContacts` user) withChatLock "updateProfile" . procCmd $ do forM_ contacts $ \ct -> do - let mergedProfile = userProfileToSend user' Nothing $ Just ct - ct' = updateMergedPreferences user' ct - void (sendDirectContactMessage ct $ XInfo mergedProfile) `catchError` (toView . CRChatError) - when (directOrUsed ct) $ createFeatureChangedItems user' ct ct' CDDirectSnd CISndChatFeature + let ct' = updateMergedPreferences user' ct + (ct'', mergedProfile) <- sendConfirmProfile user user' ct ct' + sendProfileUpdate ct'' mergedProfile + when (directOrUsed ct'') $ createFeatureChangedItems user' ct ct'' CDDirectSnd CISndChatFeature pure $ CRUserProfileUpdated (fromLocalProfile p) p' updateContactPrefs :: User -> Contact -> Preferences -> m ChatResponse - updateContactPrefs user@User {userId} ct@Contact {activeConn = Connection {customUserProfileId}, userPreferences = contactUserPrefs} contactUserPrefs' + updateContactPrefs user ct@Contact {userPreferences = contactUserPrefs} contactUserPrefs' | contactUserPrefs == contactUserPrefs' = pure $ CRContactPrefsUpdated ct ct | otherwise = do assertDirectAllowed user MDSnd ct XInfo_ ct' <- withStore' $ \db -> updateContactUserPreferences db user ct contactUserPrefs' - incognitoProfile <- forM customUserProfileId $ \profileId -> withStore $ \db -> getProfileById db userId profileId - let p' = userProfileToSend user (fromLocalProfile <$> incognitoProfile) (Just ct') withChatLock "updateProfile" . procCmd $ do - void (sendDirectContactMessage ct' $ XInfo p') `catchError` (toView . CRChatError) - when (directOrUsed ct) $ createFeatureChangedItems user ct ct' CDDirectSnd CISndChatFeature - pure $ CRContactPrefsUpdated ct ct' + (ct'', mergedProfile) <- sendConfirmProfile user user ct ct' + sendProfileUpdate ct'' mergedProfile + when (directOrUsed ct'') $ createFeatureChangedItems user ct ct'' CDDirectSnd CISndChatFeature + pure $ CRContactPrefsUpdated ct ct'' + sendConfirmProfile :: User -> User -> Contact -> Contact -> m (Contact, Profile) + sendConfirmProfile user user'@User {userId} ct ct'@Contact {activeConn = Connection {customUserProfileId}, confirmPrefPending} = do + incognitoProfile <- forM customUserProfileId $ \profileId -> withStore $ \db -> getProfileById db userId profileId + let mergedProfile = userProfileToSend user' (fromLocalProfile <$> incognitoProfile) (Just ct') + mergedTTL = prefParam $ getPreference SCFTimedMessages (preferences (mergedProfile :: Profile)) + ct'' <- + if confirmPrefPending + then do + let confirmProfile = userProfileToSend user (fromLocalProfile <$> incognitoProfile) (Just ct) + confirmTTL = prefParam $ getPreference SCFTimedMessages (preferences (confirmProfile :: Profile)) + when (confirmTTL /= mergedTTL) $ sendProfileUpdate ct' confirmProfile + withStore' $ \db -> setContactConfirmPrefPending db user ct' False + else pure ct' + pure (ct'', mergedProfile) + sendProfileUpdate :: Contact -> Profile -> m () + sendProfileUpdate ct p = void (sendDirectContactMessage ct $ XInfo p) `catchError` (toView . CRChatError) runUpdateGroupProfile :: User -> Group -> GroupProfile -> m ChatResponse runUpdateGroupProfile user (Group g@GroupInfo {groupProfile = p} ms) p' = do let s = memberStatus $ membership g @@ -2771,9 +2786,49 @@ processAgentMessage (Just user@User {userId}) corrId agentConnId agentMessage = xInfo :: Contact -> Profile -> m () xInfo c@Contact {profile = p} p' = unless (fromLocalProfile p == p') $ do - c' <- withStore $ \db -> updateContactProfile db user c p' + c' <- updateContactProfileAndUserPrefs toView $ CRContactUpdated c c' when (directOrUsed c) $ createFeatureChangedItems user c c' CDDirectRcv CIRcvChatFeature + where + updateContactProfileAndUserPrefs + | userTTL == rcvTTL = simpleProfileUpdate + | userTTL == ctTTL = contactChangedTTL + | otherwise = rollbackTTL + where + LocalProfile {preferences = ctPrefs_} = p + ctTTL = ctPrefs_ >>= \Preferences {timedMessages} -> timedMessages >>= \TimedMessagesPreference {ttl} -> ttl + Contact {userPreferences = userPrefs@Preferences {timedMessages = userTimedMessages}} = c + userTTL = userTimedMessages >>= \TimedMessagesPreference {ttl} -> ttl + Profile {preferences = rcvPrefs_} = p' + rcvTimedMessages = rcvPrefs_ >>= \Preferences {timedMessages} -> timedMessages + rcvTTL = rcvTimedMessages >>= \TimedMessagesPreference {ttl} -> ttl + simpleProfileUpdate = withStore $ \db -> do + c' <- liftIO $ setContactConfirmPrefPending db user c False + updateContactProfile db user c' p' + contactChangedTTL = do + let userPrefs' = setContactUserPref rcvTTL + withStore $ \db -> do + c' <- liftIO $ updateContactUserPreferences db user c userPrefs' + c'' <- liftIO $ setContactConfirmPrefPending db user c' True + updateContactProfile db user c'' p' + rollbackTTL = do + let rcvTimedMessages' = rcvTimedMessages >>= \rcvTM -> Just (rcvTM :: TimedMessagesPreference) {ttl = ctTTL} + rcvPrefs' = rcvPrefs_ >>= \rcvPrefs -> Just (rcvPrefs :: Preferences) {timedMessages = rcvTimedMessages'} + p'' = (p' :: Profile) {preferences = rcvPrefs'} + userPrefs' = setContactUserPref ctTTL + withStore $ \db -> do + c' <- liftIO $ updateContactUserPreferences db user c userPrefs' + c'' <- liftIO $ setContactConfirmPrefPending db user c' False + updateContactProfile db user c'' p'' + setContactUserPref ttl_ = + let userDefault = getPreference SCFTimedMessages (fullPreferences user) + userDefaultTTL = prefParam userDefault + userTimedMessages' = case userTimedMessages of + Just userTM -> Just (userTM :: TimedMessagesPreference) {ttl = ttl_} + _ + | ttl_ /= userDefaultTTL -> Just (userDefault :: TimedMessagesPreference) {ttl = ttl_} + | otherwise -> Nothing + in (userPrefs :: Preferences) {timedMessages = userTimedMessages'} createFeatureEnabledItems :: Contact -> m () createFeatureEnabledItems ct@Contact {mergedPreferences} = diff --git a/src/Simplex/Chat/Migrations/M20221216_contacts_confirm_pref_pending.hs b/src/Simplex/Chat/Migrations/M20221216_contacts_confirm_pref_pending.hs new file mode 100644 index 0000000000..239f934212 --- /dev/null +++ b/src/Simplex/Chat/Migrations/M20221216_contacts_confirm_pref_pending.hs @@ -0,0 +1,16 @@ +{-# LANGUAGE QuasiQuotes #-} + +module Simplex.Chat.Migrations.M20221216_contacts_confirm_pref_pending where + +import Database.SQLite.Simple (Query) +import Database.SQLite.Simple.QQ (sql) + +m20221216_contacts_confirm_pref_pending :: Query +m20221216_contacts_confirm_pref_pending = + [sql| +PRAGMA ignore_check_constraints=ON; + +ALTER TABLE contacts ADD COLUMN confirm_pref_pending INTEGER DEFAULT 0 CHECK (confirm_pref_pending NOT NULL); + +PRAGMA ignore_check_constraints=OFF; +|] diff --git a/src/Simplex/Chat/Migrations/chat_schema.sql b/src/Simplex/Chat/Migrations/chat_schema.sql index effd79a668..33931f4c15 100644 --- a/src/Simplex/Chat/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Migrations/chat_schema.sql @@ -60,6 +60,7 @@ CREATE TABLE contacts( unread_chat INTEGER DEFAULT 0 CHECK(unread_chat NOT NULL), contact_used INTEGER DEFAULT 0 CHECK(contact_used NOT NULL), user_preferences TEXT DEFAULT '{}' CHECK(user_preferences NOT NULL), + confirm_pref_pending INTEGER DEFAULT 0 CHECK(confirm_pref_pending NOT NULL), FOREIGN KEY(user_id, local_display_name) REFERENCES display_names(user_id, local_display_name) ON DELETE CASCADE diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 6441ca0e84..40a7d48daf 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -41,6 +41,7 @@ module Simplex.Chat.Store getContactIdByName, updateUserProfile, updateContactProfile, + setContactConfirmPrefPending, updateContactUserPreferences, updateContactAlias, updateContactConnectionAlias, @@ -320,6 +321,7 @@ import Simplex.Chat.Migrations.M20221210_idxs import Simplex.Chat.Migrations.M20221211_group_description import Simplex.Chat.Migrations.M20221212_chat_items_timed import Simplex.Chat.Migrations.M20221214_live_message +import Simplex.Chat.Migrations.M20221216_contacts_confirm_pref_pending import Simplex.Chat.Protocol import Simplex.Chat.Types import Simplex.Messaging.Agent.Protocol (ACorrId, AgentMsgId, ConnId, InvitationId, MsgMeta (..)) @@ -375,7 +377,8 @@ schemaMigrations = ("20221210_idxs", m20221210_idxs), ("20221211_group_description", m20221211_group_description), ("20221212_chat_items_timed", m20221212_chat_items_timed), - ("20221214_live_message", m20221214_live_message) + ("20221214_live_message", m20221214_live_message), + ("20221216_contacts_confirm_pref_pending", m20221216_contacts_confirm_pref_pending) ] -- | The list of migrations in ascending order by date @@ -483,7 +486,7 @@ getConnReqContactXContactId db user@User {userId} cReqHash = do [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.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, + ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.confirm_pref_pending, ct.created_at, ct.updated_at, -- 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.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 @@ -572,7 +575,7 @@ createDirectContact db user@User {userId} activeConn@Connection {connId, localAl let profile = toLocalProfile profileId p localAlias userPreferences = emptyChatPrefs mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito activeConn - pure $ Contact {contactId, localDisplayName, profile, activeConn, viaGroup = Nothing, contactUsed = False, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt, updatedAt = createdAt} + pure $ Contact {contactId, localDisplayName, profile, activeConn, viaGroup = Nothing, contactUsed = False, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, confirmPrefPending = False, createdAt, updatedAt = createdAt} createContact_ :: DB.Connection -> UserId -> Int64 -> Profile -> LocalAlias -> Maybe Int64 -> UTCTime -> ExceptT StoreError IO (Text, ContactId, ProfileId) createContact_ db userId connId Profile {displayName, fullName, image, preferences} localAlias viaGroup currentTs = @@ -692,6 +695,14 @@ updateContactProfile db user@User {userId} c p' profile = toLocalProfile profileId p' localAlias mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito activeConn +setContactConfirmPrefPending :: DB.Connection -> User -> Contact -> Bool -> IO Contact +setContactConfirmPrefPending db User {userId} ct@Contact {contactId} confirmPrefPending = do + DB.execute + db + "UPDATE contacts SET confirm_pref_pending = ? WHERE user_id = ? AND contact_id = ?" + (confirmPrefPending, userId, contactId) + pure ct {confirmPrefPending} + updateContactUserPreferences :: DB.Connection -> User -> Contact -> Preferences -> IO Contact updateContactUserPreferences db user@User {userId} c@Contact {contactId, activeConn} userPreferences = do updatedAt <- getCurrentTime @@ -776,24 +787,24 @@ updateContact_ db userId contactId displayName newName updatedAt = do (newName, updatedAt, userId, contactId) DB.execute db "DELETE FROM display_names WHERE local_display_name = ? AND user_id = ?" (displayName, userId) -type ContactRow = (ContactId, ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, LocalAlias, Bool, Maybe Bool) :. (Maybe Preferences, Preferences, UTCTime, UTCTime) +type ContactRow = (ContactId, ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, LocalAlias, Bool, Maybe Bool) :. (Maybe Preferences, Preferences, Bool, UTCTime, UTCTime) toContact :: User -> ContactRow :. ConnectionRow -> Contact -toContact user (((contactId, profileId, localDisplayName, viaGroup, displayName, fullName, image, localAlias, contactUsed, enableNtfs_) :. (preferences, userPreferences, createdAt, updatedAt)) :. connRow) = +toContact user (((contactId, profileId, localDisplayName, viaGroup, displayName, fullName, image, localAlias, contactUsed, enableNtfs_) :. (preferences, userPreferences, confirmPrefPending, createdAt, updatedAt)) :. connRow) = let profile = LocalProfile {profileId, displayName, fullName, image, preferences, localAlias} activeConn = toConnection connRow chatSettings = ChatSettings {enableNtfs = fromMaybe True enableNtfs_} mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito activeConn - in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt} + in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, chatSettings, userPreferences, mergedPreferences, confirmPrefPending, createdAt, updatedAt} toContactOrError :: User -> ContactRow :. MaybeConnectionRow -> Either StoreError Contact -toContactOrError user (((contactId, profileId, localDisplayName, viaGroup, displayName, fullName, image, localAlias, contactUsed, enableNtfs_) :. (preferences, userPreferences, createdAt, updatedAt)) :. connRow) = +toContactOrError user (((contactId, profileId, localDisplayName, viaGroup, displayName, fullName, image, localAlias, contactUsed, enableNtfs_) :. (preferences, userPreferences, confirmPrefPending, createdAt, updatedAt)) :. connRow) = let profile = LocalProfile {profileId, displayName, fullName, image, preferences, localAlias} chatSettings = ChatSettings {enableNtfs = fromMaybe True enableNtfs_} in case toMaybeConnection connRow of Just activeConn -> let mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito activeConn - in Right Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt} + in Right Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, chatSettings, userPreferences, mergedPreferences, confirmPrefPending, createdAt, updatedAt} _ -> Left $ SEContactNotReady localDisplayName getContactByName :: DB.Connection -> User -> ContactName -> ExceptT StoreError IO Contact @@ -1092,7 +1103,7 @@ createOrUpdateContactRequest db user@User {userId} userContactLinkId invId Profi [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.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, + ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.confirm_pref_pending, ct.created_at, ct.updated_at, -- 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.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 @@ -1219,7 +1230,7 @@ createAcceptedContact db user@User {userId, profile = LocalProfile {preferences} contactId <- insertedRowId db activeConn <- createConnection_ db userId ConnContact (Just contactId) agentConnId Nothing (Just userContactLinkId) customUserProfileId 0 createdAt let mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito activeConn - pure $ Contact {contactId, localDisplayName, profile = toLocalProfile profileId profile "", activeConn, viaGroup = Nothing, contactUsed = False, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = createdAt, updatedAt = createdAt} + pure $ Contact {contactId, localDisplayName, profile = toLocalProfile profileId profile "", activeConn, viaGroup = Nothing, contactUsed = False, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, confirmPrefPending = False, createdAt = createdAt, updatedAt = createdAt} getLiveSndFileTransfers :: DB.Connection -> User -> IO [SndFileTransfer] getLiveSndFileTransfers db User {userId} = do @@ -1502,18 +1513,18 @@ getConnectionEntity db user@User {userId, userContactId} agentConnId = do <$> DB.query db [sql| - SELECT c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.image, p.local_alias, c.via_group, c.contact_used, c.enable_ntfs, p.preferences, c.user_preferences, c.created_at, c.updated_at + SELECT c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.image, p.local_alias, c.via_group, c.contact_used, c.enable_ntfs, p.preferences, c.user_preferences, c.confirm_pref_pending, c.created_at, c.updated_at FROM contacts c JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id WHERE c.user_id = ? AND c.contact_id = ? |] (userId, contactId) - toContact' :: Int64 -> Connection -> [(ProfileId, ContactName, Text, Text, Maybe ImageData, LocalAlias, Maybe Int64, Bool, Maybe Bool) :. (Maybe Preferences, Preferences, UTCTime, UTCTime)] -> Either StoreError Contact - toContact' contactId activeConn [(profileId, localDisplayName, displayName, fullName, image, localAlias, viaGroup, contactUsed, enableNtfs_) :. (preferences, userPreferences, createdAt, updatedAt)] = + toContact' :: Int64 -> Connection -> [(ProfileId, ContactName, Text, Text, Maybe ImageData, LocalAlias, Maybe Int64, Bool, Maybe Bool) :. (Maybe Preferences, Preferences, Bool, UTCTime, UTCTime)] -> Either StoreError Contact + toContact' contactId activeConn [(profileId, localDisplayName, displayName, fullName, image, localAlias, viaGroup, contactUsed, enableNtfs_) :. (preferences, userPreferences, confirmPrefPending, createdAt, updatedAt)] = let profile = LocalProfile {profileId, displayName, fullName, image, preferences, localAlias} chatSettings = ChatSettings {enableNtfs = fromMaybe True enableNtfs_} mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito activeConn - in Right Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt} + in Right Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, chatSettings, userPreferences, mergedPreferences, confirmPrefPending, createdAt, updatedAt} toContact' _ _ _ = Left $ SEInternalError "referenced contact not found" getGroupAndMember_ :: Int64 -> Connection -> ExceptT StoreError IO (GroupInfo, GroupMember) getGroupAndMember_ groupMemberId c = ExceptT $ do @@ -2070,7 +2081,7 @@ getContactViaMember db user@User {userId} GroupMember {groupMemberId} = [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.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, + ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.confirm_pref_pending, ct.created_at, ct.updated_at, -- 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.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 @@ -2408,7 +2419,7 @@ getViaGroupContact db user@User {userId} GroupMember {groupMemberId} = db [sql| SELECT - ct.contact_id, ct.contact_profile_id, ct.local_display_name, p.display_name, p.full_name, p.image, p.local_alias, ct.via_group, ct.contact_used, ct.enable_ntfs, p.preferences, ct.user_preferences, ct.created_at, ct.updated_at, + ct.contact_id, ct.contact_profile_id, ct.local_display_name, p.display_name, p.full_name, p.image, p.local_alias, ct.via_group, ct.contact_used, ct.enable_ntfs, p.preferences, ct.user_preferences, ct.confirm_pref_pending, ct.created_at, ct.updated_at, 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.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 FROM contacts ct @@ -2424,13 +2435,13 @@ getViaGroupContact db user@User {userId} GroupMember {groupMemberId} = |] (userId, groupMemberId) where - toContact' :: ((ContactId, ProfileId, ContactName, Text, Text, Maybe ImageData, LocalAlias, Maybe Int64, Bool, Maybe Bool) :. (Maybe Preferences, Preferences, UTCTime, UTCTime)) :. ConnectionRow -> Contact - toContact' (((contactId, profileId, localDisplayName, displayName, fullName, image, localAlias, viaGroup, contactUsed, enableNtfs_) :. (preferences, userPreferences, createdAt, updatedAt)) :. connRow) = + toContact' :: ((ContactId, ProfileId, ContactName, Text, Text, Maybe ImageData, LocalAlias, Maybe Int64, Bool, Maybe Bool) :. (Maybe Preferences, Preferences, Bool, UTCTime, UTCTime)) :. ConnectionRow -> Contact + toContact' (((contactId, profileId, localDisplayName, displayName, fullName, image, localAlias, viaGroup, contactUsed, enableNtfs_) :. (preferences, userPreferences, confirmPrefPending, createdAt, updatedAt)) :. connRow) = let profile = LocalProfile {profileId, displayName, fullName, image, preferences, localAlias} chatSettings = ChatSettings {enableNtfs = fromMaybe True enableNtfs_} activeConn = toConnection connRow mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito activeConn - in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt} + in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, chatSettings, userPreferences, mergedPreferences, confirmPrefPending, createdAt, updatedAt} createSndDirectFileTransfer :: DB.Connection -> UserId -> Contact -> FilePath -> FileInvitation -> Maybe ConnId -> Integer -> IO FileTransferMeta createSndDirectFileTransfer db userId Contact {contactId} filePath FileInvitation {fileName, fileSize, fileInline} acId_ chunkSize = do @@ -3301,7 +3312,7 @@ getDirectChatPreviews_ db user@User {userId} = do [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.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, + ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.confirm_pref_pending, ct.created_at, ct.updated_at, -- 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.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, @@ -3623,7 +3634,7 @@ getContact db user@User {userId} contactId = [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.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, + ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.local_alias, ct.contact_used, ct.enable_ntfs, cp.preferences, ct.user_preferences, ct.confirm_pref_pending, ct.created_at, ct.updated_at, -- 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.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 diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index d77e3a0a2d..63a5a2a69e 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -108,6 +108,7 @@ data Contact = Contact chatSettings :: ChatSettings, userPreferences :: Preferences, mergedPreferences :: ContactUserPreferences, + confirmPrefPending :: Bool, createdAt :: UTCTime, updatedAt :: UTCTime } diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index 72f5bdb56a..e48e185ebe 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -3587,9 +3587,8 @@ testEnableTimedMessagesContact = alice <## "you updated preferences for bob:" alice <## "Disappearing messages: off (you allow: yes, after 1 sec, contact allows: no)" bob <## "alice updated preferences for you:" - bob <## "Disappearing messages: off (you allow: default (no), contact allows: yes, after 1 sec)" - -- TODO bob ##> "/set disappear @alice yes" - bob ##> "/_set prefs @2 {\"timedMessages\": {\"allow\": \"yes\", \"ttl\": 1}}" + bob <## "Disappearing messages: off (you allow: no, contact allows: yes, after 1 sec)" + bob ##> "/set disappear @alice yes" bob <## "you updated preferences for alice:" bob <## "Disappearing messages: enabled (you allow: yes, after 1 sec, contact allows: yes, after 1 sec)" alice <## "bob updated preferences for you:"