mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-31 14:06:05 +00:00
Merge branch 'f/chat-relays-protocol' into f/chat-relays-stress-test
This commit is contained in:
@@ -2223,7 +2223,6 @@ Known:
|
||||
- updatedAt: UTCTime
|
||||
- supportChat: [GroupSupportChat](#groupsupportchat)?
|
||||
- isRelay: bool
|
||||
- relayData: [GroupRelay](#grouprelay)?
|
||||
|
||||
|
||||
---
|
||||
@@ -2345,6 +2344,7 @@ Known:
|
||||
|
||||
**Record type**:
|
||||
- groupRelayId: int64
|
||||
- groupMemberId: int64
|
||||
- userChatRelayId: int64
|
||||
- relayStatus: [RelayStatus](#relaystatus)
|
||||
- relayLink: string?
|
||||
@@ -3598,6 +3598,10 @@ GroupRelayNotFound:
|
||||
- type: "groupRelayNotFound"
|
||||
- groupRelayId: int64
|
||||
|
||||
GroupRelayNotFoundByMemberId:
|
||||
- type: "groupRelayNotFoundByMemberId"
|
||||
- groupMemberId: int64
|
||||
|
||||
InvalidQuote:
|
||||
- type: "invalidQuote"
|
||||
|
||||
|
||||
@@ -2513,7 +2513,6 @@ export interface GroupMember {
|
||||
updatedAt: string // ISO-8601 timestamp
|
||||
supportChat?: GroupSupportChat
|
||||
isRelay: boolean
|
||||
relayData?: GroupRelay
|
||||
}
|
||||
|
||||
export interface GroupMemberAdmission {
|
||||
@@ -2595,6 +2594,7 @@ export interface GroupProfile {
|
||||
|
||||
export interface GroupRelay {
|
||||
groupRelayId: number // int64
|
||||
groupMemberId: number // int64
|
||||
userChatRelayId: number // int64
|
||||
relayStatus: RelayStatus
|
||||
relayLink?: string
|
||||
@@ -3816,6 +3816,7 @@ export type StoreError =
|
||||
| StoreError.UsageConditionsNotFound
|
||||
| StoreError.UserChatRelayNotFound
|
||||
| StoreError.GroupRelayNotFound
|
||||
| StoreError.GroupRelayNotFoundByMemberId
|
||||
| StoreError.InvalidQuote
|
||||
| StoreError.InvalidMention
|
||||
| StoreError.InvalidDeliveryTask
|
||||
@@ -3903,6 +3904,7 @@ export namespace StoreError {
|
||||
| "usageConditionsNotFound"
|
||||
| "userChatRelayNotFound"
|
||||
| "groupRelayNotFound"
|
||||
| "groupRelayNotFoundByMemberId"
|
||||
| "invalidQuote"
|
||||
| "invalidMention"
|
||||
| "invalidDeliveryTask"
|
||||
@@ -4290,6 +4292,11 @@ export namespace StoreError {
|
||||
groupRelayId: number // int64
|
||||
}
|
||||
|
||||
export interface GroupRelayNotFoundByMemberId extends Interface {
|
||||
type: "groupRelayNotFoundByMemberId"
|
||||
groupMemberId: number // int64
|
||||
}
|
||||
|
||||
export interface InvalidQuote extends Interface {
|
||||
type: "invalidQuote"
|
||||
}
|
||||
|
||||
@@ -3660,8 +3660,8 @@ processChatCommand vr nm = \case
|
||||
-- TODO - or make "add relays" api retriable, via prepared connection
|
||||
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
|
||||
(relayMember, conn, groupRelay) <- withFastStore $ \db -> do
|
||||
groupRelay <- createGroupRelayRecord db gInfo relay
|
||||
relayMember <- createRelayForOwner db vr gVar user gInfo relay groupRelay
|
||||
relayMember <- createRelayForOwner db vr gVar user gInfo relay
|
||||
groupRelay <- createGroupRelayRecord db gInfo relayMember relay
|
||||
conn <- createRelayConnection db vr user (groupMemberId' relayMember) connId ConnPrepared chatV subMode
|
||||
pure (relayMember, conn, groupRelay)
|
||||
let GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
|
||||
@@ -728,18 +728,13 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
-- [async agent commands] no continuation needed, but command should be asynchronous for stability
|
||||
allowAgentConnectionAsync user conn' confId XOk
|
||||
| otherwise -> messageError "x.grp.acpt: memberId is different from expected"
|
||||
-- TODO [relays] owner: XGrpRelayAcpt processing branch
|
||||
-- TODO - TBC relay category
|
||||
XGrpRelayAcpt relayLink
|
||||
| memberRole' membership == GROwner ->
|
||||
case relayData m of
|
||||
Just relay -> do
|
||||
-- TODO [relays] owner: check current relay status?
|
||||
withStore' $ \db -> do
|
||||
updateGroupMemberStatus db userId m GSMemAccepted
|
||||
void $ setRelayLinkAccepted db relay relayLink
|
||||
allowAgentConnectionAsync user conn' confId XOk
|
||||
Nothing -> messageError "x.grp.relay.acpt: member is not saved as relay"
|
||||
| memberRole' membership == GROwner && isRelay' m -> do
|
||||
withStore $ \db -> do
|
||||
relay <- getGroupRelayByGMId db (groupMemberId' m)
|
||||
liftIO $ updateGroupMemberStatus db userId m GSMemAccepted
|
||||
void $ liftIO $ setRelayLinkAccepted db relay relayLink
|
||||
allowAgentConnectionAsync user conn' confId XOk
|
||||
| otherwise -> messageError "x.grp.relay.acpt: only owner can add relay"
|
||||
_ -> messageError "CONF from invited member must have x.grp.acpt"
|
||||
GCHostMember ->
|
||||
@@ -803,8 +798,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
pure gInfo {membership = membership {memberStatus = GSMemConnected}}
|
||||
else pure gInfo
|
||||
pure (m {memberStatus = GSMemConnected}, gInfo')
|
||||
-- TODO [relays] member: only show "joined group" event/output once
|
||||
-- TODO - or different output "connected to relay"
|
||||
-- TODO [relays] member: don't duplicate e2ee and descr chat items for each relay
|
||||
toView $ CEvtUserJoinedGroup user gInfo' m'
|
||||
(gInfo'', m'', scopeInfo) <- mkGroupChatScope gInfo' m'
|
||||
let cd = CDGroupRcv gInfo'' scopeInfo m''
|
||||
@@ -814,23 +808,21 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
memberConnectedChatItem gInfo'' scopeInfo m''
|
||||
let welcomeMsgId_ = (\PreparedGroup {welcomeSharedMsgId = mId} -> mId) <$> prepared
|
||||
unless (memberPending membership || isJust welcomeMsgId_) $ maybeCreateGroupDescrLocal gInfo'' m''
|
||||
GCInviteeMember ->
|
||||
case relayData m of
|
||||
-- TODO [relays] owner: relay CON processing branch
|
||||
-- TODO - TBC relay category
|
||||
Just relay -> do
|
||||
-- TODO [relays] owner: agent async setConnShortLink api
|
||||
GCInviteeMember
|
||||
| isRelay' m -> do
|
||||
withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected
|
||||
let m' = m {memberStatus = GSMemConnected}
|
||||
-- TODO [relays] owner: agent async setConnShortLink api
|
||||
setGroupLinkData' NRMBackground user gInfo >>= \case
|
||||
Just gLink -> do
|
||||
(relay', relays) <- withStore' $ \db ->
|
||||
(,) <$> updateRelayStatus db relay RSActive <*> getGroupRelays db gInfo
|
||||
let m'' = m' {relayData = Just relay'}
|
||||
relays <- withStore $ \db -> do
|
||||
relay <- getGroupRelayByGMId db (groupMemberId' m)
|
||||
void $ liftIO $ updateRelayStatus db relay RSActive
|
||||
liftIO $ getGroupRelays db gInfo
|
||||
-- TODO [relays] owner: relay added chat item?
|
||||
toView $ CEvtRelayJoined user gInfo m'' gLink relays
|
||||
toView $ CEvtRelayJoined user gInfo m' gLink relays
|
||||
Nothing -> messageError "x.grp.relay.acpt: group link not updated"
|
||||
Nothing -> do
|
||||
| otherwise -> do
|
||||
(gInfo', mStatus) <-
|
||||
if not (memberPending m)
|
||||
then do
|
||||
|
||||
@@ -149,16 +149,15 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
||||
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.is_relay, NULL, NULL, NULL, NULL,
|
||||
mu.is_relay,
|
||||
-- from GroupMember
|
||||
m.group_member_id, m.group_id, 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.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.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link
|
||||
m.is_relay
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id
|
||||
JOIN groups g ON g.group_id = m.group_id
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu ON g.group_id = mu.group_id
|
||||
|
||||
@@ -76,6 +76,7 @@ module Simplex.Chat.Store.Groups
|
||||
createNewContactMember,
|
||||
createGroupRelayRecord,
|
||||
getGroupRelayById,
|
||||
getGroupRelayByGMId,
|
||||
getGroupRelays,
|
||||
createRelayForOwner,
|
||||
createRelayForMember,
|
||||
@@ -204,11 +205,11 @@ import Database.SQLite.Simple (Only (..), Query, (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
type MaybeGroupMemberRow = (Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. (Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe 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 BoolInt, Maybe Int64, Maybe Int64, Maybe RelayStatus, Maybe ShortLinkContact)
|
||||
type MaybeGroupMemberRow = (Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. (Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe 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 BoolInt)
|
||||
|
||||
toMaybeGroupMember :: Int64 -> MaybeGroupMemberRow -> Maybe GroupMember
|
||||
toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, 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) :. (Just isRelay, groupRelayId, chatRelayId, relayStatus, relayLink)) =
|
||||
Just $ toGroupMember userContactId ((groupMemberId, groupId, 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) :. (isRelay, groupRelayId, chatRelayId, relayStatus, relayLink))
|
||||
toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, 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, Just isRelay)) =
|
||||
Just $ toGroupMember userContactId ((groupMemberId, groupId, 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, isRelay))
|
||||
toMaybeGroupMember _ _ = Nothing
|
||||
|
||||
createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink
|
||||
@@ -497,8 +498,7 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe
|
||||
createdAt,
|
||||
updatedAt = createdAt,
|
||||
supportChat = Nothing,
|
||||
isRelay = BoolDef isRelay,
|
||||
relayData = Nothing
|
||||
isRelay = BoolDef isRelay
|
||||
}
|
||||
where
|
||||
memberChatVRange@(VersionRange minV maxV) = vr
|
||||
@@ -1116,8 +1116,7 @@ createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId,
|
||||
createdAt,
|
||||
updatedAt = createdAt,
|
||||
supportChat = Nothing,
|
||||
isRelay = BoolDef False,
|
||||
relayData = Nothing
|
||||
isRelay = BoolDef False
|
||||
}
|
||||
where
|
||||
insertMember_ =
|
||||
@@ -1135,18 +1134,18 @@ createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId,
|
||||
:. (minV, maxV)
|
||||
)
|
||||
|
||||
createGroupRelayRecord :: DB.Connection -> GroupInfo -> UserChatRelay -> ExceptT StoreError IO GroupRelay
|
||||
createGroupRelayRecord db GroupInfo {groupId} UserChatRelay {chatRelayId} = do
|
||||
createGroupRelayRecord :: DB.Connection -> GroupInfo -> GroupMember -> UserChatRelay -> ExceptT StoreError IO GroupRelay
|
||||
createGroupRelayRecord db GroupInfo {groupId} GroupMember {groupMemberId} UserChatRelay {chatRelayId} = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
INSERT INTO group_relays
|
||||
(group_id, chat_relay_id, relay_status, created_at, updated_at)
|
||||
VALUES (?,?,?,?,?)
|
||||
(group_id, group_member_id, chat_relay_id, relay_status, created_at, updated_at)
|
||||
VALUES (?,?,?,?,?,?)
|
||||
|]
|
||||
(groupId, chatRelayId, RSNew, currentTs, currentTs)
|
||||
(groupId, groupMemberId, chatRelayId, RSNew, currentTs, currentTs)
|
||||
relayId <- liftIO $ insertedRowId db
|
||||
getGroupRelayById db relayId
|
||||
|
||||
@@ -1158,6 +1157,14 @@ getGroupRelayById db relayId =
|
||||
(groupRelayQuery <> " WHERE group_relay_id = ?")
|
||||
(Only relayId)
|
||||
|
||||
getGroupRelayByGMId :: DB.Connection -> GroupMemberId -> ExceptT StoreError IO GroupRelay
|
||||
getGroupRelayByGMId db groupMemberId =
|
||||
ExceptT . firstRow toGroupRelay (SEGroupRelayNotFoundByMemberId groupMemberId) $
|
||||
DB.query
|
||||
db
|
||||
(groupRelayQuery <> " WHERE group_member_id = ?")
|
||||
(Only groupMemberId)
|
||||
|
||||
getGroupRelays :: DB.Connection -> GroupInfo -> IO [GroupRelay]
|
||||
getGroupRelays db GroupInfo {groupId} =
|
||||
map toGroupRelay
|
||||
@@ -1169,21 +1176,21 @@ getGroupRelays db GroupInfo {groupId} =
|
||||
groupRelayQuery :: Query
|
||||
groupRelayQuery =
|
||||
[sql|
|
||||
SELECT group_relay_id, chat_relay_id, relay_status, relay_link
|
||||
SELECT group_relay_id, group_member_id, chat_relay_id, relay_status, relay_link
|
||||
FROM group_relays
|
||||
|]
|
||||
|
||||
toGroupRelay :: (Int64, Int64, RelayStatus, Maybe ShortLinkContact) -> GroupRelay
|
||||
toGroupRelay (groupRelayId, userChatRelayId, relayStatus, relayLink) =
|
||||
GroupRelay {groupRelayId, userChatRelayId, relayStatus, relayLink}
|
||||
toGroupRelay :: (Int64, GroupMemberId, Int64, RelayStatus, Maybe ShortLinkContact) -> GroupRelay
|
||||
toGroupRelay (groupRelayId, groupMemberId, userChatRelayId, relayStatus, relayLink) =
|
||||
GroupRelay {groupRelayId, groupMemberId, userChatRelayId, relayStatus, relayLink}
|
||||
|
||||
-- TODO [relays] TBC role, category, relay profile
|
||||
-- TODO - GCInviteeMember -> GCRelayMember?
|
||||
-- TODO - GRMember -> GRRelay?
|
||||
-- TODO - create 1 profile per relay, link to chat_relays?
|
||||
-- TODO - retrieve profile from relay address
|
||||
createRelayForOwner :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupInfo -> UserChatRelay -> GroupRelay -> ExceptT StoreError IO GroupMember
|
||||
createRelayForOwner db vr gVar user@User {userId, userContactId} GroupInfo {groupId, membership} UserChatRelay {name} GroupRelay {groupRelayId} = do
|
||||
createRelayForOwner :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupInfo -> UserChatRelay -> ExceptT StoreError IO GroupMember
|
||||
createRelayForOwner db vr gVar user@User {userId, userContactId} GroupInfo {groupId, membership} UserChatRelay {name} = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
let relayProfile = profileFromName name
|
||||
(localDisplayName, memProfileId) <- createNewMemberProfile_ db user relayProfile currentTs
|
||||
@@ -1193,14 +1200,11 @@ createRelayForOwner db vr gVar user@User {userId, userContactId} GroupInfo {grou
|
||||
[sql|
|
||||
INSERT INTO group_members
|
||||
( group_id, member_id, member_role, member_category, member_status, invited_by, invited_by_group_member_id,
|
||||
user_id, local_display_name, contact_profile_id, created_at, updated_at,
|
||||
is_relay, group_relay_id
|
||||
)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
user_id, local_display_name, contact_profile_id, created_at, updated_at, is_relay)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (groupId, MemberId memId, GRMember, GCInviteeMember, GSMemInvited, fromInvitedBy userContactId IBUser, groupMemberId' membership)
|
||||
:. (userId, localDisplayName, memProfileId, currentTs, currentTs)
|
||||
:. (BI True, groupRelayId)
|
||||
:. (userId, localDisplayName, memProfileId, currentTs, currentTs, BI True)
|
||||
)
|
||||
insertedRowId db
|
||||
getGroupMemberById db vr user groupMemberId
|
||||
@@ -1689,8 +1693,7 @@ createNewMember_
|
||||
createdAt,
|
||||
updatedAt = createdAt,
|
||||
supportChat = Nothing,
|
||||
isRelay = BoolDef isRelay,
|
||||
relayData = Nothing
|
||||
isRelay = BoolDef isRelay
|
||||
}
|
||||
|
||||
checkGroupMemberHasItems :: DB.Connection -> User -> GroupMember -> IO (Maybe ChatItemId)
|
||||
|
||||
@@ -679,10 +679,9 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
||||
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
|
||||
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.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link
|
||||
m.is_relay
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id
|
||||
LEFT JOIN contacts c ON m.contact_id = c.contact_id
|
||||
LEFT JOIN chat_items i ON i.user_id = m.user_id
|
||||
AND i.group_id = m.group_id
|
||||
@@ -3005,7 +3004,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
||||
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
|
||||
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.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link,
|
||||
m.is_relay,
|
||||
-- quoted ChatItem
|
||||
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
|
||||
-- quoted GroupMember
|
||||
@@ -3014,26 +3013,23 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
||||
rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences,
|
||||
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.is_relay, rr.group_relay_id, rr.chat_relay_id, rr.relay_status, rr.relay_link,
|
||||
rm.is_relay,
|
||||
-- deleted by GroupMember
|
||||
dbm.group_member_id, dbm.group_id, 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,
|
||||
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences,
|
||||
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.is_relay, dbr.group_relay_id, dbr.chat_relay_id, dbr.relay_status, dbr.relay_link
|
||||
dbm.is_relay
|
||||
FROM chat_items i
|
||||
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
|
||||
LEFT JOIN group_members m ON m.group_member_id = i.group_member_id
|
||||
LEFT JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id
|
||||
LEFT JOIN chat_items ri ON ri.shared_msg_id = i.quoted_shared_msg_id AND ri.group_id = i.group_id
|
||||
LEFT JOIN group_members rm ON rm.group_member_id = ri.group_member_id
|
||||
LEFT JOIN contact_profiles rp ON rp.contact_profile_id = COALESCE(rm.member_profile_id, rm.contact_profile_id)
|
||||
LEFT JOIN group_relays rr ON rr.group_relay_id = rm.group_relay_id
|
||||
LEFT JOIN group_members dbm ON dbm.group_member_id = i.item_deleted_by_group_member_id
|
||||
LEFT JOIN contact_profiles dbp ON dbp.contact_profile_id = COALESCE(dbm.member_profile_id, dbm.contact_profile_id)
|
||||
LEFT JOIN group_relays dbr ON dbr.group_relay_id = dbm.group_relay_id
|
||||
WHERE i.user_id = ? AND i.group_id = ? AND i.chat_item_id = ?
|
||||
|]
|
||||
(userId, groupId, itemId)
|
||||
|
||||
@@ -12,23 +12,43 @@ m20251018_chat_relays =
|
||||
[r|
|
||||
CREATE TABLE chat_relays(
|
||||
chat_relay_id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||
address TEXT NOT NULL,
|
||||
address BYTEA NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
domains TEXT NOT NULL,
|
||||
preset SMALLINT NOT NULL DEFAULT 0,
|
||||
tested SMALLINT,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
user_id BIGINT NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (now()),
|
||||
updated_at TEXT NOT NULL DEFAULT (now()),
|
||||
UNIQUE(user_id, address),
|
||||
UNIQUE(user_id, name)
|
||||
updated_at TEXT NOT NULL DEFAULT (now())
|
||||
);
|
||||
|
||||
CREATE INDEX idx_chat_relays_user_id ON chat_relays(user_id);
|
||||
CREATE UNIQUE INDEX idx_chat_relays_user_id_address ON chat_relays(user_id, address);
|
||||
CREATE UNIQUE INDEX idx_chat_relays_user_id_name ON chat_relays(user_id, name);
|
||||
|
||||
ALTER TABLE users ADD COLUMN is_user_chat_relay SMALLINT NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE groups ADD COLUMN use_relays SMALLINT NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE groups ADD COLUMN relay_own_status TEXT;
|
||||
|
||||
ALTER TABLE group_profiles ADD COLUMN group_link BYTEA;
|
||||
|
||||
CREATE TABLE group_relays(
|
||||
group_relay_id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||
group_id BIGINT NOT NULL REFERENCES groups ON DELETE CASCADE,
|
||||
group_member_id BIGINT NOT NULL REFERENCES group_members ON DELETE CASCADE,
|
||||
chat_relay_id BIGINT NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
|
||||
relay_status TEXT NOT NULL,
|
||||
relay_link BYTEA,
|
||||
created_at TEXT NOT NULL DEFAULT (now()),
|
||||
updated_at TEXT NOT NULL DEFAULT (now())
|
||||
);
|
||||
CREATE INDEX idx_group_relays_group_id ON group_relays(group_id);
|
||||
CREATE UNIQUE INDEX idx_group_relays_group_member_id ON group_relays(group_member_id);
|
||||
CREATE INDEX idx_group_relays_chat_relay_id ON group_relays(chat_relay_id);
|
||||
|
||||
ALTER TABLE group_members ADD COLUMN is_relay SMALLINT NOT NULL DEFAULT 0;
|
||||
|]
|
||||
|
||||
@@ -36,11 +56,23 @@ down_m20251018_chat_relays :: Text
|
||||
down_m20251018_chat_relays =
|
||||
T.pack
|
||||
[r|
|
||||
ALTER TABLE group_members DROP COLUMN is_relay;
|
||||
DROP INDEX idx_chat_relays_user_id;
|
||||
DROP INDEX idx_chat_relays_user_id_address;
|
||||
DROP INDEX idx_chat_relays_user_id_name;
|
||||
DROP TABLE chat_relays;
|
||||
|
||||
ALTER TABLE users DROP COLUMN is_user_chat_relay;
|
||||
|
||||
DROP INDEX idx_chat_relays_user_id;
|
||||
ALTER TABLE groups DROP COLUMN use_relays;
|
||||
|
||||
DROP TABLE chat_relays;
|
||||
ALTER TABLE groups DROP COLUMN relay_own_status;
|
||||
|
||||
ALTER TABLE group_profiles DROP COLUMN group_link;
|
||||
|
||||
DROP INDEX idx_group_relays_group_id;
|
||||
DROP INDEX idx_group_relays_group_member_id;
|
||||
DROP INDEX idx_group_relays_chat_relay_id;
|
||||
DROP TABLE group_relays;
|
||||
|
||||
ALTER TABLE group_members DROP COLUMN is_relay;
|
||||
|]
|
||||
|
||||
@@ -284,6 +284,33 @@ ALTER TABLE test_chat_schema.chat_items ALTER COLUMN chat_item_id ADD GENERATED
|
||||
|
||||
|
||||
|
||||
CREATE TABLE test_chat_schema.chat_relays (
|
||||
chat_relay_id bigint NOT NULL,
|
||||
address bytea NOT NULL,
|
||||
name text NOT NULL,
|
||||
domains text NOT NULL,
|
||||
preset smallint DEFAULT 0 NOT NULL,
|
||||
tested smallint,
|
||||
enabled smallint DEFAULT 1 NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
deleted smallint DEFAULT 0 NOT NULL,
|
||||
created_at text DEFAULT now() NOT NULL,
|
||||
updated_at text DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
ALTER TABLE test_chat_schema.chat_relays ALTER COLUMN chat_relay_id ADD GENERATED ALWAYS AS IDENTITY (
|
||||
SEQUENCE NAME test_chat_schema.chat_relays_chat_relay_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE test_chat_schema.chat_tags (
|
||||
chat_tag_id bigint NOT NULL,
|
||||
user_id bigint,
|
||||
@@ -706,7 +733,8 @@ CREATE TABLE test_chat_schema.group_members (
|
||||
support_chat_items_mentions bigint DEFAULT 0 NOT NULL,
|
||||
support_chat_last_msg_from_member_ts timestamp with time zone,
|
||||
member_xcontact_id bytea,
|
||||
member_welcome_shared_msg_id bytea
|
||||
member_welcome_shared_msg_id bytea,
|
||||
is_relay smallint DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@@ -734,7 +762,8 @@ CREATE TABLE test_chat_schema.group_profiles (
|
||||
preferences text,
|
||||
description text,
|
||||
member_admission text,
|
||||
short_descr text
|
||||
short_descr text,
|
||||
group_link bytea
|
||||
);
|
||||
|
||||
|
||||
@@ -750,6 +779,30 @@ ALTER TABLE test_chat_schema.group_profiles ALTER COLUMN group_profile_id ADD GE
|
||||
|
||||
|
||||
|
||||
CREATE TABLE test_chat_schema.group_relays (
|
||||
group_relay_id bigint NOT NULL,
|
||||
group_id bigint NOT NULL,
|
||||
group_member_id bigint NOT NULL,
|
||||
chat_relay_id bigint NOT NULL,
|
||||
relay_status text NOT NULL,
|
||||
relay_link bytea,
|
||||
created_at text DEFAULT now() NOT NULL,
|
||||
updated_at text DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
ALTER TABLE test_chat_schema.group_relays ALTER COLUMN group_relay_id ADD GENERATED ALWAYS AS IDENTITY (
|
||||
SEQUENCE NAME test_chat_schema.group_relays_group_relay_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE test_chat_schema.group_snd_item_statuses (
|
||||
group_snd_item_status_id bigint NOT NULL,
|
||||
chat_item_id bigint NOT NULL,
|
||||
@@ -805,7 +858,9 @@ CREATE TABLE test_chat_schema.groups (
|
||||
request_shared_msg_id bytea,
|
||||
conn_link_prepared_connection smallint DEFAULT 0 NOT NULL,
|
||||
via_group_link_uri bytea,
|
||||
summary_current_members_count bigint DEFAULT 0 NOT NULL
|
||||
summary_current_members_count bigint DEFAULT 0 NOT NULL,
|
||||
use_relays smallint DEFAULT 0 NOT NULL,
|
||||
relay_own_status text
|
||||
);
|
||||
|
||||
|
||||
@@ -1273,7 +1328,8 @@ CREATE TABLE test_chat_schema.users (
|
||||
user_member_profile_updated_at timestamp with time zone,
|
||||
ui_themes text,
|
||||
active_order bigint DEFAULT 0 NOT NULL,
|
||||
auto_accept_member_contacts smallint DEFAULT 0 NOT NULL
|
||||
auto_accept_member_contacts smallint DEFAULT 0 NOT NULL,
|
||||
is_user_chat_relay smallint DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@@ -1357,6 +1413,11 @@ ALTER TABLE ONLY test_chat_schema.chat_items
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.chat_relays
|
||||
ADD CONSTRAINT chat_relays_pkey PRIMARY KEY (chat_relay_id);
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.chat_tags
|
||||
ADD CONSTRAINT chat_tags_pkey PRIMARY KEY (chat_tag_id);
|
||||
|
||||
@@ -1472,6 +1533,11 @@ ALTER TABLE ONLY test_chat_schema.group_profiles
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.group_relays
|
||||
ADD CONSTRAINT group_relays_pkey PRIMARY KEY (group_relay_id);
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.group_snd_item_statuses
|
||||
ADD CONSTRAINT group_snd_item_statuses_pkey PRIMARY KEY (group_snd_item_status_id);
|
||||
|
||||
@@ -1837,6 +1903,18 @@ CREATE INDEX idx_chat_items_user_id_item_status ON test_chat_schema.chat_items U
|
||||
|
||||
|
||||
|
||||
CREATE INDEX idx_chat_relays_user_id ON test_chat_schema.chat_relays USING btree (user_id);
|
||||
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX idx_chat_relays_user_id_address ON test_chat_schema.chat_relays USING btree (user_id, address);
|
||||
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX idx_chat_relays_user_id_name ON test_chat_schema.chat_relays USING btree (user_id, name);
|
||||
|
||||
|
||||
|
||||
CREATE INDEX idx_chat_tags_chats_chat_tag_id ON test_chat_schema.chat_tags_chats USING btree (chat_tag_id);
|
||||
|
||||
|
||||
@@ -2105,6 +2183,18 @@ CREATE INDEX idx_group_profiles_user_id ON test_chat_schema.group_profiles USING
|
||||
|
||||
|
||||
|
||||
CREATE INDEX idx_group_relays_chat_relay_id ON test_chat_schema.group_relays USING btree (chat_relay_id);
|
||||
|
||||
|
||||
|
||||
CREATE INDEX idx_group_relays_group_id ON test_chat_schema.group_relays USING btree (group_id);
|
||||
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX idx_group_relays_group_member_id ON test_chat_schema.group_relays USING btree (group_member_id);
|
||||
|
||||
|
||||
|
||||
CREATE INDEX idx_group_snd_item_statuses_chat_item_id ON test_chat_schema.group_snd_item_statuses USING btree (chat_item_id);
|
||||
|
||||
|
||||
@@ -2463,6 +2553,11 @@ ALTER TABLE ONLY test_chat_schema.chat_items
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.chat_relays
|
||||
ADD CONSTRAINT chat_relays_user_id_fkey FOREIGN KEY (user_id) REFERENCES test_chat_schema.users(user_id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.chat_tags_chats
|
||||
ADD CONSTRAINT chat_tags_chats_chat_tag_id_fkey FOREIGN KEY (chat_tag_id) REFERENCES test_chat_schema.chat_tags(chat_tag_id) ON DELETE CASCADE;
|
||||
|
||||
@@ -2783,6 +2878,21 @@ ALTER TABLE ONLY test_chat_schema.group_profiles
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.group_relays
|
||||
ADD CONSTRAINT group_relays_chat_relay_id_fkey FOREIGN KEY (chat_relay_id) REFERENCES test_chat_schema.chat_relays(chat_relay_id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.group_relays
|
||||
ADD CONSTRAINT group_relays_group_id_fkey FOREIGN KEY (group_id) REFERENCES test_chat_schema.groups(group_id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.group_relays
|
||||
ADD CONSTRAINT group_relays_group_member_id_fkey FOREIGN KEY (group_member_id) REFERENCES test_chat_schema.group_members(group_member_id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE ONLY test_chat_schema.group_snd_item_statuses
|
||||
ADD CONSTRAINT group_snd_item_statuses_chat_item_id_fkey FOREIGN KEY (chat_item_id) REFERENCES test_chat_schema.chat_items(chat_item_id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
@@ -5,13 +5,6 @@ module Simplex.Chat.Store.SQLite.Migrations.M20251018_chat_relays where
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
-- TODO [relays] owner: consider flipping group_members.group_relay_id fkey to group_relays.group_member_id
|
||||
-- TODO - pros: less joins in all member queries,
|
||||
-- TODO - can discern relay by is_relay,
|
||||
-- TODO - little places need additional relay data
|
||||
-- TODO - cons: in some places will need extra query:
|
||||
-- TODO - SELECT ... FROM group_relays WHERE group_member_id = ?
|
||||
|
||||
-- - chat_relays - user's list of chat relays to choose from (similar to protocol_servers)
|
||||
-- - users.is_user_chat_relay - indicates that the user can serve as a chat relay
|
||||
-- (TBC usage, e.g. agree to invitations to be relay)
|
||||
@@ -21,18 +14,14 @@ import Database.SQLite.Simple.QQ (sql)
|
||||
-- - group_relays.chat_relay_id - associates group_relays record with a chat_relays record,
|
||||
-- chat_relays.deleted is to keep associated record if user removes chat relay from configuration,
|
||||
-- but has group relays using it
|
||||
-- - group_members.is_relay - indicates that the member is a chat relay (to all group members)
|
||||
-- - group_members.group_relay_id - associates group_members record with a group_relays record for a group owner;
|
||||
-- receiving event to member connection, owner can match it to the relay;
|
||||
-- TBC inverse association - from group_relays to group_members?
|
||||
-- - TBC also inverse link from group_relays to group_members? (group_relays.group_member_id)
|
||||
-- - group_members.is_relay - indicates that the member is a chat relay
|
||||
-- - groups.relay_own_status - indicates for a relay client that it is chat relay for the group (RelayStatus)
|
||||
m20251018_chat_relays :: Query
|
||||
m20251018_chat_relays =
|
||||
[sql|
|
||||
CREATE TABLE chat_relays(
|
||||
chat_relay_id INTEGER PRIMARY KEY,
|
||||
address TEXT NOT NULL,
|
||||
address BLOB NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
domains TEXT NOT NULL,
|
||||
preset INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -41,11 +30,11 @@ CREATE TABLE chat_relays(
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
deleted INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
UNIQUE(user_id, address),
|
||||
UNIQUE(user_id, name)
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
CREATE INDEX idx_chat_relays_user_id ON chat_relays(user_id);
|
||||
CREATE UNIQUE INDEX idx_chat_relays_user_id_address ON chat_relays(user_id, address);
|
||||
CREATE UNIQUE INDEX idx_chat_relays_user_id_name ON chat_relays(user_id, name);
|
||||
|
||||
ALTER TABLE users ADD COLUMN is_user_chat_relay INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
@@ -58,6 +47,7 @@ ALTER TABLE group_profiles ADD COLUMN group_link BLOB;
|
||||
CREATE TABLE group_relays(
|
||||
group_relay_id INTEGER PRIMARY KEY,
|
||||
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
|
||||
group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE,
|
||||
chat_relay_id INTEGER NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
|
||||
relay_status TEXT NOT NULL,
|
||||
relay_link BLOB,
|
||||
@@ -65,18 +55,18 @@ CREATE TABLE group_relays(
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
CREATE INDEX idx_group_relays_group_id ON group_relays(group_id);
|
||||
CREATE UNIQUE INDEX idx_group_relays_group_member_id ON group_relays(group_member_id);
|
||||
CREATE INDEX idx_group_relays_chat_relay_id ON group_relays(chat_relay_id);
|
||||
|
||||
ALTER TABLE group_members ADD COLUMN is_relay INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE group_members ADD COLUMN group_relay_id INTEGER REFERENCES group_relays ON DELETE SET NULL;
|
||||
CREATE INDEX idx_group_members_group_relay_id ON group_members(group_relay_id);
|
||||
|]
|
||||
|
||||
down_m20251018_chat_relays :: Query
|
||||
down_m20251018_chat_relays =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_relays_user_id;
|
||||
DROP INDEX idx_chat_relays_user_id_address;
|
||||
DROP INDEX idx_chat_relays_user_id_name;
|
||||
DROP TABLE chat_relays;
|
||||
|
||||
ALTER TABLE users DROP COLUMN is_user_chat_relay;
|
||||
@@ -88,11 +78,9 @@ ALTER TABLE groups DROP COLUMN relay_own_status;
|
||||
ALTER TABLE group_profiles DROP COLUMN group_link;
|
||||
|
||||
DROP INDEX idx_group_relays_group_id;
|
||||
DROP INDEX idx_group_relays_group_member_id;
|
||||
DROP INDEX idx_group_relays_chat_relay_id;
|
||||
DROP TABLE group_relays;
|
||||
|
||||
ALTER TABLE group_members DROP COLUMN is_relay;
|
||||
|
||||
DROP INDEX idx_group_members_group_relay_id;
|
||||
ALTER TABLE group_members DROP COLUMN group_relay_id;
|
||||
|]
|
||||
|
||||
@@ -200,7 +200,6 @@ CREATE TABLE group_members(
|
||||
member_xcontact_id BLOB,
|
||||
member_welcome_shared_msg_id BLOB,
|
||||
is_relay INTEGER NOT NULL DEFAULT 0,
|
||||
group_relay_id INTEGER REFERENCES group_relays ON DELETE SET NULL,
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
@@ -745,6 +744,7 @@ CREATE TABLE chat_relays(
|
||||
CREATE TABLE group_relays(
|
||||
group_relay_id INTEGER PRIMARY KEY,
|
||||
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
|
||||
group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE,
|
||||
chat_relay_id INTEGER NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
|
||||
relay_status TEXT NOT NULL,
|
||||
relay_link BLOB,
|
||||
@@ -1216,8 +1216,10 @@ CREATE INDEX idx_connections_to_subscribe ON connections(
|
||||
);
|
||||
CREATE INDEX idx_chat_relays_user_id ON chat_relays(user_id);
|
||||
CREATE INDEX idx_group_relays_group_id ON group_relays(group_id);
|
||||
CREATE UNIQUE INDEX idx_group_relays_group_member_id ON group_relays(
|
||||
group_member_id
|
||||
);
|
||||
CREATE INDEX idx_group_relays_chat_relay_id ON group_relays(chat_relay_id);
|
||||
CREATE INDEX idx_group_members_group_relay_id ON group_members(group_relay_id);
|
||||
CREATE TRIGGER on_group_members_insert_update_summary
|
||||
AFTER INSERT ON group_members
|
||||
FOR EACH ROW
|
||||
|
||||
@@ -149,6 +149,7 @@ data StoreError
|
||||
| SEUsageConditionsNotFound
|
||||
| SEUserChatRelayNotFound {chatRelayId :: Int64}
|
||||
| SEGroupRelayNotFound {groupRelayId :: Int64}
|
||||
| SEGroupRelayNotFoundByMemberId {groupMemberId :: GroupMemberId}
|
||||
| SEInvalidQuote
|
||||
| SEInvalidMention
|
||||
| SEInvalidDeliveryTask {taskId :: Int64}
|
||||
@@ -658,7 +659,7 @@ type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe Member
|
||||
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe ShortLinkContact) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupMemberRow
|
||||
|
||||
type GroupMemberRow = (Int64, 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) :. (BoolInt, Maybe Int64, Maybe Int64, Maybe RelayStatus, Maybe ShortLinkContact)
|
||||
type GroupMemberRow = (Int64, 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, BoolInt)
|
||||
|
||||
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences)
|
||||
|
||||
@@ -680,7 +681,7 @@ toPreparedGroup = \case
|
||||
_ -> Nothing
|
||||
|
||||
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
|
||||
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs) :. (BI isRel, groupRelayId_, chatRelayId_, relayStatus_, relayLink)) =
|
||||
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs, BI isRel)) =
|
||||
let memberProfile = rowToLocalProfile profileRow
|
||||
memberSettings = GroupMemberSettings {showMessages}
|
||||
blockedByAdmin = maybe False mrsBlocked memberRestriction_
|
||||
@@ -699,9 +700,6 @@ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer,
|
||||
}
|
||||
_ -> Nothing
|
||||
isRelay = BoolDef isRel
|
||||
relayData = case (groupRelayId_, chatRelayId_, relayStatus_) of
|
||||
(Just groupRelayId, Just userChatRelayId, Just relayStatus) -> Just GroupRelay {groupRelayId, userChatRelayId, relayStatus, relayLink}
|
||||
_ -> Nothing
|
||||
in GroupMember {..}
|
||||
|
||||
groupMemberQuery :: Query
|
||||
@@ -712,14 +710,13 @@ groupMemberQuery =
|
||||
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.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.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link,
|
||||
m.is_relay,
|
||||
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.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id
|
||||
LEFT JOIN connections c ON c.group_member_id = m.group_member_id
|
||||
|]
|
||||
|
||||
@@ -738,7 +735,6 @@ toBusinessChatInfo _ = Nothing
|
||||
groupInfoQuery :: Query
|
||||
groupInfoQuery = groupInfoQueryFields <> " " <> groupInfoQueryFrom
|
||||
|
||||
-- membership "member" never references group_relays, therefore `NULL, NULL, NULL, NULL` which avoids extra join
|
||||
groupInfoQueryFields :: Query
|
||||
groupInfoQueryFields =
|
||||
[sql|
|
||||
@@ -757,7 +753,7 @@ groupInfoQueryFields =
|
||||
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
|
||||
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.is_relay, NULL, NULL, NULL, NULL
|
||||
mu.is_relay
|
||||
|]
|
||||
|
||||
groupInfoQueryFrom :: Query
|
||||
|
||||
@@ -962,8 +962,7 @@ data GroupMember = GroupMember
|
||||
createdAt :: UTCTime,
|
||||
updatedAt :: UTCTime,
|
||||
supportChat :: Maybe GroupSupportChat,
|
||||
isRelay :: BoolDef, -- marker for all members that this member is a chat relay
|
||||
relayData :: Maybe GroupRelay -- owner's additional data for a chat relay
|
||||
isRelay :: BoolDef
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
@@ -972,6 +971,7 @@ memberRole' GroupMember {memberRole} = memberRole
|
||||
|
||||
data GroupRelay = GroupRelay
|
||||
{ groupRelayId :: Int64,
|
||||
groupMemberId :: GroupMemberId,
|
||||
userChatRelayId :: Int64, -- ID of configured UserChatRelay
|
||||
relayStatus :: RelayStatus,
|
||||
relayLink :: Maybe ShortLinkContact
|
||||
|
||||
@@ -416,7 +416,7 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
|
||||
CEvtAcceptingBusinessRequest u g -> ttyUser u $ viewAcceptingBusinessRequest g
|
||||
CEvtContactRequestAlreadyAccepted u c -> ttyUser u [ttyFullContact c <> ": sent you a duplicate contact request, but you are already connected, no action needed"]
|
||||
CEvtBusinessRequestAlreadyAccepted u g -> ttyUser u [ttyFullGroup g <> ": sent you a duplicate connection request, but you are already connected, no action needed"]
|
||||
CEvtGroupLinkConnecting u g _ -> ttyUser u [ttyGroup' g <> ": joining the group..."]
|
||||
CEvtGroupLinkConnecting u g m -> ttyUser u $ viewUserJoiningGroup g m
|
||||
CEvtBusinessLinkConnecting u g _ _ -> ttyUser u [ttyGroup' g <> ": joining the group..."]
|
||||
CEvtUnknownMemberCreated u g fwdM um -> ttyUser u [ttyGroup' g <> ": " <> ttyMember fwdM <> " forwarded a message from an unknown member, creating unknown member record " <> ttyMember um]
|
||||
CEvtUnknownMemberBlocked u g byM um -> ttyUser u [ttyGroup' g <> ": " <> ttyMember byM <> " blocked an unknown member, creating unknown member record " <> ttyMember um]
|
||||
@@ -458,7 +458,7 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
|
||||
in ttyUser u [sShow connId <> ": END"]
|
||||
CEvtSubscriptionStatus srv status conns -> [plain $ subStatusStr status <> " " <> show (length conns) <> " connections on server " <> showSMPServer srv]
|
||||
CEvtReceivedGroupInvitation {user = u, groupInfo = g, contact = c, memberRole = r} -> ttyUser u $ viewReceivedGroupInvitation g c r
|
||||
CEvtUserJoinedGroup u g _ -> ttyUser u $ viewUserJoinedGroup g
|
||||
CEvtUserJoinedGroup u g m -> ttyUser u $ viewUserJoinedGroup g m
|
||||
CEvtRelayJoined u g relayMem groupLink relays -> ttyUser u $ viewRelayJoined g relayMem groupLink relays
|
||||
CEvtJoinedGroupMember u g m -> ttyUser u $ viewJoinedGroupMember g m
|
||||
CEvtHostConnected p h -> [plain $ "connected to " <> viewHostEvent p h]
|
||||
@@ -1199,12 +1199,22 @@ viewDirectMessagesProhibited :: MsgDirection -> Contact -> [StyledString]
|
||||
viewDirectMessagesProhibited MDSnd c = ["direct messages to indirect contact " <> ttyContact' c <> " are prohibited"]
|
||||
viewDirectMessagesProhibited MDRcv c = ["received prohibited direct message from indirect contact " <> ttyContact' c <> " (discarded)"]
|
||||
|
||||
viewUserJoinedGroup :: GroupInfo -> [StyledString]
|
||||
viewUserJoinedGroup g@GroupInfo {membership} =
|
||||
case incognitoMembershipProfile g of
|
||||
Just mp -> [ttyGroup' g <> ": you joined the group incognito as " <> incognitoProfile' (fromLocalProfile mp) <> pendingApproval_]
|
||||
Nothing -> [ttyGroup' g <> ": you joined the group" <> pendingApproval_]
|
||||
viewUserJoiningGroup :: GroupInfo -> GroupMember -> [StyledString]
|
||||
viewUserJoiningGroup g m
|
||||
| isRelay' m = [ttyGroup' g <> ": joining the group (connecting to relay)..."]
|
||||
| otherwise = [ttyGroup' g <> ": joining the group..."]
|
||||
|
||||
viewUserJoinedGroup :: GroupInfo -> GroupMember -> [StyledString]
|
||||
viewUserJoinedGroup g@GroupInfo {membership} m
|
||||
| isRelay' membership = [ttyGroup' g <> ": you joined the group as relay"]
|
||||
| otherwise =
|
||||
case incognitoMembershipProfile g of
|
||||
Just mp -> [ttyGroup' g <> ": you joined the group" <> connectedToRelay_ <> " incognito as " <> incognitoProfile' (fromLocalProfile mp) <> pendingApproval_]
|
||||
Nothing -> [ttyGroup' g <> ": you joined the group" <> connectedToRelay_ <> pendingApproval_]
|
||||
where
|
||||
connectedToRelay_
|
||||
| isRelay' m = " (connected to relay)"
|
||||
| otherwise = ""
|
||||
pendingApproval_ = case memberStatus membership of
|
||||
GSMemPendingApproval -> ", pending approval"
|
||||
GSMemPendingReview -> ", connecting to group moderators for admission to group"
|
||||
|
||||
@@ -8215,7 +8215,7 @@ createChannel1Relay gName owner relay = do
|
||||
owner <## "group link:"
|
||||
_ <- getTermLine owner
|
||||
pure (),
|
||||
relay <## ("#" <> gName <> ": you joined the group")
|
||||
relay <## ("#" <> gName <> ": you joined the group as relay")
|
||||
]
|
||||
|
||||
owner ##> ("/show link #" <> gName)
|
||||
@@ -8237,8 +8237,8 @@ memberJoinChannel gName relay shortLink fullLink member = do
|
||||
member <## "ok"
|
||||
concurrentlyN_
|
||||
[ do
|
||||
member <## ("#" <> gName <> ": joining the group...")
|
||||
member <## ("#" <> gName <> ": you joined the group"),
|
||||
member <## ("#" <> gName <> ": joining the group (connecting to relay)...")
|
||||
member <## ("#" <> gName <> ": you joined the group (connected to relay)"),
|
||||
do
|
||||
relay <## (mFullName <> ": accepting request to join group #team...")
|
||||
relay <## ("#" <> gName <> ": " <> mName <> " joined the group")
|
||||
|
||||
Reference in New Issue
Block a user