mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-01 00:56:05 +00:00
wip
This commit is contained in:
@@ -906,6 +906,7 @@ Create group.
|
||||
**Parameters**:
|
||||
- userId: int64
|
||||
- incognito: bool
|
||||
- useRelays: bool
|
||||
- groupProfile: [GroupProfile](./TYPES.md#groupprofile)
|
||||
|
||||
**Syntax**:
|
||||
|
||||
@@ -102,6 +102,7 @@ This file is generated automatically.
|
||||
- [GroupPreference](#grouppreference)
|
||||
- [GroupPreferences](#grouppreferences)
|
||||
- [GroupProfile](#groupprofile)
|
||||
- [GroupRelay](#grouprelay)
|
||||
- [GroupShortLinkData](#groupshortlinkdata)
|
||||
- [GroupSummary](#groupsummary)
|
||||
- [GroupSupportChat](#groupsupportchat)
|
||||
@@ -140,6 +141,7 @@ This file is generated automatically.
|
||||
- [RcvFileStatus](#rcvfilestatus)
|
||||
- [RcvFileTransfer](#rcvfiletransfer)
|
||||
- [RcvGroupEvent](#rcvgroupevent)
|
||||
- [RelayStatus](#relaystatus)
|
||||
- [ReportReason](#reportreason)
|
||||
- [RoleGroupPreference](#rolegrouppreference)
|
||||
- [SMPAgentError](#smpagenterror)
|
||||
@@ -2134,6 +2136,7 @@ MemberSupport:
|
||||
**Record type**:
|
||||
- groupId: int64
|
||||
- useRelays: bool
|
||||
- relayOwnStatus: [RelayStatus](#relaystatus)?
|
||||
- localDisplayName: string
|
||||
- groupProfile: [GroupProfile](#groupprofile)
|
||||
- localAlias: string
|
||||
@@ -2219,6 +2222,7 @@ Known:
|
||||
- updatedAt: UTCTime
|
||||
- supportChat: [GroupSupportChat](#groupsupportchat)?
|
||||
- isChatRelay: bool
|
||||
- relayData: [GroupRelay](#grouprelay)?
|
||||
|
||||
|
||||
---
|
||||
@@ -2329,10 +2333,21 @@ Known:
|
||||
- shortDescr: string?
|
||||
- description: string?
|
||||
- image: string?
|
||||
- groupLink: string?
|
||||
- groupPreferences: [GroupPreferences](#grouppreferences)?
|
||||
- memberAdmission: [GroupMemberAdmission](#groupmemberadmission)?
|
||||
|
||||
|
||||
---
|
||||
|
||||
## GroupRelay
|
||||
|
||||
**Record type**:
|
||||
- groupRelayId: int64
|
||||
- relayStatus: [RelayStatus](#relaystatus)
|
||||
- relayLink: string?
|
||||
|
||||
|
||||
---
|
||||
|
||||
## GroupShortLinkData
|
||||
@@ -3041,6 +3056,17 @@ NewMemberPendingReview:
|
||||
- type: "newMemberPendingReview"
|
||||
|
||||
|
||||
---
|
||||
|
||||
## RelayStatus
|
||||
|
||||
**Enum type**:
|
||||
- "new"
|
||||
- "invited"
|
||||
- "accepted"
|
||||
- "active"
|
||||
|
||||
|
||||
---
|
||||
|
||||
## ReportReason
|
||||
|
||||
@@ -280,6 +280,7 @@ chatTypesDocsData =
|
||||
(sti @GroupPreference, STRecord, "", [], "", ""),
|
||||
(sti @GroupPreferences, STRecord, "", [], "", ""),
|
||||
(sti @GroupProfile, STRecord, "", [], "", ""),
|
||||
(sti @GroupRelay, STRecord, "", [], "", ""),
|
||||
(sti @GroupShortLinkData, STRecord, "", [], "", ""),
|
||||
(sti @GroupSummary, STRecord, "", [], "", ""),
|
||||
(sti @GroupSupportChat, STRecord, "", [], "", ""),
|
||||
@@ -319,6 +320,7 @@ chatTypesDocsData =
|
||||
(sti @RcvFileStatus, STUnion, "RFS", [], "", ""),
|
||||
(sti @RcvFileTransfer, STRecord, "", [], "", ""),
|
||||
(sti @RcvGroupEvent, STUnion, "RGE", [], "", ""),
|
||||
(sti @RelayStatus, STEnum, "RS", [], "", ""),
|
||||
(sti @ReportReason, (STEnum' $ dropPfxSfx "RR" ""), "", ["RRUnknown"], "", ""),
|
||||
(sti @RoleGroupPreference, STRecord, "", [], "", ""),
|
||||
(sti @SecurityCode, STRecord, "", [], "", ""),
|
||||
@@ -466,6 +468,7 @@ deriving instance Generic GroupMemberStatus
|
||||
deriving instance Generic GroupPreference
|
||||
deriving instance Generic GroupPreferences
|
||||
deriving instance Generic GroupProfile
|
||||
deriving instance Generic GroupRelay
|
||||
deriving instance Generic GroupShortLinkData
|
||||
deriving instance Generic GroupSummary
|
||||
deriving instance Generic GroupSupportChat
|
||||
@@ -511,6 +514,7 @@ deriving instance Generic RcvFileDescr
|
||||
deriving instance Generic RcvFileStatus
|
||||
deriving instance Generic RcvFileTransfer
|
||||
deriving instance Generic RcvGroupEvent
|
||||
deriving instance Generic RelayStatus
|
||||
deriving instance Generic ReportReason
|
||||
deriving instance Generic SecurityCode
|
||||
deriving instance Generic SimplexLinkType
|
||||
|
||||
@@ -330,6 +330,7 @@ export namespace APIListMembers {
|
||||
export interface APINewGroup {
|
||||
userId: number // int64
|
||||
incognito: boolean
|
||||
useRelays: boolean
|
||||
groupProfile: T.GroupProfile
|
||||
}
|
||||
|
||||
|
||||
@@ -2421,6 +2421,7 @@ export enum GroupFeatureEnabled {
|
||||
export interface GroupInfo {
|
||||
groupId: number // int64
|
||||
useRelays: boolean
|
||||
relayOwnStatus?: RelayStatus
|
||||
localDisplayName: string
|
||||
groupProfile: GroupProfile
|
||||
localAlias: string
|
||||
@@ -2511,6 +2512,7 @@ export interface GroupMember {
|
||||
updatedAt: string // ISO-8601 timestamp
|
||||
supportChat?: GroupSupportChat
|
||||
isChatRelay: boolean
|
||||
relayData?: GroupRelay
|
||||
}
|
||||
|
||||
export interface GroupMemberAdmission {
|
||||
@@ -2585,10 +2587,17 @@ export interface GroupProfile {
|
||||
shortDescr?: string
|
||||
description?: string
|
||||
image?: string
|
||||
groupLink?: string
|
||||
groupPreferences?: GroupPreferences
|
||||
memberAdmission?: GroupMemberAdmission
|
||||
}
|
||||
|
||||
export interface GroupRelay {
|
||||
groupRelayId: number // int64
|
||||
relayStatus: RelayStatus
|
||||
relayLink?: string
|
||||
}
|
||||
|
||||
export interface GroupShortLinkData {
|
||||
groupProfile: GroupProfile
|
||||
}
|
||||
@@ -3443,6 +3452,13 @@ export namespace RcvGroupEvent {
|
||||
}
|
||||
}
|
||||
|
||||
export enum RelayStatus {
|
||||
New = "new",
|
||||
Invited = "invited",
|
||||
Accepted = "accepted",
|
||||
Active = "active",
|
||||
}
|
||||
|
||||
export enum ReportReason {
|
||||
Spam = "spam",
|
||||
Content = "content",
|
||||
|
||||
@@ -172,6 +172,7 @@ newChatController
|
||||
expireCIThreads <- TM.emptyIO
|
||||
expireCIFlags <- TM.emptyIO
|
||||
cleanupManagerAsync <- newTVarIO Nothing
|
||||
relayChecksAsync <- newTVarIO Nothing
|
||||
timedItemThreads <- TM.emptyIO
|
||||
chatActivated <- newTVarIO True
|
||||
showLiveItems <- newTVarIO False
|
||||
@@ -213,6 +214,7 @@ newChatController
|
||||
expireCIThreads,
|
||||
expireCIFlags,
|
||||
cleanupManagerAsync,
|
||||
relayChecksAsync,
|
||||
timedItemThreads,
|
||||
chatActivated,
|
||||
showLiveItems,
|
||||
|
||||
@@ -250,6 +250,7 @@ data ChatController = ChatController
|
||||
expireCIThreads :: TMap UserId (Maybe (Async ())),
|
||||
expireCIFlags :: TMap UserId Bool,
|
||||
cleanupManagerAsync :: TVar (Maybe (Async ())),
|
||||
relayChecksAsync :: TVar (Maybe (Async ())),
|
||||
chatActivated :: TVar Bool,
|
||||
timedItemThreads :: TMap (ChatRef, ChatItemId) (TVar (Maybe (Weak ThreadId))),
|
||||
showLiveItems :: TVar Bool,
|
||||
@@ -391,7 +392,7 @@ data ChatCommand
|
||||
| TestProtoServer AProtoServerWithAuth
|
||||
| GetUserChatRelays
|
||||
| SetUserChatRelays [CLINewRelay]
|
||||
-- TODO [chat relays] commands to test chat relay
|
||||
-- TODO [relays] commands to test chat relay
|
||||
-- | APITestChatRelay UserId ConnLinkContact
|
||||
-- | TestChatRelay ConnLinkContact
|
||||
| APIGetServerOperators
|
||||
@@ -503,8 +504,8 @@ data ChatCommand
|
||||
| EditMessage {chatName :: ChatName, editedMsg :: Text, message :: Text}
|
||||
| UpdateLiveMessage {chatName :: ChatName, chatItemId :: ChatItemId, liveMessage :: Bool, message :: Text}
|
||||
| ReactToMessage {add :: Bool, reaction :: MsgReaction, chatName :: ChatName, reactToMessage :: Text}
|
||||
| APINewGroup {userId :: UserId, incognito :: IncognitoEnabled, groupProfile :: GroupProfile}
|
||||
| NewGroup IncognitoEnabled GroupProfile
|
||||
| APINewGroup {userId :: UserId, incognito :: IncognitoEnabled, useRelays :: Bool, groupProfile :: GroupProfile}
|
||||
| NewGroup IncognitoEnabled Bool GroupProfile
|
||||
| AddMember GroupName ContactName GroupMemberRole
|
||||
| JoinGroup {groupName :: GroupName, enableNtfs :: MsgFilter}
|
||||
| AcceptMember GroupName ContactName GroupMemberRole
|
||||
|
||||
@@ -210,6 +210,15 @@ startChatController mainApp enableSndFiles = do
|
||||
a <- Just <$> async (void $ runExceptT cleanupManager)
|
||||
atomically $ writeTVar cleanupAsync a
|
||||
_ -> pure ()
|
||||
startRelayChecks users = do
|
||||
let relayUser_ = find (\User {userChatRelay} -> isTrue userChatRelay) users
|
||||
forM_ relayUser_ $ \relayUser -> do
|
||||
relayAsync <- asks relayChecksAsync
|
||||
readTVarIO relayAsync >>= \case
|
||||
Nothing -> do
|
||||
a <- Just <$> async (void $ runExceptT $ runRelayChecks relayUser)
|
||||
atomically $ writeTVar relayAsync a
|
||||
_ -> pure ()
|
||||
startExpireCIs user = whenM shouldExpireChats $ do
|
||||
startExpireCIThread user
|
||||
setExpireCIFlag user True
|
||||
@@ -220,6 +229,28 @@ startChatController mainApp enableSndFiles = do
|
||||
ttlCount <- getChatTTLCount db user
|
||||
pure $ ttl > 0 || ttlCount > 0
|
||||
|
||||
-- startExpireCIThread :: User -> CM' ()
|
||||
-- startExpireCIThread user@User {userId} = do
|
||||
-- expireThreads <- asks expireCIThreads
|
||||
-- atomically (TM.lookup userId expireThreads) >>= \case
|
||||
-- Nothing -> do
|
||||
-- a <- Just <$> async runExpireCIs
|
||||
-- atomically $ TM.insert userId a expireThreads
|
||||
-- _ -> pure ()
|
||||
-- where
|
||||
-- runExpireCIs = do
|
||||
-- delay <- asks (initialCleanupManagerDelay . config)
|
||||
-- liftIO $ threadDelay' delay
|
||||
-- interval <- asks $ ciExpirationInterval . config
|
||||
-- forever $ do
|
||||
-- flip catchAllErrors' (eToView') $ do
|
||||
-- expireFlags <- asks expireCIFlags
|
||||
-- atomically $ TM.lookup userId expireFlags >>= \b -> unless (b == Just True) retry
|
||||
-- lift waitChatStartedAndActivated
|
||||
-- ttl <- withStore' (`getChatItemTTL` user)
|
||||
-- expireChatItems user ttl False
|
||||
-- liftIO $ threadDelay' interval
|
||||
|
||||
getConnsToSub :: User -> CM [ConnId]
|
||||
getConnsToSub user =
|
||||
withFastStore' $ \db -> do
|
||||
@@ -1195,8 +1226,8 @@ processChatCommand vr nm = \case
|
||||
withFastStore' $ \db -> deleteGroup db user gInfo
|
||||
pure $ CRGroupDeletedUser user gInfo
|
||||
where
|
||||
getRecipients gInfo@GroupInfo {useRelays}
|
||||
| isTrue useRelays = do
|
||||
getRecipients gInfo
|
||||
| useRelays' gInfo = do
|
||||
relays <- withFastStore' $ \db -> getGroupRelays db vr user gInfo
|
||||
pure (relays, relays)
|
||||
| otherwise = do
|
||||
@@ -1630,8 +1661,8 @@ processChatCommand vr nm = \case
|
||||
withAgent (\a -> toggleConnectionNtfs a connId $ chatHasNtfs chatSettings) `catchAllErrors` eToView
|
||||
ok user
|
||||
where
|
||||
getMembers db gInfo@GroupInfo {useRelays}
|
||||
| isTrue useRelays = getGroupRelays db vr user gInfo
|
||||
getMembers db gInfo
|
||||
| useRelays' gInfo = getGroupRelays db vr user gInfo
|
||||
| otherwise = getGroupMembers db vr user gInfo
|
||||
_ -> throwCmdError "not supported"
|
||||
APISetMemberSettings gId gMemberId settings -> withUser $ \user -> do
|
||||
@@ -2032,9 +2063,9 @@ processChatCommand vr nm = \case
|
||||
Left e -> throwError $ ChatErrorStore e
|
||||
Right _ -> throwError $ ChatErrorStore SEDuplicateContactLink
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
-- TODO [chat relays] relay address creation:
|
||||
-- TODO - add relay key, identity to link data
|
||||
-- TODO - validate short link is created (returned by agent)
|
||||
-- TODO [relays] relay: address creation
|
||||
-- TODO - add relay key, identity to link data
|
||||
-- TODO - validate short link is created (returned by agent)
|
||||
let userData = contactShortLinkData (userProfileDirect user Nothing Nothing True) Nothing
|
||||
-- TODO [certs rcv]
|
||||
(connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userData) Nothing IKPQOn subMode
|
||||
@@ -2228,19 +2259,26 @@ processChatCommand vr nm = \case
|
||||
chatRef <- getChatRef user chatName
|
||||
chatItemId <- getChatItemIdByText user chatRef msg
|
||||
processChatCommand vr nm $ APIChatItemReaction chatRef chatItemId add reaction
|
||||
APINewGroup userId incognito gProfile@GroupProfile {displayName} -> withUserId userId $ \user -> do
|
||||
APINewGroup userId incognito useRelays gProfile@GroupProfile {displayName} -> withUserId userId $ \user -> do
|
||||
checkValidName displayName
|
||||
gVar <- asks random
|
||||
-- [incognito] generate incognito profile for group membership
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
gInfo <- withFastStore $ \db -> createNewGroup db vr gVar user gProfile incognitoProfile
|
||||
gInfo <- withFastStore $ \db -> createNewGroup db vr gVar user gProfile incognitoProfile useRelays
|
||||
let cd = CDGroupSnd gInfo Nothing
|
||||
createInternalChatItem user cd CIChatBanner (Just epochStart)
|
||||
createInternalChatItem user cd (CISndGroupE2EEInfo E2EInfo {pqEnabled = Just PQEncOff}) Nothing
|
||||
createGroupFeatureItems user cd CISndGroupFeature gInfo
|
||||
-- TODO [relays] owner: create group using relays
|
||||
-- TODO - prepare group link
|
||||
-- TODO - add link, owner key to group profile, sign
|
||||
-- TODO - create group link on server, use signed profile as data
|
||||
-- TODO - choose chat relays: auto or user action (new api in case of latter)
|
||||
-- TODO - send contact requests to relays
|
||||
-- TODO - create relay member connections, relay records, relay status: RSInvited
|
||||
pure $ CRGroupCreated user gInfo
|
||||
NewGroup incognito gProfile -> withUser $ \User {userId} ->
|
||||
processChatCommand vr nm $ APINewGroup userId incognito gProfile
|
||||
NewGroup incognito useRelays gProfile -> withUser $ \User {userId} ->
|
||||
processChatCommand vr nm $ APINewGroup userId incognito useRelays gProfile
|
||||
APIAddMember groupId contactId memRole -> withUser $ \user -> withGroupLock "addMember" groupId $ do
|
||||
-- TODO for large groups: no need to load all members to determine if contact is a member
|
||||
(group, contact) <- withFastStore $ \db -> (,) <$> getGroup db vr user groupId <*> getContact db vr user contactId
|
||||
@@ -2589,8 +2627,8 @@ processChatCommand vr nm = \case
|
||||
withFastStore' $ \db -> updateGroupMemberStatus db userId membership GSMemLeft
|
||||
pure $ CRLeftMemberUser user gInfo' {membership = membership {memberStatus = GSMemLeft}}
|
||||
where
|
||||
getRecipients user gInfo@GroupInfo {useRelays}
|
||||
| isTrue useRelays = do
|
||||
getRecipients user gInfo
|
||||
| useRelays' gInfo = do
|
||||
relays <- withFastStore' $ \db -> getGroupRelays db vr user gInfo
|
||||
pure (relays, relays)
|
||||
| otherwise = do
|
||||
@@ -3371,7 +3409,7 @@ processChatCommand vr nm = \case
|
||||
sendGroupMessage user gInfo' Nothing recipients (XGrpInfo p')
|
||||
where
|
||||
getRecipients
|
||||
| isTrue (useRelays gInfo') = withFastStore' $ \db -> getGroupRelays db vr user gInfo'
|
||||
| useRelays' gInfo' = withFastStore' $ \db -> getGroupRelays db vr user gInfo'
|
||||
| otherwise = do
|
||||
ms <- withFastStore' $ \db -> getGroupMembers db vr user gInfo'
|
||||
pure $ filter memberCurrentOrPending ms
|
||||
@@ -3627,6 +3665,19 @@ processChatCommand vr nm = \case
|
||||
knownLinkPlans >>= \case
|
||||
Just r -> pure r
|
||||
Nothing -> do
|
||||
-- TODO [relays] member: connect to relays
|
||||
-- TODO - get ContactLinkData.relays data
|
||||
-- TODO - see decodeShortLinkData -> linkUserData', retrieve relays in addition to userData
|
||||
-- TODO - if relay list is non-empty, connect to relays
|
||||
-- TODO - or, base on group profile? add useRelays/group type to group link data? (e.g. "channel")
|
||||
-- TODO note:
|
||||
-- TODO this is Connection Plan api - we're not connecting here yet;
|
||||
-- TODO options:
|
||||
-- TODO - save relay links on prepared group record, connect to them in APIConnectPreparedGroup
|
||||
-- TODO - only mark group as `useRelays`, repeat retrieving link data in APIConnectPreparedGroup
|
||||
-- TODO retreiving relays at point of conenctions seems better, as arbitrary time
|
||||
-- TODO can pass between creating prepared group from plan and connecting to it,
|
||||
-- TODO during which relays can change.
|
||||
(cReq, cData) <- getShortLinkConnReq user l'
|
||||
groupSLinkData_ <- liftIO $ decodeShortLinkData cData
|
||||
plan <- groupJoinRequestPlan user cReq groupSLinkData_
|
||||
@@ -4257,6 +4308,16 @@ cleanupManager = do
|
||||
let cutoffTs = addUTCTime (-(14 * nominalDay)) ts
|
||||
withStore' (`deleteOldProbes` cutoffTs)
|
||||
|
||||
runRelayChecks :: User -> CM ()
|
||||
runRelayChecks user = do
|
||||
-- TODO [relays] relay: periodically check served groups
|
||||
-- TODO - get groups where user is chat relay
|
||||
-- TODO - retrive group link data, check presence of relay link
|
||||
-- TODO - if relay link is present, update relay status to RSActive
|
||||
-- TODO - if relay link is absent and staus was RSActive -> update to new "Removed" status?
|
||||
-- TODO - * recovery for joining served group (add relay protocol) - also in this thread?
|
||||
pure ()
|
||||
|
||||
expireChatItems :: User -> Int64 -> Bool -> CM ()
|
||||
expireChatItems user@User {userId} globalTTL sync = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
@@ -4537,8 +4598,8 @@ chatCommandP =
|
||||
("/help settings" <|> "/hs") $> ChatHelp HSSettings,
|
||||
("/help db" <|> "/hd") $> ChatHelp HSDatabase,
|
||||
("/help" <|> "/h") $> ChatHelp HSMain,
|
||||
("/group" <|> "/g") *> (NewGroup <$> incognitoP <* A.space <* char_ '#' <*> groupProfile),
|
||||
"/_group " *> (APINewGroup <$> A.decimal <*> incognitoOnOffP <* A.space <*> jsonP),
|
||||
("/group" <|> "/g") *> (NewGroup <$> incognitoP <*> (" use_relays=" *> onOffP <|> pure False) <* A.space <* char_ '#' <*> groupProfile),
|
||||
"/_group " *> (APINewGroup <$> A.decimal <*> incognitoOnOffP <*> (" use_relays=" *> onOffP <|> pure False) <* A.space <*> jsonP),
|
||||
("/add " <|> "/a ") *> char_ '#' *> (AddMember <$> displayNameP <* A.space <* char_ '@' <*> displayNameP <*> (memberRole <|> pure GRMember)),
|
||||
("/join " <|> "/j ") *> char_ '#' *> (JoinGroup <$> displayNameP <*> (" mute" $> MFNone <|> pure MFAll)),
|
||||
"/accept member " *> char_ '#' *> (AcceptMember <$> displayNameP <* A.space <* char_ '@' <*> displayNameP <*> (memberRole <|> pure GRMember)),
|
||||
@@ -4800,7 +4861,7 @@ chatCommandP =
|
||||
{ directMessages = Just DirectMessagesGroupPreference {enable = FEOn, role = Nothing},
|
||||
history = Just HistoryGroupPreference {enable = FEOn}
|
||||
}
|
||||
pure GroupProfile {displayName = gName, fullName = "", shortDescr, description = Nothing, image = Nothing, groupPreferences, memberAdmission = Nothing}
|
||||
pure GroupProfile {displayName = gName, fullName = "", shortDescr, description = Nothing, image = Nothing, groupLink = Nothing, groupPreferences, memberAdmission = Nothing}
|
||||
memberCriteriaP = ("all" $> Just MCAll) <|> ("off" $> Nothing)
|
||||
shortDescrP = do
|
||||
descr <- A.takeWhile1 isSpace *> (T.dropWhileEnd isSpace <$> textP) <|> pure ""
|
||||
|
||||
@@ -1018,7 +1018,7 @@ acceptBusinessJoinRequestAsync
|
||||
|
||||
businessGroupProfile :: Profile -> GroupPreferences -> GroupProfile
|
||||
businessGroupProfile Profile {displayName, fullName, shortDescr, image} groupPreferences =
|
||||
GroupProfile {displayName, fullName, description = Nothing, shortDescr, image, groupPreferences = Just groupPreferences, memberAdmission = Nothing}
|
||||
GroupProfile {displayName, fullName, description = Nothing, shortDescr, image, groupLink = Nothing, groupPreferences = Just groupPreferences, memberAdmission = Nothing}
|
||||
|
||||
introduceToModerators :: VersionRangeChat -> User -> GroupInfo -> GroupMember -> CM ()
|
||||
introduceToModerators vr user gInfo@GroupInfo {groupId} m@GroupMember {memberRole, memberId} = do
|
||||
@@ -1456,8 +1456,8 @@ getChatScopeInfo vr user = \case
|
||||
pure $ GCSIMemberSupport (Just supportMem)
|
||||
|
||||
getGroupRecipients :: VersionRangeChat -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> VersionChat -> CM [GroupMember]
|
||||
getGroupRecipients vr user gInfo@GroupInfo {useRelays, membership} scopeInfo modsCompatVersion
|
||||
| isTrue useRelays && not (isMemberRelay membership) = do
|
||||
getGroupRecipients vr user gInfo@GroupInfo {membership} scopeInfo modsCompatVersion
|
||||
| useRelays' gInfo && not (isMemberRelay membership) = do
|
||||
unless (memberCurrent membership && memberActive membership) $ throwChatError $ CECommandError "not current member"
|
||||
withFastStore' $ \db -> getGroupRelays db vr user gInfo
|
||||
| otherwise = case scopeInfo of
|
||||
@@ -1994,9 +1994,9 @@ sendGroupMessages_ _user gInfo@GroupInfo {groupId} recipientMembers events = do
|
||||
data MemberSendAction = MSASend Connection | MSASendBatched Connection | MSAPending | MSAForwarded
|
||||
|
||||
memberSendAction :: GroupInfo -> NonEmpty (ChatMsgEvent e) -> [GroupMember] -> GroupMember -> Maybe MemberSendAction
|
||||
memberSendAction GroupInfo {useRelays, membership} events members m@GroupMember {memberRole, memberStatus}
|
||||
memberSendAction gInfo@GroupInfo {membership} events members m@GroupMember {memberRole, memberStatus}
|
||||
-- groups with relays require newer version - we don't need to check member version for batching and forwarding support
|
||||
| isTrue useRelays =
|
||||
| useRelays' gInfo =
|
||||
if
|
||||
-- if user is chat relay, send to all non chat relay members
|
||||
| isMemberRelay membership && not (isMemberRelay m) -> MSASendBatched . snd <$> readyMemberConn m
|
||||
@@ -2109,7 +2109,7 @@ saveGroupRcvMsg user groupId authorMember conn@Connection {connId} agentMsgMeta
|
||||
pure (am', conn', msg)
|
||||
|
||||
saveGroupFwdRcvMsg :: MsgEncodingI e => User -> GroupInfo -> GroupMember -> GroupMember -> MsgBody -> ChatMessage e -> UTCTime -> CM (Maybe RcvMessage)
|
||||
saveGroupFwdRcvMsg user GroupInfo {groupId, useRelays} forwardingMember refAuthorMember@GroupMember {memberId = refMemberId} msgBody ChatMessage {msgId = sharedMsgId_, chatMsgEvent} brokerTs = do
|
||||
saveGroupFwdRcvMsg user gInfo@GroupInfo {groupId} forwardingMember refAuthorMember@GroupMember {memberId = refMemberId} msgBody ChatMessage {msgId = sharedMsgId_, chatMsgEvent} brokerTs = do
|
||||
let newMsg = NewRcvMessage {chatMsgEvent, msgBody, brokerTs}
|
||||
fwdMemberId = Just $ groupMemberId' forwardingMember
|
||||
refAuthorId = Just $ groupMemberId' refAuthorMember
|
||||
@@ -2117,7 +2117,7 @@ saveGroupFwdRcvMsg user GroupInfo {groupId, useRelays} forwardingMember refAutho
|
||||
withStore' (\db -> runExceptT $ createNewRcvMessage db (GroupId groupId) newMsg sharedMsgId_ refAuthorId fwdMemberId) >>= \case
|
||||
Right msg -> pure $ Just msg
|
||||
Left e@SEDuplicateGroupMessage {authorGroupMemberId, forwardedByGroupMemberId}
|
||||
| isTrue useRelays -> pure Nothing -- with chat relays, duplicates are expected
|
||||
| useRelays' gInfo -> pure Nothing -- with chat relays, duplicates are expected
|
||||
| otherwise -> case (authorGroupMemberId, forwardedByGroupMemberId) of
|
||||
(Just authorGMId, Nothing) -> do
|
||||
vr <- chatVersionRange
|
||||
|
||||
@@ -220,7 +220,7 @@ processAgentMsgSndFile _corrId aFileId msg = do
|
||||
toView $ CEvtSndFileCompleteXFTP user ci' ft
|
||||
where
|
||||
getRecipients
|
||||
| isTrue (useRelays g) = withStore' $ \db -> getGroupRelays db vr user g
|
||||
| useRelays' g = withStore' $ \db -> getGroupRelays db vr user g
|
||||
| otherwise = withStore' $ \db -> getGroupMembers db vr user g
|
||||
memberFTs :: [GroupMember] -> [(Connection, SndFileTransfer)]
|
||||
memberFTs ms = M.elems $ M.intersectionWith (,) (M.fromList mConns') (M.fromList sfts')
|
||||
@@ -728,6 +728,15 @@ 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"
|
||||
XGrpRelayAcpt relayLink -> do
|
||||
-- TODO [relays] owner: process relay acceptance
|
||||
-- TODO - * relay is invitee? other processing branch?
|
||||
-- TODO - * check processing client is owner, otherwise error
|
||||
-- TODO - update relay record with relay link, relay status: RSAccepted
|
||||
-- TODO - update group link (add relay link)
|
||||
-- TODO - agent async setConnShortLink api; agent api to allow setting ContactLinkData.relays
|
||||
-- TODO - on group link updated: relay status: RSActive (can share group link with members)
|
||||
pure ()
|
||||
_ -> messageError "CONF from invited member must have x.grp.acpt"
|
||||
GCHostMember ->
|
||||
case chatMsgEvent of
|
||||
@@ -800,6 +809,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
let welcomeMsgId_ = (\PreparedGroup {welcomeSharedMsgId = mId} -> mId) <$> prepared
|
||||
unless (memberPending membership || isJust welcomeMsgId_) $ maybeCreateGroupDescrLocal gInfo'' m''
|
||||
GCInviteeMember -> do
|
||||
-- TODO [relays] relay: don't introduce new member to other members
|
||||
(gInfo', mStatus) <-
|
||||
if not (memberPending m)
|
||||
then do
|
||||
@@ -1140,6 +1150,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
case chatMsgEvent of
|
||||
XContact p xContactId_ welcomeMsgId_ requestMsg_ -> profileContactRequest invId chatVRange p xContactId_ welcomeMsgId_ requestMsg_ pqSupport
|
||||
XInfo p -> profileContactRequest invId chatVRange p Nothing Nothing Nothing pqSupport
|
||||
XGrpRelayInv groupLink -> relayContactRequest groupLink
|
||||
-- TODO show/log error, other events in contact request
|
||||
_ -> pure ()
|
||||
MERR _ err -> do
|
||||
@@ -1308,6 +1319,18 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
| otherwise -> do
|
||||
mem <- acceptGroupJoinSendRejectAsync user uclId gInfo invId chatVRange p xContactId_ rjctReason
|
||||
toViewTE $ TERejectingGroupJoinRequestMember user gInfo mem rjctReason
|
||||
relayContactRequest :: ShortLinkContact -> CM ()
|
||||
relayContactRequest groupLink = do
|
||||
-- TODO [relays] relay: process contact request to server group
|
||||
-- TODO - retrieve group link data, validate group profile, verify owner's signature
|
||||
-- TODO - create group record, relay status: RSInvited
|
||||
-- TODO - create relay link (async)
|
||||
-- TODO - new user contact link referencing this group
|
||||
-- TODO - link data: relay key for group, relay identity (profile, certificate, relay identity key)
|
||||
-- TODO - accept request - send XGrpRelayAcpt to owner (continuation on link created)
|
||||
-- TODO - create owner member connection, relay status: RSAccepted
|
||||
-- TODO - * duplicate requests can be deduplicated by group link
|
||||
pure ()
|
||||
|
||||
memberCanSend ::
|
||||
GroupMember ->
|
||||
@@ -2813,8 +2836,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
|
||||
-- TODO [channels fwd] base on differentiation between groups and channels
|
||||
isUserGrpFwdRelay :: GroupInfo -> Bool
|
||||
isUserGrpFwdRelay GroupInfo {useRelays, membership = membership@GroupMember {memberRole}}
|
||||
| isTrue useRelays = isMemberRelay membership
|
||||
isUserGrpFwdRelay gInfo@GroupInfo {membership = membership@GroupMember {memberRole}}
|
||||
| useRelays' gInfo = isMemberRelay membership
|
||||
| otherwise = memberRole >= GRAdmin
|
||||
|
||||
xGrpLeave :: GroupInfo -> GroupMember -> RcvMessage -> UTCTime -> CM (Maybe DeliveryJobScope)
|
||||
@@ -3095,7 +3118,7 @@ deleteGroupConnections user gInfo waitDelivery = do
|
||||
deleteMembersConnections' user members waitDelivery
|
||||
where
|
||||
getMembers vr
|
||||
| isTrue (useRelays gInfo) = withStore' $ \db -> getGroupRelays db vr user gInfo
|
||||
| useRelays' gInfo = withStore' $ \db -> getGroupRelays db vr user gInfo
|
||||
| otherwise = withStore' $ \db -> getGroupMembers db vr user gInfo
|
||||
|
||||
startDeliveryTaskWorkers :: CM ()
|
||||
@@ -3219,7 +3242,7 @@ runDeliveryJobWorker a deliveryKey Worker {doWork} = do
|
||||
MessageDeliveryJob {jobId, jobScope, singleSenderGMId_, body, cursorGMId_ = startingCursor} = job
|
||||
sendBodyToMembers :: CM ()
|
||||
sendBodyToMembers
|
||||
| isTrue (useRelays gInfo) = -- channel
|
||||
| useRelays' gInfo = -- channel
|
||||
case jobScope of
|
||||
-- there's no member review in channels, so job spec includePending is ignored
|
||||
DJSGroup {} -> do
|
||||
|
||||
@@ -88,7 +88,7 @@ disabledSimplexChatSMPServers =
|
||||
"smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion"
|
||||
]
|
||||
|
||||
-- TODO [chat relays] real chat relays
|
||||
-- TODO [relays] real chat relays
|
||||
simplexChatRelays :: [NewUserChatRelay]
|
||||
simplexChatRelays =
|
||||
[ presetChatRelay True "chat_relay_1" ["simplex.im"] (either error id $ strDecode "https://smp111.simplex.im/r#Pz9qz7ZVljMofoRxiDDpL_w2DZSazK8IgafxqnWKv6Y"),
|
||||
|
||||
@@ -328,8 +328,8 @@ data ChatMsgEvent (e :: MsgEncoding) where
|
||||
XGrpLinkReject :: GroupLinkRejection -> ChatMsgEvent 'Json
|
||||
XGrpLinkMem :: Profile -> ChatMsgEvent 'Json
|
||||
XGrpLinkAcpt :: GroupAcceptance -> GroupMemberRole -> MemberId -> ChatMsgEvent 'Json
|
||||
XGrpRelayInv :: ConnLinkContact -> ChatMsgEvent 'Json
|
||||
XGrpRelayAcpt :: ConnLinkContact -> ChatMsgEvent 'Json
|
||||
XGrpRelayInv :: ShortLinkContact -> ChatMsgEvent 'Json
|
||||
XGrpRelayAcpt :: ShortLinkContact -> ChatMsgEvent 'Json
|
||||
XGrpMemNew :: MemberInfo -> Maybe MsgScope -> ChatMsgEvent 'Json
|
||||
XGrpMemIntro :: MemberInfo -> Maybe MemberRestrictions -> ChatMsgEvent 'Json
|
||||
XGrpMemInv :: MemberId -> IntroInvitation -> ChatMsgEvent 'Json
|
||||
|
||||
@@ -135,26 +135,30 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_link,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.use_relays, g.relay_own_status,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.is_chat_relay, 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}}
|
||||
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_chat_relay, NULL, NULL, NULL,
|
||||
-- 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.is_chat_relay,
|
||||
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.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_chat_relay, r.group_relay_id, r.relay_status, r.relay_link
|
||||
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
|
||||
|
||||
@@ -161,11 +161,11 @@ getNextDeliveryTasks :: DB.Connection -> GroupInfo -> MessageDeliveryTask -> IO
|
||||
getNextDeliveryTasks db gInfo task =
|
||||
getWorkItems "message delivery task" getTaskIds (getMsgDeliveryTask_ db) (markDeliveryTaskFailed_ db)
|
||||
where
|
||||
GroupInfo {groupId, useRelays} = gInfo
|
||||
GroupInfo {groupId} = gInfo
|
||||
MessageDeliveryTask {jobScope, senderGMId} = task
|
||||
getTaskIds :: IO [Int64]
|
||||
getTaskIds
|
||||
| isTrue useRelays =
|
||||
| useRelays' gInfo =
|
||||
map fromOnly
|
||||
<$> DB.query
|
||||
db
|
||||
|
||||
@@ -190,11 +190,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 BoolInt) :. (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)
|
||||
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 RelayStatus, Maybe ShortLinkContact)
|
||||
|
||||
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', Just isChatRelay) :. (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 $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked', isChatRelay) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs))
|
||||
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 isChatRelay, groupRelayId, 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) :. (isChatRelay, groupRelayId, relayStatus, relayLink))
|
||||
toMaybeGroupMember _ _ = Nothing
|
||||
|
||||
createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink
|
||||
@@ -309,8 +309,8 @@ setGroupLinkShortLink db gLnk@GroupLink {userContactLinkId, connLinkContact = CC
|
||||
pure gLnk {connLinkContact = CCLink connFullLink (Just shortLink), shortLinkDataSet = True, shortLinkLargeDataSet = BoolDef True}
|
||||
|
||||
-- | creates completely new group with a single member - the current user
|
||||
createNewGroup :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo
|
||||
createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = ExceptT $ do
|
||||
createNewGroup :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> Bool -> ExceptT StoreError IO GroupInfo
|
||||
createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile useRelays = ExceptT $ do
|
||||
let GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission} = groupProfile
|
||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||
currentTs <- getCurrentTime
|
||||
@@ -326,11 +326,11 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
|
||||
db
|
||||
[sql|
|
||||
INSERT INTO groups
|
||||
(local_display_name, user_id, group_profile_id, enable_ntfs,
|
||||
(use_relays, local_display_name, user_id, group_profile_id, enable_ntfs,
|
||||
created_at, updated_at, chat_ts, user_member_profile_sent_at)
|
||||
VALUES (?,?,?,?,?,?,?,?)
|
||||
VALUES (?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
(ldn, userId, profileId, BI True, currentTs, currentTs, currentTs, currentTs)
|
||||
(BI useRelays, ldn, userId, profileId, BI True, currentTs, currentTs, currentTs, currentTs)
|
||||
insertedRowId db
|
||||
memberId <- liftIO $ encodedRandomBytes gVar 12
|
||||
membership <- createContactMemberInv_ db user groupId Nothing user (MemberIdRole (MemberId memberId) GROwner) GCUserMember GSMemCreator IBUser customUserProfileId currentTs vr
|
||||
@@ -338,7 +338,8 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
|
||||
pure
|
||||
GroupInfo
|
||||
{ groupId,
|
||||
useRelays = BoolDef False,
|
||||
useRelays = BoolDef useRelays,
|
||||
relayOwnStatus = Nothing,
|
||||
localDisplayName = ldn,
|
||||
groupProfile,
|
||||
localAlias = "",
|
||||
@@ -414,6 +415,7 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
|
||||
( GroupInfo
|
||||
{ groupId,
|
||||
useRelays = BoolDef False,
|
||||
relayOwnStatus = Nothing,
|
||||
localDisplayName,
|
||||
groupProfile,
|
||||
localAlias = "",
|
||||
@@ -481,7 +483,8 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe
|
||||
createdAt,
|
||||
updatedAt = createdAt,
|
||||
supportChat = Nothing,
|
||||
isChatRelay = BoolDef False
|
||||
isChatRelay = BoolDef False,
|
||||
relayData = Nothing
|
||||
}
|
||||
where
|
||||
memberChatVRange@(VersionRange minV maxV) = vr
|
||||
@@ -1100,7 +1103,8 @@ createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId,
|
||||
createdAt,
|
||||
updatedAt = createdAt,
|
||||
supportChat = Nothing,
|
||||
isChatRelay = BoolDef False
|
||||
isChatRelay = BoolDef False,
|
||||
relayData = Nothing
|
||||
}
|
||||
where
|
||||
insertMember_ =
|
||||
@@ -1457,14 +1461,14 @@ createNewMember_
|
||||
db
|
||||
[sql|
|
||||
INSERT INTO group_members
|
||||
(group_id, member_id, member_role, member_category, member_status, member_restriction, is_chat_relay, invited_by, invited_by_group_member_id,
|
||||
(group_id, member_id, member_role, member_category, member_status, member_restriction, invited_by, invited_by_group_member_id,
|
||||
user_id, local_display_name, contact_id, contact_profile_id, created_at, updated_at,
|
||||
peer_chat_min_version, peer_chat_max_version)
|
||||
peer_chat_min_version, peer_chat_max_version, is_chat_relay)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (groupId, memberId, memberRole, memberCategory, memberStatus, memRestriction, BI isChatRelay, invitedById, memInvitedByGroupMemberId)
|
||||
( (groupId, memberId, memberRole, memberCategory, memberStatus, memRestriction, invitedById, memInvitedByGroupMemberId)
|
||||
:. (userId, localDisplayName, memberContactId, memberContactProfileId, createdAt, createdAt)
|
||||
:. (minV, maxV)
|
||||
:. (minV, maxV, BI isChatRelay)
|
||||
)
|
||||
groupMemberId <- insertedRowId db
|
||||
pure
|
||||
@@ -1488,7 +1492,8 @@ createNewMember_
|
||||
createdAt,
|
||||
updatedAt = createdAt,
|
||||
supportChat = Nothing,
|
||||
isChatRelay = BoolDef isChatRelay
|
||||
isChatRelay = BoolDef isChatRelay,
|
||||
relayData = Nothing
|
||||
}
|
||||
|
||||
checkGroupMemberHasItems :: DB.Connection -> User -> GroupMember -> IO (Maybe ChatItemId)
|
||||
@@ -1820,14 +1825,14 @@ updateGroupProfileFromMember db user g@GroupInfo {groupId} Profile {displayName
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.preferences, gp.member_admission
|
||||
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.group_link, gp.preferences, gp.member_admission
|
||||
FROM group_profiles gp
|
||||
JOIN groups g ON gp.group_profile_id = g.group_profile_id
|
||||
WHERE g.group_id = ?
|
||||
|]
|
||||
(Only groupId)
|
||||
toGroupProfile (displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission) =
|
||||
GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission}
|
||||
toGroupProfile (displayName, fullName, shortDescr, description, image, groupLink, groupPreferences, memberAdmission) =
|
||||
GroupProfile {displayName, fullName, shortDescr, description, image, groupLink, groupPreferences, memberAdmission}
|
||||
|
||||
getGroupInfo :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO GroupInfo
|
||||
getGroupInfo db vr User {userId, userContactId} groupId = ExceptT $ do
|
||||
|
||||
@@ -675,12 +675,14 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
||||
SELECT i.chat_item_id,
|
||||
-- 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.is_chat_relay, 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,
|
||||
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.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_chat_relay, r.group_relay_id, r.relay_status, r.relay_link
|
||||
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
|
||||
@@ -2999,35 +3001,39 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
||||
i.forwarded_by_group_member_id, i.show_group_as_sender,
|
||||
-- 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.is_chat_relay, 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,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.relay_link,
|
||||
-- quoted ChatItem
|
||||
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
|
||||
-- quoted GroupMember
|
||||
rm.group_member_id, rm.group_id, 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.is_chat_relay, 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,
|
||||
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_chat_relay, rr.group_relay_id, rr.relay_status, rr.relay_link,
|
||||
-- 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.is_chat_relay, 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,
|
||||
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.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_chat_relay, dbr.group_relay_id, dbr.relay_status, dbr.relay_link
|
||||
FROM chat_items i
|
||||
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
|
||||
LEFT JOIN group_members gsm ON gsm.group_member_id = i.group_scope_group_member_id
|
||||
LEFT JOIN contact_profiles gsp ON gsp.contact_profile_id = COALESCE(gsm.member_profile_id, gsm.contact_profile_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)
|
||||
|
||||
@@ -10,7 +10,7 @@ import Database.SQLite.Simple.QQ (sql)
|
||||
-- (TBC usage, e.g. agree to invitations to be relay)
|
||||
-- - group_relays - group owner's list of relays for a group
|
||||
-- - group_relays.relay_link - links for all relays of a group are included in GroupShortLinkData
|
||||
-- - group_relays.relay_status - group owner's status for each relay (GroupRelayStatus)
|
||||
-- - group_relays.relay_status - group owner's status for each relay (RelayStatus)
|
||||
-- - 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
|
||||
@@ -19,8 +19,7 @@ import Database.SQLite.Simple.QQ (sql)
|
||||
-- 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)
|
||||
-- - groups.relay_status_self - indicates for a relay client that it is chat relay for the group (GroupRelayStatus)
|
||||
-- - connections.group_member_id_messaging - secondary connection for a group member in relayed group
|
||||
-- - 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|
|
||||
@@ -43,6 +42,12 @@ CREATE INDEX idx_chat_relays_user_id ON chat_relays(user_id);
|
||||
|
||||
ALTER TABLE users ADD COLUMN is_user_chat_relay INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE groups ADD COLUMN use_relays INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE groups ADD COLUMN relay_own_status TEXT;
|
||||
|
||||
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,
|
||||
@@ -57,11 +62,6 @@ ALTER TABLE group_members ADD COLUMN is_chat_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);
|
||||
|
||||
ALTER TABLE groups ADD COLUMN relay_status_self TEXT;
|
||||
|
||||
ALTER TABLE connections ADD COLUMN group_member_id_messaging INTEGER REFERENCES group_members ON DELETE CASCADE;
|
||||
CREATE INDEX idx_connections_group_member_id_messaging ON connections(group_member_id_messaging);
|
||||
|]
|
||||
|
||||
down_m20251018_chat_relays :: Query
|
||||
@@ -72,6 +72,12 @@ DROP TABLE chat_relays;
|
||||
|
||||
ALTER TABLE users DROP COLUMN is_user_chat_relay;
|
||||
|
||||
ALTER TABLE groups DROP COLUMN use_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_chat_relay_id;
|
||||
DROP TABLE group_relays;
|
||||
@@ -80,9 +86,4 @@ ALTER TABLE group_members DROP COLUMN is_chat_relay;
|
||||
|
||||
DROP INDEX idx_group_members_group_relay_id;
|
||||
ALTER TABLE group_members DROP COLUMN group_relay_id;
|
||||
|
||||
ALTER TABLE groups DROP COLUMN relay_status_self;
|
||||
|
||||
DROP INDEX idx_connections_group_member_id_messaging;
|
||||
ALTER TABLE connections DROP COLUMN group_member_id_messaging;
|
||||
|]
|
||||
|
||||
@@ -106,26 +106,30 @@ SEARCH c USING INDEX idx_connections_contact_id (contact_id=?) LEFT-JOIN
|
||||
Query:
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_link,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.use_relays, g.relay_own_status,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.is_chat_relay, 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}}
|
||||
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_chat_relay, NULL, NULL, NULL,
|
||||
-- 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.is_chat_relay,
|
||||
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.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_chat_relay, r.group_relay_id, r.relay_status, r.relay_link
|
||||
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
|
||||
@@ -137,6 +141,7 @@ Plan:
|
||||
SEARCH m USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH g USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH gp USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH mu USING INDEX idx_group_members_contact_id (contact_id=?)
|
||||
SEARCH pu USING INTEGER PRIMARY KEY (rowid=?)
|
||||
@@ -817,7 +822,7 @@ Plan:
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_next (group_id=? AND worker_scope=? AND failed=? AND task_status=?)
|
||||
|
||||
Query:
|
||||
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.preferences, gp.member_admission
|
||||
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.group_link, gp.preferences, gp.member_admission
|
||||
FROM group_profiles gp
|
||||
JOIN groups g ON gp.group_profile_id = g.group_profile_id
|
||||
WHERE g.group_id = ?
|
||||
@@ -845,12 +850,14 @@ Query:
|
||||
SELECT i.chat_item_id,
|
||||
-- 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.is_chat_relay, 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,
|
||||
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.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_chat_relay, r.group_relay_id, r.relay_status, r.relay_link
|
||||
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
|
||||
@@ -861,6 +868,7 @@ Query:
|
||||
Plan:
|
||||
SEARCH m USING INDEX sqlite_autoindex_group_members_1 (group_id=? AND member_id=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH i USING COVERING INDEX idx_chat_items_group_shared_msg_id (user_id=? AND group_id=? AND group_member_id=? AND shared_msg_id=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
@@ -969,6 +977,7 @@ Query:
|
||||
RETURNING chat_relay_id
|
||||
|
||||
Plan:
|
||||
SEARCH group_relays USING COVERING INDEX idx_group_relays_chat_relay_id (chat_relay_id=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO group_members
|
||||
@@ -1015,9 +1024,9 @@ Plan:
|
||||
|
||||
Query:
|
||||
INSERT INTO groups
|
||||
(local_display_name, user_id, group_profile_id, enable_ntfs,
|
||||
(use_relays, local_display_name, user_id, group_profile_id, enable_ntfs,
|
||||
created_at, updated_at, chat_ts, user_member_profile_sent_at)
|
||||
VALUES (?,?,?,?,?,?,?,?)
|
||||
VALUES (?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -1073,48 +1082,54 @@ Query:
|
||||
i.forwarded_by_group_member_id, i.show_group_as_sender,
|
||||
-- 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.is_chat_relay, 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,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.relay_link,
|
||||
-- quoted ChatItem
|
||||
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
|
||||
-- quoted GroupMember
|
||||
rm.group_member_id, rm.group_id, 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.is_chat_relay, 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,
|
||||
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_chat_relay, rr.group_relay_id, rr.relay_status, rr.relay_link,
|
||||
-- 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.is_chat_relay, 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,
|
||||
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.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_chat_relay, dbr.group_relay_id, dbr.relay_status, dbr.relay_link
|
||||
FROM chat_items i
|
||||
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
|
||||
LEFT JOIN group_members gsm ON gsm.group_member_id = i.group_scope_group_member_id
|
||||
LEFT JOIN contact_profiles gsp ON gsp.contact_profile_id = COALESCE(gsm.member_profile_id, gsm.contact_profile_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 = ?
|
||||
|
||||
Plan:
|
||||
SEARCH i USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH f USING INDEX idx_files_chat_item_id (chat_item_id=?) LEFT-JOIN
|
||||
SEARCH gsm USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH m USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH ri USING INDEX idx_chat_items_group_id_shared_msg_id (group_id=? AND shared_msg_id=?) LEFT-JOIN
|
||||
SEARCH rm USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH rp USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH rr USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH dbm USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH dbp USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH dbr USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
@@ -1653,9 +1668,9 @@ SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (conta
|
||||
|
||||
Query:
|
||||
INSERT INTO group_members
|
||||
(group_id, member_id, member_role, member_category, member_status, member_restriction, is_chat_relay, invited_by, invited_by_group_member_id,
|
||||
(group_id, member_id, member_role, member_category, member_status, member_restriction, invited_by, invited_by_group_member_id,
|
||||
user_id, local_display_name, contact_id, contact_profile_id, created_at, updated_at,
|
||||
peer_chat_min_version, peer_chat_max_version)
|
||||
peer_chat_min_version, peer_chat_max_version, is_chat_relay)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
@@ -4979,18 +4994,20 @@ SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_me
|
||||
Query:
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_link,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.use_relays, g.relay_own_status,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.is_chat_relay, 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,
|
||||
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.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_chat_relay, NULL, NULL, NULL
|
||||
|
||||
FROM groups g
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
@@ -5013,18 +5030,20 @@ SEARCH pu USING INTEGER PRIMARY KEY (rowid=?)
|
||||
Query:
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_link,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.use_relays, g.relay_own_status,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.is_chat_relay, 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,
|
||||
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.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_chat_relay, NULL, NULL, NULL
|
||||
|
||||
FROM groups g
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
@@ -5040,18 +5059,20 @@ SEARCH pu USING INTEGER PRIMARY KEY (rowid=?)
|
||||
Query:
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_link,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.use_relays, g.relay_own_status,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.is_chat_relay, 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,
|
||||
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.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_chat_relay, NULL, NULL, NULL
|
||||
|
||||
FROM groups g
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
@@ -5096,16 +5117,18 @@ SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
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.is_chat_relay,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.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.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
|
||||
|
||||
WHERE m.group_id = ? AND m.user_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?)
|
||||
@@ -5119,120 +5142,139 @@ SEARCH m USING INDEX idx_group_members_group_id (user_id=? AND group_id=?)
|
||||
LIST SUBQUERY 1
|
||||
SCAN chat_items USING COVERING INDEX idx_chat_items_group_member_id
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
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.is_chat_relay,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.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.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
|
||||
WHERE m.group_id = ? AND m.group_member_id = ? AND m.user_id = ?
|
||||
Plan:
|
||||
SEARCH m USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
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.is_chat_relay,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.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.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
|
||||
WHERE m.group_id = ? AND m.member_category = ?
|
||||
Plan:
|
||||
SEARCH m USING INDEX sqlite_autoindex_group_members_1 (group_id=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
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.is_chat_relay,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.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.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
|
||||
WHERE m.group_id = ? AND m.member_id = ?
|
||||
Plan:
|
||||
SEARCH m USING INDEX sqlite_autoindex_group_members_1 (group_id=? AND member_id=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
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.is_chat_relay,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.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.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
|
||||
WHERE m.group_member_id = ? AND m.user_id = ?
|
||||
Plan:
|
||||
SEARCH m USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
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.is_chat_relay,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.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.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
|
||||
WHERE m.user_id = ? AND m.group_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?)
|
||||
Plan:
|
||||
SEARCH m USING INDEX idx_group_members_group_id (user_id=? AND group_id=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
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.is_chat_relay,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.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.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
|
||||
WHERE m.user_id = ? AND m.group_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?) AND m.member_role IN (?,?,?)
|
||||
Plan:
|
||||
SEARCH m USING INDEX idx_group_members_group_id (user_id=? AND group_id=?)
|
||||
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH r USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
SEARCH c USING INDEX idx_connections_group_member_id (group_member_id=?) LEFT-JOIN
|
||||
|
||||
Query:
|
||||
@@ -5678,6 +5720,7 @@ SEARCH groups USING COVERING INDEX idx_groups_chat_item_id (chat_item_id=?)
|
||||
Query: DELETE FROM chat_relays WHERE user_id = ? AND chat_relay_id = ? AND preset = ?
|
||||
Plan:
|
||||
SEARCH chat_relays USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH group_relays USING COVERING INDEX idx_group_relays_chat_relay_id (chat_relay_id=?)
|
||||
|
||||
Query: DELETE FROM commands WHERE user_id = ? AND command_id = ?
|
||||
Plan:
|
||||
@@ -5831,6 +5874,7 @@ SEARCH contacts USING COVERING INDEX idx_contacts_contact_group_member_id (conta
|
||||
Query: DELETE FROM groups WHERE user_id = ? AND group_id = ?
|
||||
Plan:
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH group_relays USING COVERING INDEX idx_group_relays_group_id (group_id=?)
|
||||
SEARCH delivery_jobs USING COVERING INDEX idx_delivery_jobs_group_id (group_id=?)
|
||||
SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_group_id (group_id=?)
|
||||
SEARCH chat_item_mentions USING COVERING INDEX idx_chat_item_mentions_group_id (group_id=?)
|
||||
@@ -6216,7 +6260,7 @@ SEARCH chat_items USING INTEGER PRIMARY KEY (rowid>?)
|
||||
|
||||
Query: SELECT count(1) FROM group_members
|
||||
Plan:
|
||||
SCAN group_members USING COVERING INDEX idx_group_members_invited_by_group_member_id
|
||||
SCAN group_members USING COVERING INDEX idx_group_members_group_relay_id
|
||||
|
||||
Query: SELECT count(1) FROM pending_group_messages
|
||||
Plan:
|
||||
|
||||
@@ -122,7 +122,8 @@ CREATE TABLE group_profiles(
|
||||
preferences TEXT,
|
||||
description TEXT NULL,
|
||||
member_admission TEXT,
|
||||
short_descr TEXT
|
||||
short_descr TEXT,
|
||||
group_link BLOB
|
||||
);
|
||||
CREATE TABLE groups(
|
||||
group_id INTEGER PRIMARY KEY, -- local group ID
|
||||
@@ -156,7 +157,9 @@ CREATE TABLE groups(
|
||||
request_shared_msg_id BLOB,
|
||||
conn_link_prepared_connection INTEGER NOT NULL DEFAULT 0,
|
||||
via_group_link_uri BLOB,
|
||||
summary_current_members_count INTEGER NOT NULL DEFAULT 0, -- received
|
||||
summary_current_members_count INTEGER NOT NULL DEFAULT 0,
|
||||
use_relays INTEGER NOT NULL DEFAULT 0,
|
||||
relay_own_status TEXT, -- received
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
@@ -197,6 +200,7 @@ CREATE TABLE group_members(
|
||||
member_xcontact_id BLOB,
|
||||
member_welcome_shared_msg_id BLOB,
|
||||
is_chat_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
|
||||
@@ -732,11 +736,19 @@ CREATE TABLE chat_relays(
|
||||
tested INTEGER,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
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)
|
||||
);
|
||||
CREATE TABLE group_relays(
|
||||
group_relay_id INTEGER PRIMARY KEY,
|
||||
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
|
||||
chat_relay_id INTEGER NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
|
||||
relay_status TEXT NOT NULL,
|
||||
relay_link BLOB
|
||||
);
|
||||
CREATE INDEX contact_profiles_index ON contact_profiles(
|
||||
display_name,
|
||||
full_name
|
||||
@@ -1201,6 +1213,9 @@ CREATE INDEX idx_connections_to_subscribe ON connections(
|
||||
to_subscribe
|
||||
);
|
||||
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 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
|
||||
|
||||
@@ -654,22 +654,22 @@ type PreparedGroupRow = (Maybe ConnReqContact, Maybe ShortLinkContact, BoolInt,
|
||||
|
||||
type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe MemberId)
|
||||
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Int64, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupMemberRow
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact) :. (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, BoolInt) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime)
|
||||
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 RelayStatus, Maybe ShortLinkContact)
|
||||
|
||||
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences)
|
||||
|
||||
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
|
||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image) :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (uiThemes, currentMembers, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri) :. userMemberRow) =
|
||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupLink) :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri) :. userMemberRow) =
|
||||
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||
groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission}
|
||||
groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission, groupLink}
|
||||
businessChat = toBusinessChatInfo businessRow
|
||||
preparedGroup = toPreparedGroup preparedGroupRow
|
||||
groupSummary = GroupSummary {currentMembers}
|
||||
in GroupInfo {groupId, useRelays = BoolDef False, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, preparedGroup, chatTags, chatItemTTL, uiThemes, groupSummary, customData, membersRequireAttention, viaGroupLinkUri}
|
||||
in GroupInfo {groupId, useRelays = BoolDef useRelays, relayOwnStatus, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, preparedGroup, chatTags, chatItemTTL, uiThemes, groupSummary, customData, membersRequireAttention, viaGroupLinkUri}
|
||||
|
||||
toPreparedGroup :: PreparedGroupRow -> Maybe PreparedGroup
|
||||
toPreparedGroup = \case
|
||||
@@ -678,14 +678,13 @@ toPreparedGroup = \case
|
||||
_ -> Nothing
|
||||
|
||||
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
|
||||
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_, BI isCRelay) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs)) =
|
||||
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 isCRelay, groupRelayId_, relayStatus_, relayLink)) =
|
||||
let memberProfile = rowToLocalProfile profileRow
|
||||
memberSettings = GroupMemberSettings {showMessages}
|
||||
blockedByAdmin = maybe False mrsBlocked memberRestriction_
|
||||
invitedBy = toInvitedBy userContactId invitedById
|
||||
activeConn = Nothing
|
||||
memberChatVRange = fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
|
||||
isChatRelay = BoolDef isCRelay
|
||||
supportChat = case supportChatTs_ of
|
||||
Just chatTs ->
|
||||
Just
|
||||
@@ -697,22 +696,28 @@ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer,
|
||||
lastMsgFromMemberTs = supportChatLastMsgFromMemberTs
|
||||
}
|
||||
_ -> Nothing
|
||||
isChatRelay = BoolDef isCRelay
|
||||
relayData = case (groupRelayId_, relayStatus_) of
|
||||
(Just groupRelayId, Just relayStatus) -> Just GroupRelay {groupRelayId, relayStatus, relayLink}
|
||||
_ -> Nothing
|
||||
in GroupMember {..}
|
||||
|
||||
groupMemberQuery :: Query
|
||||
groupMemberQuery =
|
||||
[sql|
|
||||
SELECT
|
||||
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.is_chat_relay,
|
||||
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_chat_relay, r.group_relay_id, r.relay_status, r.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.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
|
||||
|]
|
||||
|
||||
@@ -731,23 +736,26 @@ toBusinessChatInfo _ = Nothing
|
||||
groupInfoQuery :: Query
|
||||
groupInfoQuery = groupInfoQueryFields <> " " <> groupInfoQueryFrom
|
||||
|
||||
-- membership "member" never references group_relays, therefore `NULL, NULL, NULL` which avoids extra join
|
||||
groupInfoQueryFields :: Query
|
||||
groupInfoQueryFields =
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_link,
|
||||
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
|
||||
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
|
||||
g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.use_relays, g.relay_own_status,
|
||||
g.ui_themes, g.summary_current_members_count, g.custom_data, g.chat_item_ttl, g.members_require_attention, g.via_group_link_uri,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.is_chat_relay, 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,
|
||||
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.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_chat_relay, NULL, NULL, NULL
|
||||
|]
|
||||
|
||||
groupInfoQueryFrom :: Query
|
||||
|
||||
@@ -118,7 +118,7 @@ instance ToField AgentUserId where toField (AgentUserId uId) = toField uId
|
||||
aUserId :: User -> UserId
|
||||
aUserId User {agentUserId = AgentUserId uId} = uId
|
||||
|
||||
-- TODO [chat relays] filter out chat relay users where necessary (e.g. loading list of users for UI)
|
||||
-- TODO [relays] filter out chat relay users where necessary (e.g. loading list of users for UI)
|
||||
data User = User
|
||||
{ userId :: UserId,
|
||||
agentUserId :: AgentUserId,
|
||||
@@ -449,6 +449,7 @@ type GroupId = Int64
|
||||
data GroupInfo = GroupInfo
|
||||
{ groupId :: GroupId,
|
||||
useRelays :: BoolDef,
|
||||
relayOwnStatus :: Maybe RelayStatus,
|
||||
localDisplayName :: GroupName,
|
||||
groupProfile :: GroupProfile,
|
||||
localAlias :: Text,
|
||||
@@ -467,11 +468,13 @@ data GroupInfo = GroupInfo
|
||||
customData :: Maybe CustomData,
|
||||
groupSummary :: GroupSummary,
|
||||
membersRequireAttention :: Int,
|
||||
viaGroupLinkUri :: Maybe ConnReqContact,
|
||||
relayOwnStatus :: Maybe GroupRelayStatus
|
||||
viaGroupLinkUri :: Maybe ConnReqContact
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
useRelays' :: GroupInfo -> Bool
|
||||
useRelays' GroupInfo {useRelays} = isTrue useRelays
|
||||
|
||||
data BusinessChatType
|
||||
= BCBusiness -- used on the customer side
|
||||
| BCCustomer -- used on the business side
|
||||
@@ -732,9 +735,9 @@ data GroupProfile = GroupProfile
|
||||
shortDescr :: Maybe Text, -- short description limited to 160 characters
|
||||
description :: Maybe Text, -- this has been repurposed as welcome message
|
||||
image :: Maybe ImageData,
|
||||
groupLink :: Maybe ConnLinkContact,
|
||||
groupPreferences :: Maybe GroupPreferences,
|
||||
memberAdmission :: Maybe GroupMemberAdmission,
|
||||
groupLink :: Maybe ConnLinkContact
|
||||
memberAdmission :: Maybe GroupMemberAdmission
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
@@ -963,24 +966,37 @@ data GroupMember = GroupMember
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
-- TODO [chat relays] review; consider where to use it:
|
||||
-- TODO - GroupMember? (now)
|
||||
-- TODO - separate list of relays in GroupInfo?
|
||||
-- TODO - only on request?
|
||||
data GroupRelay = GroupRelay
|
||||
{ groupRelayId :: Int64,
|
||||
relayStatus :: GroupRelayStatus,
|
||||
relayLink :: ConnLinkContact
|
||||
relayStatus :: RelayStatus,
|
||||
relayLink :: Maybe ShortLinkContact
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
data GroupRelayStatus
|
||||
= GRSNew -- only for owner
|
||||
| GRSInvited
|
||||
| GRSAccepted
|
||||
| GRSActive
|
||||
data RelayStatus
|
||||
= RSNew -- only for owner
|
||||
| RSInvited
|
||||
| RSAccepted
|
||||
| RSActive
|
||||
deriving (Eq, Show)
|
||||
|
||||
instance TextEncoding RelayStatus where
|
||||
textEncode = \case
|
||||
RSNew -> "new"
|
||||
RSInvited -> "invited"
|
||||
RSAccepted -> "accepted"
|
||||
RSActive -> "active"
|
||||
textDecode = \case
|
||||
"new" -> Just RSNew
|
||||
"invited" -> Just RSInvited
|
||||
"accepted" -> Just RSAccepted
|
||||
"active" -> Just RSActive
|
||||
_ -> Nothing
|
||||
|
||||
instance FromField RelayStatus where fromField = fromTextField_ textDecode
|
||||
|
||||
instance ToField RelayStatus where toField = toField . textEncode
|
||||
|
||||
data GroupSupportChat = GroupSupportChat
|
||||
{ chatTs :: UTCTime,
|
||||
unread :: Int64,
|
||||
@@ -2016,7 +2032,7 @@ $(JQ.deriveJSON defaultJSON ''PendingContactConnection)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupSupportChat)
|
||||
|
||||
$(JQ.deriveJSON (enumJSON $ dropPrefix "GRS") ''GroupRelayStatus)
|
||||
$(JQ.deriveJSON (enumJSON $ dropPrefix "RS") ''RelayStatus)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupRelay)
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ testProfile :: Profile
|
||||
testProfile = Profile {displayName = "alice", fullName = "Alice", shortDescr = Nothing, image = Just (ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII="), peerType = Nothing, contactLink = Nothing, preferences = testChatPreferences}
|
||||
|
||||
testGroupProfile :: GroupProfile
|
||||
testGroupProfile = GroupProfile {displayName = "team", fullName = "Team", description = Nothing, shortDescr = Nothing, image = Nothing, groupPreferences = testGroupPreferences, memberAdmission = Nothing}
|
||||
testGroupProfile = GroupProfile {displayName = "team", fullName = "Team", description = Nothing, shortDescr = Nothing, image = Nothing, groupLink = Nothing, groupPreferences = testGroupPreferences, memberAdmission = Nothing}
|
||||
|
||||
decodeChatMessageTest :: Spec
|
||||
decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
|
||||
|
||||
Reference in New Issue
Block a user