mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-14 04:59:56 +00:00
chat: persist peer-claimed simplexName from incoming profiles
Write paths: - updateContactProfile_' / updateGroupProfile_ now set the new contact_profiles.simplex_name / group_profiles.simplex_name columns from Profile.simplexName / GroupProfile.simplexName respectively. - createContact_ INSERT writes Profile.simplexName to the new contact_profiles column (separate from the existing simplexName arg, which still writes contacts.simplex_name — the user's locally-known label). Read paths (closing Task 2's deferred sites): - toContact splits simplex_name reads: Contact.simplexName from contacts.simplex_name (existing); LocalProfile.simplexName from contact_profiles.simplex_name (new column). - toGroupInfo similarly splits: GroupInfo.simplexName from groups.simplex_name; groupProfile.simplexName from group_profiles.simplex_name. - ProfileRow / rowToLocalProfile, toContactRequest, getUserContactProfiles, toGroupProfile, getProfileById, groupMemberQuery, getGroupAndMember_, saveRcvChatItem-related quotes — all extended to read p.simplex_name and decode it into LocalProfile.simplexName / GroupProfile.simplexName. Conflict handling (Decision B): - clearConflictingContactProfileSimplexName_ / *Group* helpers do an atomic UPDATE-with-RETURNING that NULLs simplex_name on any other row in the same user that would collide on the partial UNIQUE index, returning the displaced row's display_name. - updateContactProfileWithConflict / updateGroupProfileWithConflict bundle clear+update in one transaction. - processContactProfileUpdate / xGrpInfo invoke the *WithConflict variants and emit CEvtSimplexNameConflict when a displacement happened (with the claiming and displaced display names). Adds ChatEvent CEvtSimplexNameConflict and SimplexNameConflictEntity (SNCEContact / SNCEGroup) with JSON instances and View.hs rendering.
This commit is contained in:
@@ -946,6 +946,15 @@ data ChatEvent
|
|||||||
| CEvtTimedAction {action :: String, durationMilliseconds :: Int64}
|
| CEvtTimedAction {action :: String, durationMilliseconds :: Int64}
|
||||||
| CEvtTerminalEvent TerminalEvent
|
| CEvtTerminalEvent TerminalEvent
|
||||||
| CEvtCustomChatEvent {user_ :: Maybe User, response :: Text}
|
| CEvtCustomChatEvent {user_ :: Maybe User, response :: Text}
|
||||||
|
| -- Emitted when an incoming peer Profile / GroupProfile carrying a
|
||||||
|
-- simplexName collides with another row in the same user's DB that
|
||||||
|
-- already holds that name. The older row's simplex_name is NULLed
|
||||||
|
-- (newer-claim-wins per RSLV); displacedFrom is the old row's local
|
||||||
|
-- display_name, claimedBy is the peer / group whose claim won.
|
||||||
|
CEvtSimplexNameConflict {user :: User, simplexName :: SimplexNameInfo, entity :: SimplexNameConflictEntity, claimedBy :: ContactName, displacedFrom :: ContactName}
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
data SimplexNameConflictEntity = SNCEContact | SNCEGroup
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
data TerminalEvent
|
data TerminalEvent
|
||||||
@@ -1771,6 +1780,8 @@ $(JQ.deriveJSON defaultJSON ''RelayTestFailure)
|
|||||||
|
|
||||||
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CR") ''ChatResponse)
|
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CR") ''ChatResponse)
|
||||||
|
|
||||||
|
$(JQ.deriveJSON (enumJSON $ dropPrefix "SNCE") ''SimplexNameConflictEntity)
|
||||||
|
|
||||||
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CEvt") ''ChatEvent)
|
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CEvt") ''ChatEvent)
|
||||||
|
|
||||||
$(JQ.deriveFromJSON defaultJSON ''ArchiveConfig)
|
$(JQ.deriveFromJSON defaultJSON ''ArchiveConfig)
|
||||||
|
|||||||
@@ -2560,12 +2560,15 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
|||||||
processContactProfileUpdate :: Contact -> Profile -> Bool -> CM Contact
|
processContactProfileUpdate :: Contact -> Profile -> Bool -> CM Contact
|
||||||
processContactProfileUpdate c@Contact {profile = lp} p' createItems
|
processContactProfileUpdate c@Contact {profile = lp} p' createItems
|
||||||
| p /= p' = do
|
| p /= p' = do
|
||||||
c' <- withStore $ \db ->
|
(c', displaced_) <- withStore $ \db ->
|
||||||
if userTTL == rcvTTL
|
if userTTL == rcvTTL
|
||||||
then updateContactProfile db user c p'
|
then updateContactProfileWithConflict db user c p'
|
||||||
else do
|
else do
|
||||||
c' <- liftIO $ updateContactUserPreferences db user c ctUserPrefs'
|
c' <- liftIO $ updateContactUserPreferences db user c ctUserPrefs'
|
||||||
updateContactProfile db user c' p'
|
updateContactProfileWithConflict db user c' p'
|
||||||
|
forM_ ((,) <$> p'SimplexName <*> displaced_) $ \(ni, displaced) ->
|
||||||
|
let Contact {localDisplayName = newLDN} = c'
|
||||||
|
in toView $ CEvtSimplexNameConflict user ni SNCEContact newLDN displaced
|
||||||
when (directOrUsed c' && createItems) $ do
|
when (directOrUsed c' && createItems) $ do
|
||||||
createProfileUpdatedItem c'
|
createProfileUpdatedItem c'
|
||||||
lift $ createRcvFeatureItems user c c'
|
lift $ createRcvFeatureItems user c c'
|
||||||
@@ -2577,7 +2580,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
|||||||
p = fromLocalProfile lp
|
p = fromLocalProfile lp
|
||||||
Contact {userPreferences = ctUserPrefs@Preferences {timedMessages = ctUserTMPref}} = c
|
Contact {userPreferences = ctUserPrefs@Preferences {timedMessages = ctUserTMPref}} = c
|
||||||
userTTL = prefParam $ getPreference SCFTimedMessages ctUserPrefs
|
userTTL = prefParam $ getPreference SCFTimedMessages ctUserPrefs
|
||||||
Profile {preferences = rcvPrefs_} = p'
|
Profile {preferences = rcvPrefs_, simplexName = p'SimplexName} = p'
|
||||||
rcvTTL = prefParam $ getPreference SCFTimedMessages rcvPrefs_
|
rcvTTL = prefParam $ getPreference SCFTimedMessages rcvPrefs_
|
||||||
ctUserPrefs' =
|
ctUserPrefs' =
|
||||||
let userDefault = getPreference SCFTimedMessages (fullPreferences user)
|
let userDefault = getPreference SCFTimedMessages (fullPreferences user)
|
||||||
@@ -3284,7 +3287,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
|||||||
toView $ CEvtGroupDeleted user gInfo'' {membership = membership {memberStatus = GSMemGroupDeleted}} m' msgSigned
|
toView $ CEvtGroupDeleted user gInfo'' {membership = membership {memberStatus = GSMemGroupDeleted}} m' msgSigned
|
||||||
|
|
||||||
xGrpInfo :: GroupInfo -> GroupMember -> GroupProfile -> RcvMessage -> UTCTime -> CM (Maybe DeliveryJobScope)
|
xGrpInfo :: GroupInfo -> GroupMember -> GroupProfile -> RcvMessage -> UTCTime -> CM (Maybe DeliveryJobScope)
|
||||||
xGrpInfo g@GroupInfo {groupProfile = p@GroupProfile {publicGroup = pg}, businessChat} m@GroupMember {memberRole} p'@GroupProfile {publicGroup = pg'} msg@RcvMessage {msgSigned} brokerTs
|
xGrpInfo g@GroupInfo {groupProfile = p@GroupProfile {publicGroup = pg}, businessChat} m@GroupMember {memberRole} p'@GroupProfile {publicGroup = pg', simplexName = p'GroupSimplexName} msg@RcvMessage {msgSigned} brokerTs
|
||||||
| memberRole < GROwner = messageError "x.grp.info with insufficient member permissions" $> Nothing
|
| memberRole < GROwner = messageError "x.grp.info with insufficient member permissions" $> Nothing
|
||||||
| let pgId = fmap (\PublicGroupProfile {publicGroupId} -> publicGroupId),
|
| let pgId = fmap (\PublicGroupProfile {publicGroupId} -> publicGroupId),
|
||||||
useRelays' g && (isNothing pg' || pgId pg' /= pgId pg) = messageError "x.grp.info: publicGroupId mismatch for channel" $> Nothing
|
useRelays' g && (isNothing pg' || pgId pg' /= pgId pg) = messageError "x.grp.info: publicGroupId mismatch for channel" $> Nothing
|
||||||
@@ -3292,7 +3295,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
|||||||
| otherwise = do
|
| otherwise = do
|
||||||
case businessChat of
|
case businessChat of
|
||||||
Nothing -> unless (p == p') $ do
|
Nothing -> unless (p == p') $ do
|
||||||
g' <- withStore $ \db -> updateGroupProfile db user g p'
|
(g', displaced_) <- withStore $ \db -> updateGroupProfileWithConflict db user g p'
|
||||||
|
forM_ ((,) <$> p'GroupSimplexName <*> displaced_) $ \(ni, displaced) ->
|
||||||
|
let GroupInfo {localDisplayName = newLDN} = g'
|
||||||
|
in toView $ CEvtSimplexNameConflict user ni SNCEGroup newLDN displaced
|
||||||
(g'', m', scopeInfo) <- mkGroupChatScope g' m
|
(g'', m', scopeInfo) <- mkGroupChatScope g' m
|
||||||
toView $ CEvtGroupUpdated user g g'' (Just m') msgSigned
|
toView $ CEvtGroupUpdated user g g'' (Just m') msgSigned
|
||||||
let cd = CDGroupRcv g'' scopeInfo m'
|
let cd = CDGroupRcv g'' scopeInfo m'
|
||||||
|
|||||||
@@ -113,16 +113,16 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
|||||||
c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
|
c.contact_profile_id, c.local_display_name, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, 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.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id,
|
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id,
|
||||||
c.contact_group_member_id, c.contact_grp_inv_sent, c.grp_direct_inv_link, c.grp_direct_inv_from_group_id, c.grp_direct_inv_from_group_member_id, c.grp_direct_inv_from_member_conn_id, c.grp_direct_inv_started_connection,
|
c.contact_group_member_id, c.contact_grp_inv_sent, c.grp_direct_inv_link, c.grp_direct_inv_from_group_id, c.grp_direct_inv_from_group_member_id, c.grp_direct_inv_from_member_conn_id, c.grp_direct_inv_started_connection,
|
||||||
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl, c.simplex_name
|
c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl, c.simplex_name, p.simplex_name
|
||||||
FROM contacts c
|
FROM contacts c
|
||||||
JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id
|
JOIN contact_profiles p ON c.contact_profile_id = p.contact_profile_id
|
||||||
WHERE c.user_id = ? AND c.contact_id = ? AND c.contact_status = ? AND c.deleted = 0
|
WHERE c.user_id = ? AND c.contact_id = ? AND c.contact_status = ? AND c.deleted = 0
|
||||||
|]
|
|]
|
||||||
(userId, contactId, CSActive)
|
(userId, contactId, CSActive)
|
||||||
toContact' :: Int64 -> Connection -> [ChatTagId] -> ContactRow' -> Contact
|
toContact' :: Int64 -> Connection -> [ChatTagId] -> ContactRow' -> Contact
|
||||||
toContact' contactId conn chatTags ((profileId, localDisplayName, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL, simplexNameRaw)) =
|
toContact' contactId conn chatTags ((profileId, localDisplayName, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL, ctSimplexNameRaw, cpSimplexNameRaw)) =
|
||||||
let simplexName = decodeSimplexName simplexNameRaw
|
let simplexName = decodeSimplexName ctSimplexNameRaw
|
||||||
profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName, peerType, preferences, localAlias}
|
profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName = decodeSimplexName cpSimplexNameRaw, peerType, preferences, localAlias}
|
||||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito conn
|
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito conn
|
||||||
activeConn = Just conn
|
activeConn = Just conn
|
||||||
@@ -149,17 +149,17 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
|||||||
g.use_relays, g.relay_own_status,
|
g.use_relays, g.relay_own_status,
|
||||||
g.ui_themes, g.summary_current_members_count, g.public_member_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
g.ui_themes, g.summary_current_members_count, g.public_member_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||||
g.root_priv_key, g.root_pub_key, g.member_priv_key,
|
g.root_priv_key, g.root_pub_key, g.member_priv_key,
|
||||||
g.simplex_name,
|
g.simplex_name, gp.simplex_name,
|
||||||
-- GroupInfo {membership}
|
-- GroupInfo {membership}
|
||||||
mu.group_member_id, mu.group_id, mu.index_in_group, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
mu.group_member_id, mu.group_id, mu.index_in_group, 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,
|
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,
|
||||||
-- GroupInfo {membership = GroupMember {memberProfile}}
|
-- GroupInfo {membership = GroupMember {memberProfile}}
|
||||||
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, pu.simplex_name,
|
||||||
mu.created_at, mu.updated_at,
|
mu.created_at, mu.updated_at,
|
||||||
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link,
|
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link,
|
||||||
-- from GroupMember
|
-- from GroupMember
|
||||||
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
|
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.simplex_name,
|
||||||
m.created_at, m.updated_at,
|
m.created_at, m.updated_at,
|
||||||
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link
|
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link
|
||||||
FROM group_members m
|
FROM group_members m
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ createOrUpdateContactRequest
|
|||||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, 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.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
||||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.simplex_name,
|
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.simplex_name, cp.simplex_name,
|
||||||
-- Connection
|
-- 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.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
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.xcontact_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.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,
|
c.contact_id, c.group_member_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,
|
||||||
@@ -143,7 +143,7 @@ createOrUpdateContactRequest
|
|||||||
SELECT
|
SELECT
|
||||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, cr.xcontact_id,
|
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.simplex_name, cr.xcontact_id,
|
||||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||||
cr.created_at, cr.updated_at,
|
cr.created_at, cr.updated_at,
|
||||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ module Simplex.Chat.Store.Direct
|
|||||||
( updateContactLDN_,
|
( updateContactLDN_,
|
||||||
updateContactProfile_,
|
updateContactProfile_,
|
||||||
updateContactProfile_',
|
updateContactProfile_',
|
||||||
|
clearConflictingContactProfileSimplexName_,
|
||||||
updateMemberContactProfileReset_',
|
updateMemberContactProfileReset_',
|
||||||
updateMemberContactProfileReset_,
|
updateMemberContactProfileReset_,
|
||||||
updateMemberContactProfile_,
|
updateMemberContactProfile_,
|
||||||
@@ -52,6 +53,7 @@ module Simplex.Chat.Store.Direct
|
|||||||
getContactIdByName,
|
getContactIdByName,
|
||||||
getContactIdBySimplexName,
|
getContactIdBySimplexName,
|
||||||
updateContactProfile,
|
updateContactProfile,
|
||||||
|
updateContactProfileWithConflict,
|
||||||
updateContactUserPreferences,
|
updateContactUserPreferences,
|
||||||
updateContactAlias,
|
updateContactAlias,
|
||||||
updateContactConnectionAlias,
|
updateContactConnectionAlias,
|
||||||
@@ -320,7 +322,7 @@ getContactByConnReqHash db vr user@User {userId} cReqHash1 cReqHash2 = do
|
|||||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, 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.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
||||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.simplex_name,
|
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.simplex_name, cp.simplex_name,
|
||||||
-- Connection
|
-- 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.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
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.xcontact_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.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,
|
c.contact_id, c.group_member_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,
|
||||||
@@ -557,19 +559,29 @@ deleteUnusedProfile_ db userId profileId =
|
|||||||
)
|
)
|
||||||
|
|
||||||
updateContactProfile :: DB.Connection -> User -> Contact -> Profile -> ExceptT StoreError IO Contact
|
updateContactProfile :: DB.Connection -> User -> Contact -> Profile -> ExceptT StoreError IO Contact
|
||||||
updateContactProfile db user@User {userId} c p'
|
updateContactProfile db user c p' = fst <$> updateContactProfileWithConflict db user c p'
|
||||||
|
|
||||||
|
-- | Like updateContactProfile but additionally clears the simplex_name on any
|
||||||
|
-- other contact_profiles row in the same user that already holds the same
|
||||||
|
-- (user_id, simplex_name) — returning that row's display_name so the caller
|
||||||
|
-- can emit CEvtSimplexNameConflict. Used by the incoming-XInfo path; local
|
||||||
|
-- updates that don't expect conflicts can continue to use updateContactProfile.
|
||||||
|
updateContactProfileWithConflict :: DB.Connection -> User -> Contact -> Profile -> ExceptT StoreError IO (Contact, Maybe ContactName)
|
||||||
|
updateContactProfileWithConflict db user@User {userId} c p'
|
||||||
| displayName == newName = do
|
| displayName == newName = do
|
||||||
|
displaced <- liftIO $ clearConflictingContactProfileSimplexName_ db userId (Just profileId) profileSimplexName
|
||||||
liftIO $ updateContactProfile_ db userId profileId p'
|
liftIO $ updateContactProfile_ db userId profileId p'
|
||||||
pure c {profile, mergedPreferences}
|
pure (c {profile, mergedPreferences}, displaced)
|
||||||
| otherwise =
|
| otherwise =
|
||||||
ExceptT . withLocalDisplayName db userId newName $ \ldn -> do
|
ExceptT . withLocalDisplayName db userId newName $ \ldn -> do
|
||||||
currentTs <- getCurrentTime
|
currentTs <- getCurrentTime
|
||||||
|
displaced <- clearConflictingContactProfileSimplexName_ db userId (Just profileId) profileSimplexName
|
||||||
updateContactProfile_' db userId profileId p' currentTs
|
updateContactProfile_' db userId profileId p' currentTs
|
||||||
updateContactLDN_ db user contactId localDisplayName ldn currentTs
|
updateContactLDN_ db user contactId localDisplayName ldn currentTs
|
||||||
pure $ Right c {localDisplayName = ldn, profile, mergedPreferences}
|
pure $ Right (c {localDisplayName = ldn, profile, mergedPreferences}, displaced)
|
||||||
where
|
where
|
||||||
Contact {contactId, localDisplayName, profile = LocalProfile {profileId, displayName, localAlias}, userPreferences} = c
|
Contact {contactId, localDisplayName, profile = LocalProfile {profileId, displayName, localAlias}, userPreferences} = c
|
||||||
Profile {displayName = newName, preferences} = p'
|
Profile {displayName = newName, simplexName = profileSimplexName, preferences} = p'
|
||||||
profile = toLocalProfile profileId p' localAlias
|
profile = toLocalProfile profileId p' localAlias
|
||||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ contactConnIncognito c
|
mergedPreferences = contactUserPreferences user userPreferences preferences $ contactConnIncognito c
|
||||||
|
|
||||||
@@ -704,15 +716,47 @@ updateContactProfile_ db userId profileId profile = do
|
|||||||
updateContactProfile_' db userId profileId profile currentTs
|
updateContactProfile_' db userId profileId profile currentTs
|
||||||
|
|
||||||
updateContactProfile_' :: DB.Connection -> UserId -> ProfileId -> Profile -> UTCTime -> IO ()
|
updateContactProfile_' :: DB.Connection -> UserId -> ProfileId -> Profile -> UTCTime -> IO ()
|
||||||
updateContactProfile_' db userId profileId Profile {displayName, fullName, shortDescr, image, contactLink, preferences, peerType} updatedAt = do
|
updateContactProfile_' db userId profileId Profile {displayName, fullName, shortDescr, image, contactLink, simplexName, preferences, peerType} updatedAt = do
|
||||||
DB.execute
|
DB.execute
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
UPDATE contact_profiles
|
UPDATE contact_profiles
|
||||||
SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = ?, preferences = ?, chat_peer_type = ?, updated_at = ?
|
SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = ?, simplex_name = ?, preferences = ?, chat_peer_type = ?, updated_at = ?
|
||||||
WHERE user_id = ? AND contact_profile_id = ?
|
WHERE user_id = ? AND contact_profile_id = ?
|
||||||
|]
|
|]
|
||||||
(displayName, fullName, shortDescr, image, contactLink, preferences, peerType, updatedAt, userId, profileId)
|
((displayName, fullName, shortDescr, image, contactLink, simplexName, preferences, peerType, updatedAt) :. (userId, profileId))
|
||||||
|
|
||||||
|
-- | Clears simplex_name on any other contact_profiles row that holds the same
|
||||||
|
-- (user_id, simplex_name) so a subsequent UPDATE/INSERT setting that value
|
||||||
|
-- won't trip the partial UNIQUE index. Pass the profileId being updated to
|
||||||
|
-- exclude self; pass Nothing for the pre-INSERT case. Returns the displaced
|
||||||
|
-- row's display_name when a conflict was resolved, for the caller to surface
|
||||||
|
-- as CEvtSimplexNameConflict. Newer-claim-wins matches RSLV semantics: the
|
||||||
|
-- latest broadcast is the canonical assignment.
|
||||||
|
clearConflictingContactProfileSimplexName_ :: DB.Connection -> UserId -> Maybe ProfileId -> Maybe SimplexNameInfo -> IO (Maybe ContactName)
|
||||||
|
clearConflictingContactProfileSimplexName_ _ _ _ Nothing = pure Nothing
|
||||||
|
clearConflictingContactProfileSimplexName_ db userId Nothing (Just simplexName) =
|
||||||
|
maybeFirstRow fromOnly $
|
||||||
|
DB.query
|
||||||
|
db
|
||||||
|
[sql|
|
||||||
|
UPDATE contact_profiles
|
||||||
|
SET simplex_name = NULL
|
||||||
|
WHERE user_id = ? AND simplex_name = ?
|
||||||
|
RETURNING display_name
|
||||||
|
|]
|
||||||
|
(userId, simplexName)
|
||||||
|
clearConflictingContactProfileSimplexName_ db userId (Just profileId) (Just simplexName) =
|
||||||
|
maybeFirstRow fromOnly $
|
||||||
|
DB.query
|
||||||
|
db
|
||||||
|
[sql|
|
||||||
|
UPDATE contact_profiles
|
||||||
|
SET simplex_name = NULL
|
||||||
|
WHERE user_id = ? AND simplex_name = ? AND contact_profile_id <> ?
|
||||||
|
RETURNING display_name
|
||||||
|
|]
|
||||||
|
(userId, simplexName, profileId)
|
||||||
|
|
||||||
-- update only member profile fields (when member doesn't have associated contact - we can reset contactLink and prefs)
|
-- update only member profile fields (when member doesn't have associated contact - we can reset contactLink and prefs)
|
||||||
updateMemberContactProfileReset_ :: DB.Connection -> UserId -> ProfileId -> Profile -> IO ()
|
updateMemberContactProfileReset_ :: DB.Connection -> UserId -> ProfileId -> Profile -> IO ()
|
||||||
@@ -814,7 +858,7 @@ contactRequestQuery =
|
|||||||
SELECT
|
SELECT
|
||||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
|
||||||
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
|
||||||
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, cr.xcontact_id,
|
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.simplex_name, cr.xcontact_id,
|
||||||
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
|
||||||
cr.created_at, cr.updated_at,
|
cr.created_at, cr.updated_at,
|
||||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||||
@@ -936,7 +980,7 @@ getContact_ db vr user@User {userId} contactId deleted = do
|
|||||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
ct.contact_id, ct.contact_profile_id, ct.local_display_name, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, 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.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
|
||||||
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.grp_direct_inv_link, ct.grp_direct_inv_from_group_id, ct.grp_direct_inv_from_group_member_id, ct.grp_direct_inv_from_member_conn_id, ct.grp_direct_inv_started_connection,
|
||||||
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.simplex_name,
|
ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl, ct.simplex_name, cp.simplex_name,
|
||||||
-- Connection
|
-- 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.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
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.xcontact_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.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,
|
c.contact_id, c.group_member_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,
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ module Simplex.Chat.Store.Groups
|
|||||||
getGroupViaShortLinkToConnect,
|
getGroupViaShortLinkToConnect,
|
||||||
getGroupInfoByGroupLinkHash,
|
getGroupInfoByGroupLinkHash,
|
||||||
updateGroupProfile,
|
updateGroupProfile,
|
||||||
|
updateGroupProfileWithConflict,
|
||||||
|
clearConflictingGroupProfileSimplexName_,
|
||||||
updateGroupPreferences,
|
updateGroupPreferences,
|
||||||
updateGroupProfileFromMember,
|
updateGroupProfileFromMember,
|
||||||
getGroupIdByName,
|
getGroupIdByName,
|
||||||
@@ -230,11 +232,11 @@ import Database.SQLite.Simple (Only (..), Query, (:.) (..))
|
|||||||
import Database.SQLite.Simple.QQ (sql)
|
import Database.SQLite.Simple.QQ (sql)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
type MaybeGroupMemberRow = (Maybe GroupMemberId, Maybe GroupId, 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 Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact)
|
type MaybeGroupMemberRow = (Maybe GroupMemberId, Maybe GroupId, 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 Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences, Maybe Text) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact)
|
||||||
|
|
||||||
toMaybeGroupMember :: Int64 -> MaybeGroupMemberRow -> Maybe GroupMember
|
toMaybeGroupMember :: Int64 -> MaybeGroupMemberRow -> Maybe GroupMember
|
||||||
toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just indexInGroup, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. (Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink)) =
|
toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just indexInGroup, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. (Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences, profileSimplexNameRaw) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink)) =
|
||||||
Just $ toGroupMember userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink))
|
Just $ toGroupMember userContactId ((groupMemberId, groupId, indexInGroup, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences, profileSimplexNameRaw) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs, memberPubKey, relayLink))
|
||||||
toMaybeGroupMember _ _ = Nothing
|
toMaybeGroupMember _ _ = Nothing
|
||||||
|
|
||||||
createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink
|
createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink
|
||||||
@@ -2367,17 +2369,26 @@ createMemberConnection_ db userId groupMemberId agentConnId chatV peerChatVRange
|
|||||||
createConnection_ db userId ConnMember (Just groupMemberId) agentConnId ConnNew chatV peerChatVRange viaContact Nothing Nothing connLevel currentTs subMode PQSupportOff Nothing
|
createConnection_ db userId ConnMember (Just groupMemberId) agentConnId ConnNew chatV peerChatVRange viaContact Nothing Nothing connLevel currentTs subMode PQSupportOff Nothing
|
||||||
|
|
||||||
updateGroupProfile :: DB.Connection -> User -> GroupInfo -> GroupProfile -> ExceptT StoreError IO GroupInfo
|
updateGroupProfile :: DB.Connection -> User -> GroupInfo -> GroupProfile -> ExceptT StoreError IO GroupInfo
|
||||||
updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, groupProfile = GroupProfile {displayName}} p'@GroupProfile {displayName = newName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission}
|
updateGroupProfile db user g p' = fst <$> updateGroupProfileWithConflict db user g p'
|
||||||
|
|
||||||
|
-- | Like updateGroupProfile but additionally clears the simplex_name on any
|
||||||
|
-- other group_profiles row (for the same user) that already holds the same
|
||||||
|
-- (user_id, simplex_name) — returning that row's display_name so the caller
|
||||||
|
-- can emit CEvtSimplexNameConflict. Used by the XGrpInfo path.
|
||||||
|
updateGroupProfileWithConflict :: DB.Connection -> User -> GroupInfo -> GroupProfile -> ExceptT StoreError IO (GroupInfo, Maybe GroupName)
|
||||||
|
updateGroupProfileWithConflict db user@User {userId} g@GroupInfo {groupId, localDisplayName, groupProfile = GroupProfile {displayName}} p'@GroupProfile {displayName = newName, fullName, shortDescr, description, image, publicGroup, simplexName, groupPreferences, memberAdmission}
|
||||||
| displayName == newName = liftIO $ do
|
| displayName == newName = liftIO $ do
|
||||||
currentTs <- getCurrentTime
|
currentTs <- getCurrentTime
|
||||||
|
displaced <- clearConflictingGroupProfileSimplexName_ db userId (Just groupId) simplexName
|
||||||
updateGroupProfile_ currentTs
|
updateGroupProfile_ currentTs
|
||||||
pure (g :: GroupInfo) {groupProfile = p', fullGroupPreferences}
|
pure ((g :: GroupInfo) {groupProfile = p', fullGroupPreferences}, displaced)
|
||||||
| otherwise =
|
| otherwise =
|
||||||
ExceptT . withLocalDisplayName db userId newName $ \ldn -> do
|
ExceptT . withLocalDisplayName db userId newName $ \ldn -> do
|
||||||
currentTs <- getCurrentTime
|
currentTs <- getCurrentTime
|
||||||
|
displaced <- clearConflictingGroupProfileSimplexName_ db userId (Just groupId) simplexName
|
||||||
updateGroupProfile_ currentTs
|
updateGroupProfile_ currentTs
|
||||||
updateGroup_ ldn currentTs
|
updateGroup_ ldn currentTs
|
||||||
pure $ Right (g :: GroupInfo) {localDisplayName = ldn, groupProfile = p', fullGroupPreferences}
|
pure $ Right ((g :: GroupInfo) {localDisplayName = ldn, groupProfile = p', fullGroupPreferences}, displaced)
|
||||||
where
|
where
|
||||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||||
(groupType_, groupLink_) = case publicGroup of
|
(groupType_, groupLink_) = case publicGroup of
|
||||||
@@ -2391,6 +2402,7 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName,
|
|||||||
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?,
|
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?,
|
||||||
group_type = ?, group_link = ?,
|
group_type = ?, group_link = ?,
|
||||||
group_web_page = ?, group_domain = ?, domain_web_page = ?, allow_embedding = ?,
|
group_web_page = ?, group_domain = ?, domain_web_page = ?, allow_embedding = ?,
|
||||||
|
simplex_name = ?,
|
||||||
preferences = ?, member_admission = ?, updated_at = ?
|
preferences = ?, member_admission = ?, updated_at = ?
|
||||||
WHERE group_profile_id IN (
|
WHERE group_profile_id IN (
|
||||||
SELECT group_profile_id
|
SELECT group_profile_id
|
||||||
@@ -2398,7 +2410,7 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName,
|
|||||||
WHERE user_id = ? AND group_id = ?
|
WHERE user_id = ? AND group_id = ?
|
||||||
)
|
)
|
||||||
|]
|
|]
|
||||||
((newName, fullName, shortDescr, description, image, groupType_, groupLink_) :. publicGroupAccessRow publicGroup :. (groupPreferences, memberAdmission, currentTs, userId, groupId))
|
((newName, fullName, shortDescr, description, image, groupType_, groupLink_) :. publicGroupAccessRow publicGroup :. Only simplexName :. (groupPreferences, memberAdmission, currentTs, userId, groupId))
|
||||||
updateGroup_ ldn currentTs = do
|
updateGroup_ ldn currentTs = do
|
||||||
DB.execute
|
DB.execute
|
||||||
db
|
db
|
||||||
@@ -2406,6 +2418,38 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName,
|
|||||||
(ldn, currentTs, userId, groupId)
|
(ldn, currentTs, userId, groupId)
|
||||||
safeDeleteLDN db user localDisplayName
|
safeDeleteLDN db user localDisplayName
|
||||||
|
|
||||||
|
-- | Mirror of clearConflictingContactProfileSimplexName_ for group_profiles.
|
||||||
|
-- Pass the groupId being updated to exclude its underlying group_profile_id
|
||||||
|
-- from the clear; pass Nothing for the pre-INSERT case. See that helper.
|
||||||
|
clearConflictingGroupProfileSimplexName_ :: DB.Connection -> UserId -> Maybe GroupId -> Maybe SimplexNameInfo -> IO (Maybe GroupName)
|
||||||
|
clearConflictingGroupProfileSimplexName_ _ _ _ Nothing = pure Nothing
|
||||||
|
clearConflictingGroupProfileSimplexName_ db userId Nothing (Just simplexName) =
|
||||||
|
maybeFirstRow fromOnly $
|
||||||
|
DB.query
|
||||||
|
db
|
||||||
|
[sql|
|
||||||
|
UPDATE group_profiles
|
||||||
|
SET simplex_name = NULL
|
||||||
|
WHERE user_id = ? AND simplex_name = ?
|
||||||
|
RETURNING display_name
|
||||||
|
|]
|
||||||
|
(userId, simplexName)
|
||||||
|
clearConflictingGroupProfileSimplexName_ db userId (Just groupId) (Just simplexName) =
|
||||||
|
maybeFirstRow fromOnly $
|
||||||
|
DB.query
|
||||||
|
db
|
||||||
|
[sql|
|
||||||
|
UPDATE group_profiles
|
||||||
|
SET simplex_name = NULL
|
||||||
|
WHERE user_id = ?
|
||||||
|
AND simplex_name = ?
|
||||||
|
AND group_profile_id NOT IN (
|
||||||
|
SELECT group_profile_id FROM groups WHERE user_id = ? AND group_id = ?
|
||||||
|
)
|
||||||
|
RETURNING display_name
|
||||||
|
|]
|
||||||
|
(userId, simplexName, userId, groupId)
|
||||||
|
|
||||||
updateGroupPreferences :: DB.Connection -> User -> GroupInfo -> GroupPreferences -> IO GroupInfo
|
updateGroupPreferences :: DB.Connection -> User -> GroupInfo -> GroupPreferences -> IO GroupInfo
|
||||||
updateGroupPreferences db User {userId} g@GroupInfo {groupId, groupProfile = p} ps = do
|
updateGroupPreferences db User {userId} g@GroupInfo {groupId, groupProfile = p} ps = do
|
||||||
currentTs <- getCurrentTime
|
currentTs <- getCurrentTime
|
||||||
@@ -2439,15 +2483,15 @@ updateGroupProfileFromMember db user g@GroupInfo {groupId} Profile {displayName
|
|||||||
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image,
|
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image,
|
||||||
gp.group_type, gp.group_link, gp.public_group_id,
|
gp.group_type, gp.group_link, gp.public_group_id,
|
||||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding,
|
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding,
|
||||||
gp.preferences, gp.member_admission
|
gp.simplex_name, gp.preferences, gp.member_admission
|
||||||
FROM group_profiles gp
|
FROM group_profiles gp
|
||||||
JOIN groups g ON gp.group_profile_id = g.group_profile_id
|
JOIN groups g ON gp.group_profile_id = g.group_profile_id
|
||||||
WHERE g.group_id = ?
|
WHERE g.group_id = ?
|
||||||
|]
|
|]
|
||||||
(Only groupId)
|
(Only groupId)
|
||||||
toGroupProfile ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (groupPreferences, memberAdmission)) =
|
toGroupProfile ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (simplexNameRaw, groupPreferences, memberAdmission)) =
|
||||||
let publicGroupAccess = toPublicGroupAccess accessRow
|
let publicGroupAccess = toPublicGroupAccess accessRow
|
||||||
in GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ publicGroupAccess, simplexName = Nothing, groupPreferences, memberAdmission}
|
in GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ publicGroupAccess, simplexName = decodeSimplexName simplexNameRaw, groupPreferences, memberAdmission}
|
||||||
|
|
||||||
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
|
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
|
||||||
getGroupInfoByUserContactLinkConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2) = do
|
getGroupInfoByUserContactLinkConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2) = do
|
||||||
|
|||||||
@@ -715,7 +715,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
|||||||
-- GroupMember
|
-- GroupMember
|
||||||
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
|
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
|
||||||
m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
||||||
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
|
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.simplex_name,
|
||||||
m.created_at, m.updated_at,
|
m.created_at, m.updated_at,
|
||||||
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link
|
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link
|
||||||
FROM group_members m
|
FROM group_members m
|
||||||
@@ -3066,7 +3066,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
|||||||
-- GroupMember
|
-- GroupMember
|
||||||
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
|
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
|
||||||
m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
|
||||||
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
|
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.simplex_name,
|
||||||
m.created_at, m.updated_at,
|
m.created_at, m.updated_at,
|
||||||
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
|
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
|
||||||
-- quoted ChatItem
|
-- quoted ChatItem
|
||||||
@@ -3074,13 +3074,13 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
|||||||
-- quoted GroupMember
|
-- quoted GroupMember
|
||||||
rm.group_member_id, rm.group_id, rm.index_in_group, rm.member_id, rm.peer_chat_min_version, rm.peer_chat_max_version, rm.member_role, rm.member_category,
|
rm.group_member_id, rm.group_id, rm.index_in_group, rm.member_id, rm.peer_chat_min_version, rm.peer_chat_max_version, rm.member_role, rm.member_category,
|
||||||
rm.member_status, rm.show_messages, rm.member_restriction, rm.invited_by, rm.invited_by_group_member_id, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
|
rm.member_status, rm.show_messages, rm.member_restriction, rm.invited_by, rm.invited_by_group_member_id, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
|
||||||
rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences,
|
rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences, rp.simplex_name,
|
||||||
rm.created_at, rm.updated_at,
|
rm.created_at, rm.updated_at,
|
||||||
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link,
|
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, rm.member_pub_key, rm.relay_link,
|
||||||
-- deleted by GroupMember
|
-- deleted by GroupMember
|
||||||
dbm.group_member_id, dbm.group_id, dbm.index_in_group, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
|
dbm.group_member_id, dbm.group_id, dbm.index_in_group, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
|
||||||
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
|
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
|
||||||
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences,
|
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences, dbp.simplex_name,
|
||||||
dbm.created_at, dbm.updated_at,
|
dbm.created_at, dbm.updated_at,
|
||||||
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link
|
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, dbm.member_pub_key, dbm.relay_link
|
||||||
FROM chat_items i
|
FROM chat_items i
|
||||||
|
|||||||
@@ -367,14 +367,14 @@ getUserContactProfiles db User {userId} =
|
|||||||
<$> DB.query
|
<$> DB.query
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT display_name, full_name, short_descr, image, contact_link, chat_peer_type, preferences
|
SELECT display_name, full_name, short_descr, image, contact_link, chat_peer_type, simplex_name, preferences
|
||||||
FROM contact_profiles
|
FROM contact_profiles
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
|]
|
|]
|
||||||
(Only userId)
|
(Only userId)
|
||||||
where
|
where
|
||||||
toContactProfile :: (ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe Preferences) -> Profile
|
toContactProfile :: (ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe Text, Maybe Preferences) -> Profile
|
||||||
toContactProfile (displayName, fullName, shortDescr, image, contactLink, peerType, preferences) = Profile {displayName, fullName, shortDescr, image, contactLink, simplexName = Nothing, peerType, preferences}
|
toContactProfile (displayName, fullName, shortDescr, image, contactLink, peerType, simplexNameRaw, preferences) = Profile {displayName, fullName, shortDescr, image, contactLink, simplexName = decodeSimplexName simplexNameRaw, peerType, preferences}
|
||||||
|
|
||||||
createUserContactLink :: DB.Connection -> User -> ConnId -> CreatedLinkContact -> SubscriptionMode -> ExceptT StoreError IO ()
|
createUserContactLink :: DB.Connection -> User -> ConnId -> CreatedLinkContact -> SubscriptionMode -> ExceptT StoreError IO ()
|
||||||
createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) subMode =
|
createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) subMode =
|
||||||
|
|||||||
@@ -422,12 +422,12 @@ createContact db user profile = do
|
|||||||
void $ createContact_ db user profile emptyChatPrefs Nothing "" currentTs Nothing
|
void $ createContact_ db user profile emptyChatPrefs Nothing "" currentTs Nothing
|
||||||
|
|
||||||
createContact_ :: DB.Connection -> User -> Profile -> Preferences -> Maybe (ACreatedConnLink, Maybe SharedMsgId) -> LocalAlias -> UTCTime -> Maybe SimplexNameInfo -> ExceptT StoreError IO ContactId
|
createContact_ :: DB.Connection -> User -> Profile -> Preferences -> Maybe (ACreatedConnLink, Maybe SharedMsgId) -> LocalAlias -> UTCTime -> Maybe SimplexNameInfo -> ExceptT StoreError IO ContactId
|
||||||
createContact_ db User {userId} Profile {displayName, fullName, shortDescr, image, contactLink, peerType, preferences} ctUserPreferences prepared localAlias currentTs simplexName =
|
createContact_ db User {userId} Profile {displayName, fullName, shortDescr, image, contactLink, simplexName = profileSimplexName, peerType, preferences} ctUserPreferences prepared localAlias currentTs simplexName =
|
||||||
ExceptT . withLocalDisplayName db userId displayName $ \ldn -> do
|
ExceptT . withLocalDisplayName db userId displayName $ \ldn -> do
|
||||||
DB.execute
|
DB.execute
|
||||||
db
|
db
|
||||||
"INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, chat_peer_type, user_id, local_alias, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)"
|
"INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, chat_peer_type, user_id, local_alias, preferences, simplex_name, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"
|
||||||
((displayName, fullName, shortDescr, image, contactLink, peerType) :. (userId, localAlias, preferences, currentTs, currentTs))
|
((displayName, fullName, shortDescr, image, contactLink, peerType) :. (userId, localAlias, preferences, profileSimplexName, currentTs, currentTs))
|
||||||
profileId <- insertedRowId db
|
profileId <- insertedRowId db
|
||||||
DB.execute
|
DB.execute
|
||||||
db
|
db
|
||||||
@@ -494,14 +494,16 @@ type PreparedContactRow = (Maybe AConnectionRequestUri, Maybe AConnShortLink, Ma
|
|||||||
|
|
||||||
type GroupDirectInvitationRow = (Maybe ConnReqInvitation, Maybe GroupId, Maybe GroupMemberId, Maybe Int64, BoolInt)
|
type GroupDirectInvitationRow = (Maybe ConnReqInvitation, Maybe GroupId, Maybe GroupMemberId, Maybe Int64, BoolInt)
|
||||||
|
|
||||||
type ContactRow' = (ProfileId, ContactName, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe GroupMemberId, BoolInt) :. GroupDirectInvitationRow :. (Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64, Maybe Text)
|
type ContactRow' = (ProfileId, ContactName, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe GroupMemberId, BoolInt) :. GroupDirectInvitationRow :. (Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64, Maybe Text, Maybe Text)
|
||||||
|
|
||||||
type ContactRow = Only ContactId :. ContactRow'
|
type ContactRow = Only ContactId :. ContactRow'
|
||||||
|
|
||||||
|
-- ct.simplex_name -> Contact.simplexName (user's locally-known label)
|
||||||
|
-- cp.simplex_name -> LocalProfile.simplexName (peer's broadcast claim)
|
||||||
toContact :: VersionRangeChat -> User -> [ChatTagId] -> ContactRow :. MaybeConnectionRow -> Contact
|
toContact :: VersionRangeChat -> User -> [ChatTagId] -> ContactRow :. MaybeConnectionRow -> Contact
|
||||||
toContact vr user chatTags ((Only contactId :. (profileId, localDisplayName, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL, simplexNameRaw)) :. connRow) =
|
toContact vr user chatTags ((Only contactId :. (profileId, localDisplayName, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent) :. groupDirectInvRow :. (uiThemes, BI chatDeleted, customData, chatItemTTL, ctSimplexNameRaw, cpSimplexNameRaw)) :. connRow) =
|
||||||
let simplexName = decodeSimplexName simplexNameRaw
|
let simplexName = decodeSimplexName ctSimplexNameRaw
|
||||||
profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName, peerType, preferences, localAlias}
|
profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName = decodeSimplexName cpSimplexNameRaw, peerType, preferences, localAlias}
|
||||||
activeConn = toMaybeConnection vr connRow
|
activeConn = toMaybeConnection vr connRow
|
||||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||||
incognito = maybe False connIncognito activeConn
|
incognito = maybe False connIncognito activeConn
|
||||||
@@ -532,17 +534,17 @@ getProfileById db userId profileId =
|
|||||||
DB.query
|
DB.query
|
||||||
db
|
db
|
||||||
[sql|
|
[sql|
|
||||||
SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, cp.preferences -- , ct.user_preferences
|
SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.chat_peer_type, cp.local_alias, cp.preferences, cp.simplex_name -- , ct.user_preferences
|
||||||
FROM contact_profiles cp
|
FROM contact_profiles cp
|
||||||
WHERE cp.user_id = ? AND cp.contact_profile_id = ?
|
WHERE cp.user_id = ? AND cp.contact_profile_id = ?
|
||||||
|]
|
|]
|
||||||
(userId, profileId)
|
(userId, profileId)
|
||||||
|
|
||||||
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64) :. (Int64, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat)
|
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64) :. (Int64, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe Text) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat)
|
||||||
|
|
||||||
toContactRequest :: ContactRequestRow -> UserContactRequest
|
toContactRequest :: ContactRequestRow -> UserContactRequest
|
||||||
toContactRequest ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer)) = do
|
toContactRequest ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, simplexNameRaw) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer)) = do
|
||||||
let profile = Profile {displayName, fullName, shortDescr, image, contactLink, simplexName = Nothing, peerType, preferences}
|
let profile = Profile {displayName, fullName, shortDescr, image, contactLink, simplexName = decodeSimplexName simplexNameRaw, peerType, preferences}
|
||||||
cReqChatVRange = fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
|
cReqChatVRange = fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
|
||||||
in UserContactRequest {contactRequestId, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, cReqChatVRange, localDisplayName, profileId, profile, xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, createdAt, updatedAt}
|
in UserContactRequest {contactRequestId, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, cReqChatVRange, localDisplayName, profileId, profile, xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, createdAt, updatedAt}
|
||||||
|
|
||||||
@@ -676,30 +678,27 @@ type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe Member
|
|||||||
|
|
||||||
type GroupKeysRow = (Maybe C.PrivateKeyEd25519, Maybe C.PublicKeyEd25519, Maybe C.PrivateKeyEd25519)
|
type GroupKeysRow = (Maybe C.PrivateKeyEd25519, Maybe C.PublicKeyEd25519, Maybe C.PrivateKeyEd25519)
|
||||||
|
|
||||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. PublicGroupAccessRow :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupKeysRow :. Only (Maybe Text) :. GroupMemberRow
|
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. PublicGroupAccessRow :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupKeysRow :. (Maybe Text, Maybe Text) :. GroupMemberRow
|
||||||
|
|
||||||
type PublicGroupAccessRow = (Maybe Text, Maybe Text, Maybe BoolInt, Maybe BoolInt)
|
type PublicGroupAccessRow = (Maybe Text, Maybe Text, Maybe BoolInt, Maybe BoolInt)
|
||||||
|
|
||||||
type GroupMemberRow = (GroupMemberId, GroupId, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact)
|
type GroupMemberRow = (GroupMemberId, GroupId, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact)
|
||||||
|
|
||||||
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences)
|
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences, Maybe Text)
|
||||||
|
|
||||||
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
|
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
|
||||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri) :. groupKeysRow :. Only simplexNameRaw :. userMemberRow) =
|
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri) :. groupKeysRow :. (gSimplexNameRaw, gpSimplexNameRaw) :. userMemberRow) =
|
||||||
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr}
|
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr}
|
||||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||||
publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ (toPublicGroupAccess accessRow)
|
publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ (toPublicGroupAccess accessRow)
|
||||||
groupKeys = toGroupKeys publicGroupId_ groupKeysRow
|
groupKeys = toGroupKeys publicGroupId_ groupKeysRow
|
||||||
simplexName = decodeSimplexName simplexNameRaw
|
-- groups.simplex_name is the user's locally-known group name (set by the
|
||||||
-- groups.simplex_name is the per-user locally-known group name (set by the
|
-- prepare-via-name path). group_profiles.simplex_name is the peer's
|
||||||
-- prepare-via-name path; see prior plan Task 7). Mirrored into
|
-- broadcast claim (written from XGrpInfo). They are kept distinct so the
|
||||||
-- groupProfile.simplexName as a stopgap until group_profiles.simplex_name
|
-- user's locally-resolved label is not echoed back as canonical.
|
||||||
-- is introduced for the peer-claimed/canonical name. The mirror reads
|
simplexName = decodeSimplexName gSimplexNameRaw
|
||||||
-- NULL today (no writer yet) so XGrpInfo carries Nothing — the leak risk
|
groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, simplexName = decodeSimplexName gpSimplexNameRaw, groupPreferences, memberAdmission}
|
||||||
-- of broadcasting the user's locally-resolved label as canonical only
|
|
||||||
-- materialises once the prepare path starts writing the column.
|
|
||||||
groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, simplexName, groupPreferences, memberAdmission}
|
|
||||||
businessChat = toBusinessChatInfo businessRow
|
businessChat = toBusinessChatInfo businessRow
|
||||||
preparedGroup = toPreparedGroup preparedGroupRow
|
preparedGroup = toPreparedGroup preparedGroupRow
|
||||||
groupSummary = GroupSummary {currentMembers, publicMemberCount}
|
groupSummary = GroupSummary {currentMembers, publicMemberCount}
|
||||||
@@ -763,7 +762,7 @@ groupMemberQuery =
|
|||||||
[sql|
|
[sql|
|
||||||
SELECT
|
SELECT
|
||||||
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
m.group_member_id, m.group_id, m.index_in_group, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
|
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, p.simplex_name,
|
||||||
m.created_at, m.updated_at,
|
m.created_at, m.updated_at,
|
||||||
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
|
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, m.member_pub_key, m.relay_link,
|
||||||
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.xcontact_id, c.custom_user_profile_id,
|
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.xcontact_id, c.custom_user_profile_id,
|
||||||
@@ -780,8 +779,8 @@ toContactMember vr User {userContactId} (memberRow :. connRow) =
|
|||||||
(toGroupMember userContactId memberRow) {activeConn = toMaybeConnection vr connRow}
|
(toGroupMember userContactId memberRow) {activeConn = toMaybeConnection vr connRow}
|
||||||
|
|
||||||
rowToLocalProfile :: ProfileRow -> LocalProfile
|
rowToLocalProfile :: ProfileRow -> LocalProfile
|
||||||
rowToLocalProfile (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, preferences) =
|
rowToLocalProfile (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, preferences, simplexNameRaw) =
|
||||||
LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName = Nothing, peerType, localAlias, preferences}
|
LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, simplexName = decodeSimplexName simplexNameRaw, peerType, localAlias, preferences}
|
||||||
|
|
||||||
toBusinessChatInfo :: BusinessChatInfoRow -> Maybe BusinessChatInfo
|
toBusinessChatInfo :: BusinessChatInfoRow -> Maybe BusinessChatInfo
|
||||||
toBusinessChatInfo (Just chatType, Just businessId, Just customerId) = Just BusinessChatInfo {chatType, businessId, customerId}
|
toBusinessChatInfo (Just chatType, Just businessId, Just customerId) = Just BusinessChatInfo {chatType, businessId, customerId}
|
||||||
@@ -805,11 +804,11 @@ groupInfoQueryFields =
|
|||||||
g.use_relays, g.relay_own_status,
|
g.use_relays, g.relay_own_status,
|
||||||
g.ui_themes, g.summary_current_members_count, g.public_member_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
g.ui_themes, g.summary_current_members_count, g.public_member_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||||
g.root_priv_key, g.root_pub_key, g.member_priv_key,
|
g.root_priv_key, g.root_pub_key, g.member_priv_key,
|
||||||
g.simplex_name,
|
g.simplex_name, gp.simplex_name,
|
||||||
-- GroupMember - membership
|
-- GroupMember - membership
|
||||||
mu.group_member_id, mu.group_id, mu.index_in_group, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
mu.group_member_id, mu.group_id, mu.index_in_group, 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,
|
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.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, pu.simplex_name,
|
||||||
mu.created_at, mu.updated_at,
|
mu.created_at, mu.updated_at,
|
||||||
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link
|
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, mu.member_pub_key, mu.relay_link
|
||||||
|]
|
|]
|
||||||
|
|||||||
@@ -555,6 +555,18 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
|
|||||||
TEContactVerificationReset u ct -> ttyUser u $ viewContactVerificationReset ct
|
TEContactVerificationReset u ct -> ttyUser u $ viewContactVerificationReset ct
|
||||||
TEGroupMemberVerificationReset u g m -> ttyUser u $ viewGroupMemberVerificationReset g m
|
TEGroupMemberVerificationReset u g m -> ttyUser u $ viewGroupMemberVerificationReset g m
|
||||||
CEvtCustomChatEvent u r -> ttyUser' u $ map plain $ T.lines r
|
CEvtCustomChatEvent u r -> ttyUser' u $ map plain $ T.lines r
|
||||||
|
CEvtSimplexNameConflict u ni entity claimedBy displacedFrom ->
|
||||||
|
ttyUser u
|
||||||
|
[ "simplex name "
|
||||||
|
<> plain (shortNameInfoStr ni)
|
||||||
|
<> " now claimed by "
|
||||||
|
<> plain (entityLabel entity claimedBy)
|
||||||
|
<> ", was "
|
||||||
|
<> plain (entityLabel entity displacedFrom)
|
||||||
|
]
|
||||||
|
where
|
||||||
|
entityLabel SNCEContact n = "@" <> n
|
||||||
|
entityLabel SNCEGroup n = "#" <> n
|
||||||
where
|
where
|
||||||
ttyUser :: User -> [StyledString] -> [StyledString]
|
ttyUser :: User -> [StyledString] -> [StyledString]
|
||||||
ttyUser user@User {showNtfs, activeUser, viewPwdHash} ss
|
ttyUser user@User {showNtfs, activeUser, viewPwdHash} ss
|
||||||
|
|||||||
Reference in New Issue
Block a user