mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-15 06:50:19 +00:00
core: member requires attention counter (#5881)
This commit is contained in:
@@ -727,6 +727,7 @@ enum ChatResponse1: Decodable, ChatAPIResult {
|
||||
case contactDeleted(user: UserRef, contact: Contact)
|
||||
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
|
||||
case groupDeletedUser(user: UserRef, groupInfo: GroupInfo)
|
||||
case itemsReadForChat(user: UserRef, chatInfo: ChatInfo)
|
||||
case chatCleared(user: UserRef, chatInfo: ChatInfo)
|
||||
case userProfileNoChange(user: User)
|
||||
case userProfileUpdated(user: User, fromProfile: Profile, toProfile: Profile, updateSummary: UserProfileUpdateSummary)
|
||||
@@ -765,6 +766,7 @@ enum ChatResponse1: Decodable, ChatAPIResult {
|
||||
case .contactDeleted: "contactDeleted"
|
||||
case .contactConnectionDeleted: "contactConnectionDeleted"
|
||||
case .groupDeletedUser: "groupDeletedUser"
|
||||
case .itemsReadForChat: "itemsReadForChat"
|
||||
case .chatCleared: "chatCleared"
|
||||
case .userProfileNoChange: "userProfileNoChange"
|
||||
case .userProfileUpdated: "userProfileUpdated"
|
||||
@@ -797,6 +799,7 @@ enum ChatResponse1: Decodable, ChatAPIResult {
|
||||
case let .contactDeleted(u, contact): return withUser(u, String(describing: contact))
|
||||
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
|
||||
case let .groupDeletedUser(u, groupInfo): return withUser(u, String(describing: groupInfo))
|
||||
case let .itemsReadForChat(u, chatInfo): return withUser(u, String(describing: chatInfo))
|
||||
case let .chatCleared(u, chatInfo): return withUser(u, String(describing: chatInfo))
|
||||
case .userProfileNoChange: return noDetails
|
||||
case let .userProfileUpdated(u, _, toProfile, _): return withUser(u, String(describing: toProfile))
|
||||
|
||||
@@ -564,6 +564,8 @@ final class ChatModel: ObservableObject {
|
||||
// }
|
||||
|
||||
func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
|
||||
// updates membersRequireAttention
|
||||
updateChatInfo(cInfo)
|
||||
// mark chat non deleted
|
||||
if case let .direct(contact) = cInfo, contact.chatDeleted {
|
||||
var updatedContact = contact
|
||||
@@ -1034,23 +1036,6 @@ final class ChatModel: ObservableObject {
|
||||
return unread
|
||||
}
|
||||
|
||||
func increaseGroupSupportChatsUnreadCounter(_ chatId: ChatId) {
|
||||
changeGroupSupportChatsUnreadCounter(chatId, 1)
|
||||
}
|
||||
|
||||
func decreaseGroupSupportChatsUnreadCounter(_ chatId: ChatId, by: Int = 1) {
|
||||
changeGroupSupportChatsUnreadCounter(chatId, -by)
|
||||
}
|
||||
|
||||
private func changeGroupSupportChatsUnreadCounter(_ chatId: ChatId, _ by: Int = 0) {
|
||||
if by == 0 { return }
|
||||
|
||||
if let i = getChatIndex(chatId) {
|
||||
let chat = chats[i]
|
||||
chat.chatStats.supportChatsUnreadCount = max(0, chat.chatStats.supportChatsUnreadCount + by)
|
||||
}
|
||||
}
|
||||
|
||||
func increaseGroupReportsCounter(_ chatId: ChatId) {
|
||||
changeGroupReportsCounter(chatId, 1)
|
||||
}
|
||||
@@ -1296,6 +1281,18 @@ final class Chat: ObservableObject, Identifiable, ChatLike {
|
||||
|
||||
var viewId: String { get { "\(chatInfo.id) \(created.timeIntervalSince1970)" } }
|
||||
|
||||
var supportUnreadCount: Int {
|
||||
switch chatInfo {
|
||||
case let .group(groupInfo, _):
|
||||
if groupInfo.canModerate {
|
||||
return groupInfo.membersRequireAttention
|
||||
} else {
|
||||
return groupInfo.membership.supportChat?.unread ?? 0
|
||||
}
|
||||
default: return 0
|
||||
}
|
||||
}
|
||||
|
||||
public static var sampleData: Chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
|
||||
}
|
||||
|
||||
|
||||
@@ -1252,8 +1252,10 @@ func apiChatRead(type: ChatType, id: Int64, scope: GroupChatScope?) async throws
|
||||
try await sendCommandOkResp(.apiChatRead(type: type, id: id, scope: scope))
|
||||
}
|
||||
|
||||
func apiChatItemsRead(type: ChatType, id: Int64, scope: GroupChatScope?, itemIds: [Int64]) async throws {
|
||||
try await sendCommandOkResp(.apiChatItemsRead(type: type, id: id, scope: scope, itemIds: itemIds))
|
||||
func apiChatItemsRead(type: ChatType, id: Int64, scope: GroupChatScope?, itemIds: [Int64]) async throws -> ChatInfo {
|
||||
let r: ChatResponse1 = try await chatSendCmd(.apiChatItemsRead(type: type, id: id, scope: scope, itemIds: itemIds))
|
||||
if case let .itemsReadForChat(_, updatedChatInfo) = r { return updatedChatInfo }
|
||||
throw r.unexpected
|
||||
}
|
||||
|
||||
func apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) async throws {
|
||||
@@ -1587,8 +1589,9 @@ func markChatUnread(_ chat: Chat, unreadChat: Bool = true) async {
|
||||
|
||||
func apiMarkChatItemsRead(_ im: ItemsModel, _ cInfo: ChatInfo, _ itemIds: [ChatItem.ID], mentionsRead: Int) async {
|
||||
do {
|
||||
try await apiChatItemsRead(type: cInfo.chatType, id: cInfo.apiId, scope: cInfo.groupChatScope(), itemIds: itemIds)
|
||||
let updatedChatInfo = try await apiChatItemsRead(type: cInfo.chatType, id: cInfo.apiId, scope: cInfo.groupChatScope(), itemIds: itemIds)
|
||||
await MainActor.run {
|
||||
ChatModel.shared.updateChatInfo(updatedChatInfo)
|
||||
ChatModel.shared.markChatItemsRead(im, cInfo, itemIds, mentionsRead)
|
||||
}
|
||||
} catch {
|
||||
@@ -1637,9 +1640,9 @@ func apiJoinGroup(_ groupId: Int64) async throws -> JoinGroupResult {
|
||||
}
|
||||
}
|
||||
|
||||
func apiAcceptMember(_ groupId: Int64, _ groupMemberId: Int64, _ memberRole: GroupMemberRole) async throws -> GroupMember {
|
||||
func apiAcceptMember(_ groupId: Int64, _ groupMemberId: Int64, _ memberRole: GroupMemberRole) async throws -> (GroupInfo, GroupMember) {
|
||||
let r: ChatResponse2 = try await chatSendCmd(.apiAcceptMember(groupId: groupId, groupMemberId: groupMemberId, memberRole: memberRole))
|
||||
if case let .memberAccepted(_, _, member) = r { return member }
|
||||
if case let .memberAccepted(_, groupInfo, member) = r { return (groupInfo, member) }
|
||||
throw r.unexpected
|
||||
}
|
||||
|
||||
@@ -2131,9 +2134,6 @@ func processReceivedMsg(_ res: ChatEvent) async {
|
||||
if cItem.isActiveReport {
|
||||
m.increaseGroupReportsCounter(cInfo.id)
|
||||
}
|
||||
if cInfo.groupChatScope() != nil && cItem.isRcvNew && cInfo.ntfsEnabled(chatItem: cItem) {
|
||||
m.increaseGroupSupportChatsUnreadCounter(cInfo.id)
|
||||
}
|
||||
} else if cItem.isRcvNew && cInfo.ntfsEnabled(chatItem: cItem) {
|
||||
m.increaseUnreadCounter(user: user)
|
||||
}
|
||||
@@ -2252,6 +2252,7 @@ func processReceivedMsg(_ res: ChatEvent) async {
|
||||
if active(user) {
|
||||
await MainActor.run {
|
||||
_ = m.upsertGroupMember(groupInfo, member)
|
||||
m.updateGroup(groupInfo)
|
||||
}
|
||||
}
|
||||
case let .deletedMemberUser(user, groupInfo, member, withMessages): // TODO update user member
|
||||
|
||||
@@ -35,7 +35,7 @@ struct ChatInfoToolbar: View {
|
||||
.frame(width: 14, height: 14)
|
||||
.symbolRenderingMode(.palette)
|
||||
.foregroundStyle(.white, .red)
|
||||
} else if chat.chatStats.supportChatsUnreadCount > 0 {
|
||||
} else if chat.supportUnreadCount > 0 {
|
||||
Image(systemName: "flag.circle.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
|
||||
@@ -1339,17 +1339,11 @@ struct ChatView: View {
|
||||
if !itemIds.isEmpty {
|
||||
waitToMarkRead {
|
||||
await apiMarkChatItemsRead(im, chat.chatInfo, itemIds, mentionsRead: unreadMentions)
|
||||
if (im.secondaryIMFilter != nil) {
|
||||
m.decreaseGroupSupportChatsUnreadCounter(chat.chatInfo.id, by: itemIds.count )
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if chatItem.isRcvNew {
|
||||
waitToMarkRead {
|
||||
await apiMarkChatItemsRead(im, chat.chatInfo, [chatItem.id], mentionsRead: chatItem.meta.userMention ? 1 : 0)
|
||||
if (im.secondaryIMFilter != nil) {
|
||||
m.decreaseGroupSupportChatsUnreadCounter(chat.chatInfo.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,9 +73,10 @@ func showAcceptMemberAlert(_ groupInfo: GroupInfo, _ member: GroupMember, dismis
|
||||
func acceptMember(_ groupInfo: GroupInfo, _ member: GroupMember, _ role: GroupMemberRole, dismiss: DismissAction? = nil) {
|
||||
Task {
|
||||
do {
|
||||
let acceptedMember = try await apiAcceptMember(groupInfo.groupId, member.groupMemberId, role)
|
||||
let (gInfo, acceptedMember) = try await apiAcceptMember(groupInfo.groupId, member.groupMemberId, role)
|
||||
await MainActor.run {
|
||||
_ = ChatModel.shared.upsertGroupMember(groupInfo, acceptedMember)
|
||||
ChatModel.shared.updateGroup(gInfo)
|
||||
dismiss?()
|
||||
}
|
||||
} catch let error {
|
||||
|
||||
@@ -579,7 +579,7 @@ struct GroupChatInfoView: View {
|
||||
} label: {
|
||||
Label(
|
||||
"Chats with members",
|
||||
systemImage: chat.chatStats.supportChatsUnreadCount > 0 ? "flag.fill" : "flag"
|
||||
systemImage: chat.supportUnreadCount > 0 ? "flag.fill" : "flag"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,7 +445,7 @@ struct ChatPreviewView: View {
|
||||
ProgressView()
|
||||
} else if chat.chatStats.reportsCount > 0 {
|
||||
groupReportsIcon(size: size * 0.8)
|
||||
} else if chat.chatStats.supportChatsUnreadCount > 0 {
|
||||
} else if chat.supportUnreadCount > 0 {
|
||||
GroupSupportUnreadIcon(size: size * 0.8)
|
||||
} else {
|
||||
incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
|
||||
|
||||
@@ -1610,14 +1610,12 @@ public struct ChatStats: Decodable, Hashable {
|
||||
unreadCount: Int = 0,
|
||||
unreadMentions: Int = 0,
|
||||
reportsCount: Int = 0,
|
||||
supportChatsUnreadCount: Int = 0,
|
||||
minUnreadItemId: Int64 = 0,
|
||||
unreadChat: Bool = false
|
||||
) {
|
||||
self.unreadCount = unreadCount
|
||||
self.unreadMentions = unreadMentions
|
||||
self.reportsCount = reportsCount
|
||||
self.supportChatsUnreadCount = supportChatsUnreadCount
|
||||
self.minUnreadItemId = minUnreadItemId
|
||||
self.unreadChat = unreadChat
|
||||
}
|
||||
@@ -1626,8 +1624,6 @@ public struct ChatStats: Decodable, Hashable {
|
||||
public var unreadMentions: Int = 0
|
||||
// actual only via getChats() and getChat(.initial), otherwise, zero
|
||||
public var reportsCount: Int = 0
|
||||
// actual only via getChats() and getChat(.initial), otherwise, zero
|
||||
public var supportChatsUnreadCount: Int = 0
|
||||
public var minUnreadItemId: Int64 = 0
|
||||
// actual only via getChats(), otherwise, false
|
||||
public var unreadChat: Bool = false
|
||||
@@ -2029,6 +2025,7 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable {
|
||||
var updatedAt: Date
|
||||
var chatTs: Date?
|
||||
public var uiThemes: ThemeModeOverrides?
|
||||
public var membersRequireAttention: Int
|
||||
|
||||
public var id: ChatId { get { "#\(groupId)" } }
|
||||
public var apiId: Int64 { get { groupId } }
|
||||
@@ -2066,6 +2063,7 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable {
|
||||
chatSettings: ChatSettings.defaults,
|
||||
createdAt: .now,
|
||||
updatedAt: .now,
|
||||
membersRequireAttention: 0,
|
||||
chatTags: [],
|
||||
localAlias: ""
|
||||
)
|
||||
|
||||
+14
-24
@@ -429,6 +429,8 @@ object ChatModel {
|
||||
}
|
||||
|
||||
suspend fun addChatItem(rhId: Long?, cInfo: ChatInfo, cItem: ChatItem) {
|
||||
// updates membersRequireAttention
|
||||
updateChatInfo(rhId, cInfo)
|
||||
// mark chat non deleted
|
||||
if (cInfo is ChatInfo.Direct && cInfo.chatDeleted) {
|
||||
val updatedContact = cInfo.contact.copy(chatDeleted = false)
|
||||
@@ -903,28 +905,6 @@ object ChatModel {
|
||||
}
|
||||
}
|
||||
|
||||
fun increaseGroupSupportChatsUnreadCounter(rhId: Long?, chatId: ChatId) {
|
||||
changeGroupSupportChatsUnreadCounter(rhId, chatId, 1)
|
||||
}
|
||||
|
||||
fun decreaseGroupSupportChatsUnreadCounter(rhId: Long?, chatId: ChatId, by: Int = 1) {
|
||||
changeGroupSupportChatsUnreadCounter(rhId, chatId, -by)
|
||||
}
|
||||
|
||||
private fun changeGroupSupportChatsUnreadCounter(rhId: Long?, chatId: ChatId, by: Int = 0) {
|
||||
if (by == 0) return
|
||||
|
||||
val i = getChatIndex(rhId, chatId)
|
||||
if (i >= 0) {
|
||||
val chat = chats.value[i]
|
||||
chats[i] = chat.copy(
|
||||
chatStats = chat.chatStats.copy(
|
||||
supportChatsUnreadCount = (chat.chatStats.supportChatsUnreadCount + by).coerceAtLeast(0),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun increaseGroupReportsCounter(rhId: Long?, chatId: ChatId) {
|
||||
changeGroupReportsCounter(rhId, chatId, 1)
|
||||
}
|
||||
@@ -1329,6 +1309,16 @@ data class Chat(
|
||||
|
||||
val id: String get() = chatInfo.id
|
||||
|
||||
val supportUnreadCount: Int get() = when (chatInfo) {
|
||||
is ChatInfo.Group ->
|
||||
if (chatInfo.groupInfo.canModerate) {
|
||||
chatInfo.groupInfo.membersRequireAttention
|
||||
} else {
|
||||
chatInfo.groupInfo.membership.supportChat?.unread ?: 0
|
||||
}
|
||||
else -> 0
|
||||
}
|
||||
|
||||
fun groupFeatureEnabled(feature: GroupFeature): Boolean =
|
||||
if (chatInfo is ChatInfo.Group) {
|
||||
chatInfo.groupInfo.groupFeatureEnabled(feature)
|
||||
@@ -1342,8 +1332,6 @@ data class Chat(
|
||||
val unreadMentions: Int = 0,
|
||||
// actual only via getChats() and getChat(.initial), otherwise, zero
|
||||
val reportsCount: Int = 0,
|
||||
// actual only via getChats() and getChat(.initial), otherwise, zero
|
||||
val supportChatsUnreadCount: Int = 0,
|
||||
val minUnreadItemId: Long = 0,
|
||||
// actual only via getChats(), otherwise, false
|
||||
val unreadChat: Boolean = false
|
||||
@@ -1853,6 +1841,7 @@ data class GroupInfo (
|
||||
override val updatedAt: Instant,
|
||||
val chatTs: Instant?,
|
||||
val uiThemes: ThemeModeOverrides? = null,
|
||||
val membersRequireAttention: Int,
|
||||
val chatTags: List<Long>,
|
||||
val chatItemTTL: Long?,
|
||||
override val localAlias: String,
|
||||
@@ -1915,6 +1904,7 @@ data class GroupInfo (
|
||||
updatedAt = Clock.System.now(),
|
||||
chatTs = Clock.System.now(),
|
||||
uiThemes = null,
|
||||
membersRequireAttention = 0,
|
||||
chatTags = emptyList(),
|
||||
localAlias = "",
|
||||
chatItemTTL = null
|
||||
|
||||
+9
-8
@@ -1669,11 +1669,11 @@ object ChatController {
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun apiChatItemsRead(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?, itemIds: List<Long>): Boolean {
|
||||
suspend fun apiChatItemsRead(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?, itemIds: List<Long>): ChatInfo? {
|
||||
val r = sendCmd(rh, CC.ApiChatItemsRead(type, id, scope, itemIds))
|
||||
if (r.result is CR.CmdOk) return true
|
||||
if (r is API.Result && r.res is CR.ItemsReadForChat) return r.res.chatInfo
|
||||
Log.e(TAG, "apiChatItemsRead bad response: ${r.responseType} ${r.details}")
|
||||
return false
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiChatUnread(rh: Long?, type: ChatType, id: Long, unreadChat: Boolean): Boolean {
|
||||
@@ -1902,9 +1902,9 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiAcceptMember(rh: Long?, groupId: Long, groupMemberId: Long, memberRole: GroupMemberRole): GroupMember? {
|
||||
suspend fun apiAcceptMember(rh: Long?, groupId: Long, groupMemberId: Long, memberRole: GroupMemberRole): Pair<GroupInfo, GroupMember>? {
|
||||
val r = sendCmd(rh, CC.ApiAcceptMember(groupId, groupMemberId, memberRole))
|
||||
if (r is API.Result && r.res is CR.MemberAccepted) return r.res.member
|
||||
if (r is API.Result && r.res is CR.MemberAccepted) return r.res.groupInfo to r.res.member
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiAcceptMember", generalGetString(MR.strings.error_accepting_member), r)
|
||||
}
|
||||
@@ -2430,9 +2430,6 @@ object ChatController {
|
||||
if (cItem.isActiveReport) {
|
||||
chatModel.chatsContext.increaseGroupReportsCounter(rhId, cInfo.id)
|
||||
}
|
||||
if (cInfo.groupChatScope() != null && cItem.isRcvNew && cInfo.ntfsEnabled(cItem)) {
|
||||
chatModel.chatsContext.increaseGroupSupportChatsUnreadCounter(rhId, cInfo.id)
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.secondaryChatsContext.value?.addChatItem(rhId, cInfo, cItem)
|
||||
@@ -2586,6 +2583,7 @@ object ChatController {
|
||||
if (active(r.user)) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
}
|
||||
}
|
||||
is CR.DeletedMemberUser -> // TODO update user member
|
||||
@@ -5796,6 +5794,7 @@ sealed class CR {
|
||||
@Serializable @SerialName("contactAlreadyExists") class ContactAlreadyExists(val user: UserRef, val contact: Contact): CR()
|
||||
@Serializable @SerialName("contactDeleted") class ContactDeleted(val user: UserRef, val contact: Contact): CR()
|
||||
@Serializable @SerialName("contactDeletedByContact") class ContactDeletedByContact(val user: UserRef, val contact: Contact): CR()
|
||||
@Serializable @SerialName("itemsReadForChat") class ItemsReadForChat(val user: UserRef, val chatInfo: ChatInfo): CR()
|
||||
@Serializable @SerialName("chatCleared") class ChatCleared(val user: UserRef, val chatInfo: ChatInfo): CR()
|
||||
@Serializable @SerialName("userProfileNoChange") class UserProfileNoChange(val user: User): CR()
|
||||
@Serializable @SerialName("userProfileUpdated") class UserProfileUpdated(val user: User, val fromProfile: Profile, val toProfile: Profile, val updateSummary: UserProfileUpdateSummary): CR()
|
||||
@@ -5976,6 +5975,7 @@ sealed class CR {
|
||||
is ContactAlreadyExists -> "contactAlreadyExists"
|
||||
is ContactDeleted -> "contactDeleted"
|
||||
is ContactDeletedByContact -> "contactDeletedByContact"
|
||||
is ItemsReadForChat -> "itemsReadForChat"
|
||||
is ChatCleared -> "chatCleared"
|
||||
is UserProfileNoChange -> "userProfileNoChange"
|
||||
is UserProfileUpdated -> "userProfileUpdated"
|
||||
@@ -6146,6 +6146,7 @@ sealed class CR {
|
||||
is ContactAlreadyExists -> withUser(user, json.encodeToString(contact))
|
||||
is ContactDeleted -> withUser(user, json.encodeToString(contact))
|
||||
is ContactDeletedByContact -> withUser(user, json.encodeToString(contact))
|
||||
is ItemsReadForChat -> withUser(user, json.encodeToString(chatInfo))
|
||||
is ChatCleared -> withUser(user, json.encodeToString(chatInfo))
|
||||
is UserProfileNoChange -> withUser(user, noDetails())
|
||||
is UserProfileUpdated -> withUser(user, json.encodeToString(toProfile))
|
||||
|
||||
+23
-19
@@ -633,17 +633,17 @@ fun ChatView(
|
||||
withBGApi {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.markChatItemsRead(chatRh, chatInfo.id, itemsIds)
|
||||
if (chatsCtx.secondaryContextFilter != null) {
|
||||
chatModel.chatsContext.decreaseGroupSupportChatsUnreadCounter(chatRh, chatInfo.id)
|
||||
}
|
||||
ntfManager.cancelNotificationsForChat(chatInfo.id)
|
||||
chatModel.controller.apiChatItemsRead(
|
||||
val updatedChatInfo = chatModel.controller.apiChatItemsRead(
|
||||
chatRh,
|
||||
chatInfo.chatType,
|
||||
chatInfo.apiId,
|
||||
chatInfo.groupChatScope(),
|
||||
itemsIds
|
||||
)
|
||||
if (updatedChatInfo != null) {
|
||||
chatModel.chatsContext.updateChatInfo(chatRh, updatedChatInfo)
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.secondaryChatsContext.value?.markChatItemsRead(chatRh, chatInfo.id, itemsIds)
|
||||
@@ -894,10 +894,10 @@ fun ChatLayout(
|
||||
}
|
||||
}
|
||||
val reportsCount = reportsCount(chatInfo?.id)
|
||||
val supportChatsUnreadCount = supportChatsUnreadCount(chatInfo?.id)
|
||||
val supportUnreadCount = supportUnreadCount(chatInfo?.id)
|
||||
if (oneHandUI.value && chatBottomBar.value) {
|
||||
if (chatsCtx.secondaryContextFilter == null && (reportsCount > 0 || supportChatsUnreadCount > 0)) {
|
||||
SupportChatsCountToolbar(reportsCount, supportChatsUnreadCount, withStatusBar = true, showReports, showSupportChats)
|
||||
if (chatInfo != null && chatsCtx.secondaryContextFilter == null && (reportsCount > 0 || supportUnreadCount > 0)) {
|
||||
SupportChatsCountToolbar(chatInfo, reportsCount, supportUnreadCount, withStatusBar = true, showReports, showSupportChats)
|
||||
} else {
|
||||
StatusBarBackground()
|
||||
}
|
||||
@@ -953,8 +953,8 @@ fun ChatLayout(
|
||||
SelectedItemsCounterToolbar(selectedChatItems, !oneHandUI.value || !chatBottomBar.value)
|
||||
}
|
||||
}
|
||||
if ((reportsCount > 0 || supportChatsUnreadCount > 0) && (!oneHandUI.value || !chatBottomBar.value)) {
|
||||
SupportChatsCountToolbar(reportsCount, supportChatsUnreadCount, withStatusBar = false, showReports, showSupportChats)
|
||||
if (chatInfo != null && (reportsCount > 0 || supportUnreadCount > 0) && (!oneHandUI.value || !chatBottomBar.value)) {
|
||||
SupportChatsCountToolbar(chatInfo, reportsCount, supportUnreadCount, withStatusBar = false, showReports, showSupportChats)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1187,8 +1187,9 @@ fun ChatInfoToolbarTitle(cInfo: ChatInfo, imageSize: Dp = 40.dp, iconColor: Colo
|
||||
|
||||
@Composable
|
||||
private fun SupportChatsCountToolbar(
|
||||
chatInfo: ChatInfo,
|
||||
reportsCount: Int,
|
||||
supportChatsUnreadCount: Int,
|
||||
supportUnreadCount: Int,
|
||||
withStatusBar: Boolean,
|
||||
showReports: () -> Unit,
|
||||
showSupportChats: () -> Unit
|
||||
@@ -1225,7 +1226,7 @@ private fun SupportChatsCountToolbar(
|
||||
}
|
||||
}
|
||||
|
||||
if (supportChatsUnreadCount > 0) {
|
||||
if (supportUnreadCount > 0) {
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -1240,10 +1241,13 @@ private fun SupportChatsCountToolbar(
|
||||
Icon(painterResource(MR.images.ic_flag), null, Modifier.size(22.dp), tint = MaterialTheme.colors.primary)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
if (appPlatform.isAndroid)
|
||||
stringResource(MR.strings.group_new_support_messages_short).format(supportChatsUnreadCount)
|
||||
if (chatInfo is ChatInfo.Group && chatInfo.groupInfo.canModerate)
|
||||
if (appPlatform.isAndroid)
|
||||
stringResource(MR.strings.group_new_support_chats_short).format(supportUnreadCount)
|
||||
else
|
||||
stringResource(MR.strings.group_new_support_chats).format(supportUnreadCount)
|
||||
else
|
||||
stringResource(MR.strings.group_new_support_messages).format(supportChatsUnreadCount),
|
||||
stringResource(MR.strings.group_new_support_messages).format(supportUnreadCount),
|
||||
style = MaterialTheme.typography.button
|
||||
)
|
||||
}
|
||||
@@ -1329,10 +1333,10 @@ fun BoxScope.ChatItemsList(
|
||||
}
|
||||
val reversedChatItems = remember { derivedStateOf { chatsCtx.chatItems.value.asReversed() } }
|
||||
val reportsCount = reportsCount(chatInfo.id)
|
||||
val supportChatsUnreadCount = supportChatsUnreadCount(chatInfo.id)
|
||||
val supportUnreadCount = supportUnreadCount(chatInfo.id)
|
||||
val topPaddingToContent = topPaddingToContent(
|
||||
chatView = chatsCtx.secondaryContextFilter == null,
|
||||
additionalTopBar = chatsCtx.secondaryContextFilter == null && (reportsCount > 0 || supportChatsUnreadCount > 0)
|
||||
additionalTopBar = chatsCtx.secondaryContextFilter == null && (reportsCount > 0 || supportUnreadCount > 0)
|
||||
)
|
||||
val topPaddingToContentPx = rememberUpdatedState(with(LocalDensity.current) { topPaddingToContent.roundToPx() })
|
||||
val numberOfBottomAppBars = numberOfBottomAppBars()
|
||||
@@ -1672,7 +1676,7 @@ fun BoxScope.ChatItemsList(
|
||||
),
|
||||
reverseLayout = true,
|
||||
additionalBarOffset = composeViewHeight,
|
||||
additionalTopBar = rememberUpdatedState(chatsCtx.secondaryContextFilter == null && (reportsCount > 0 || supportChatsUnreadCount > 0)),
|
||||
additionalTopBar = rememberUpdatedState(chatsCtx.secondaryContextFilter == null && (reportsCount > 0 || supportUnreadCount > 0)),
|
||||
chatBottomBar = remember { appPrefs.chatBottomBar.state }
|
||||
) {
|
||||
val mergedItemsValue = mergedItems.value
|
||||
@@ -2358,11 +2362,11 @@ fun reportsCount(staleChatId: String?): Int {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun supportChatsUnreadCount(staleChatId: String?): Int {
|
||||
fun supportUnreadCount(staleChatId: String?): Int {
|
||||
return if (staleChatId?.startsWith("#") != true) {
|
||||
0
|
||||
} else {
|
||||
remember(staleChatId) { derivedStateOf { chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId }?.chatStats } }.value?.supportChatsUnreadCount ?: 0
|
||||
remember(staleChatId) { derivedStateOf { chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId } } }.value?.supportUnreadCount ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -102,13 +102,14 @@ fun acceptMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, c
|
||||
|
||||
private fun acceptMember(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, role: GroupMemberRole, close: (() -> Unit)?) {
|
||||
withBGApi {
|
||||
val acceptedMember = chatModel.controller.apiAcceptMember(rhId, groupInfo.groupId, member.groupMemberId, role)
|
||||
if (acceptedMember != null) {
|
||||
val r = chatModel.controller.apiAcceptMember(rhId, groupInfo.groupId, member.groupMemberId, role)
|
||||
if (r != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, acceptedMember)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, r.second)
|
||||
chatModel.chatsContext.updateGroup(rhId, r.first)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, acceptedMember)
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, r.second)
|
||||
}
|
||||
}
|
||||
close?.invoke()
|
||||
|
||||
+2
-2
@@ -741,10 +741,10 @@ private fun GroupChatInfoHeader(cInfo: ChatInfo, groupInfo: GroupInfo) {
|
||||
@Composable
|
||||
private fun MemberSupportButton(chat: Chat, onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(if (chat.chatStats.supportChatsUnreadCount > 0) MR.images.ic_flag_filled else MR.images.ic_flag),
|
||||
painterResource(if (chat.supportUnreadCount > 0) MR.images.ic_flag_filled else MR.images.ic_flag),
|
||||
stringResource(MR.strings.member_support),
|
||||
click = onClick,
|
||||
iconColor = (if (chat.chatStats.supportChatsUnreadCount > 0) MaterialTheme.colors.primary else MaterialTheme.colors.secondary)
|
||||
iconColor = (if (chat.supportUnreadCount > 0) MaterialTheme.colors.primary else MaterialTheme.colors.secondary)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -364,7 +364,7 @@ fun ChatPreviewView(
|
||||
progressView()
|
||||
} else if (chat.chatStats.reportsCount > 0) {
|
||||
GroupReportsIcon()
|
||||
} else if (chat.chatStats.supportChatsUnreadCount > 0) {
|
||||
} else if (chat.supportUnreadCount > 0) {
|
||||
GroupSupportUnreadIcon()
|
||||
} else {
|
||||
IncognitoIcon(chat.chatInfo.incognito)
|
||||
|
||||
@@ -465,8 +465,9 @@
|
||||
<string name="group_reports_active_one">1 report</string>
|
||||
<string name="group_reports_active">%d reports</string>
|
||||
<string name="group_reports_member_reports">Member reports</string>
|
||||
<string name="group_new_support_messages">%d new support messages</string>
|
||||
<string name="group_new_support_messages_short">%d messages</string>
|
||||
<string name="group_new_support_messages">%d messages</string>
|
||||
<string name="group_new_support_chats">%d chats with members</string>
|
||||
<string name="group_new_support_chats_short">%d chats</string>
|
||||
|
||||
<!-- ShareListView.kt -->
|
||||
<string name="share_message">Share message…</string>
|
||||
|
||||
@@ -676,6 +676,7 @@ data ChatResponse
|
||||
| CRSentConfirmation {user :: User, connection :: PendingContactConnection}
|
||||
| CRSentInvitation {user :: User, connection :: PendingContactConnection, customUserProfile :: Maybe Profile}
|
||||
| CRSentInvitationToContact {user :: User, contact :: Contact, customUserProfile :: Maybe Profile}
|
||||
| CRItemsReadForChat {user :: User, chatInfo :: AChatInfo}
|
||||
| CRContactDeleted {user :: User, contact :: Contact}
|
||||
| CRChatCleared {user :: User, chatInfo :: AChatInfo}
|
||||
| CRUserContactLinkCreated {user :: User, connLinkContact :: CreatedLinkContact}
|
||||
|
||||
@@ -1017,22 +1017,26 @@ processChatCommand' vr = \case
|
||||
CTContactConnection -> throwCmdError "not supported"
|
||||
APIChatItemsRead chatRef@(ChatRef cType chatId scope) itemIds -> withUser $ \_ -> case cType of
|
||||
CTDirect -> do
|
||||
user <- withFastStore $ \db -> getUserByContactId db chatId
|
||||
(user, ct) <- withFastStore $ \db -> do
|
||||
user <- getUserByContactId db chatId
|
||||
ct <- getContact db vr user chatId
|
||||
pure (user, ct)
|
||||
timedItems <- withFastStore' $ \db -> do
|
||||
timedItems <- updateDirectChatItemsReadList db user chatId itemIds
|
||||
setDirectChatItemsDeleteAt db user chatId timedItems =<< getCurrentTime
|
||||
forM_ timedItems $ \(itemId, deleteAt) -> startProximateTimedItemThread user (chatRef, itemId) deleteAt
|
||||
ok user
|
||||
pure $ CRItemsReadForChat user (AChatInfo SCTDirect $ DirectChat ct)
|
||||
CTGroup -> do
|
||||
(user, gInfo) <- withFastStore $ \db -> do
|
||||
user <- getUserByGroupId db chatId
|
||||
gInfo <- getGroupInfo db vr user chatId
|
||||
pure (user, gInfo)
|
||||
timedItems <- withFastStore' $ \db -> do
|
||||
timedItems <- updateGroupChatItemsReadList db user gInfo scope itemIds
|
||||
setGroupChatItemsDeleteAt db user chatId timedItems =<< getCurrentTime
|
||||
(timedItems, gInfo') <- withFastStore $ \db -> do
|
||||
(timedItems, gInfo') <- updateGroupChatItemsReadList db vr user gInfo scope itemIds
|
||||
timedItems' <- liftIO $ setGroupChatItemsDeleteAt db user chatId timedItems =<< getCurrentTime
|
||||
pure (timedItems', gInfo')
|
||||
forM_ timedItems $ \(itemId, deleteAt) -> startProximateTimedItemThread user (chatRef, itemId) deleteAt
|
||||
ok user
|
||||
pure $ CRItemsReadForChat user (AChatInfo SCTGroup $ GroupChat gInfo' Nothing)
|
||||
CTLocal -> throwCmdError "not supported"
|
||||
CTContactRequest -> throwCmdError "not supported"
|
||||
CTContactConnection -> throwCmdError "not supported"
|
||||
@@ -2054,8 +2058,11 @@ processChatCommand' vr = \case
|
||||
void $ sendDirectMemberMessage mConn msg groupId
|
||||
introduceToRemaining vr user gInfo m {memberRole = role}
|
||||
when (groupFeatureAllowed SGFHistory gInfo) $ sendHistory user gInfo m
|
||||
m' <- withFastStore' $ \db -> updateGroupMemberAccepted db user m GSMemConnected role
|
||||
pure $ CRMemberAccepted user gInfo m'
|
||||
(m', gInfo') <- withFastStore' $ \db -> do
|
||||
m' <- updateGroupMemberAccepted db user m GSMemConnected role
|
||||
gInfo' <- updateGroupMembersRequireAttention db user gInfo m m'
|
||||
pure (m', gInfo')
|
||||
pure $ CRMemberAccepted user gInfo' m'
|
||||
Nothing -> throwChatError CEGroupMemberNotActive
|
||||
GSMemPendingReview -> do
|
||||
let scope = Just $ GCSMemberSupport $ Just (groupMemberId' m)
|
||||
@@ -2063,8 +2070,11 @@ processChatCommand' vr = \case
|
||||
let rcpModMs' = filter memberCurrent modMs
|
||||
msg = XGrpLinkAcpt GAAccepted role (memberId' m)
|
||||
void $ sendGroupMessage user gInfo scope ([m] <> rcpModMs') msg
|
||||
m' <- withFastStore' $ \db -> updateGroupMemberAccepted db user m newMemberStatus role
|
||||
pure $ CRMemberAccepted user gInfo m'
|
||||
(m', gInfo') <- withFastStore' $ \db -> do
|
||||
m' <- updateGroupMemberAccepted db user m newMemberStatus role
|
||||
gInfo' <- updateGroupMembersRequireAttention db user gInfo m m'
|
||||
pure (m', gInfo')
|
||||
pure $ CRMemberAccepted user gInfo' m'
|
||||
where
|
||||
newMemberStatus = case memberConn m of
|
||||
Just c | connReady c -> GSMemConnected
|
||||
|
||||
@@ -2032,8 +2032,9 @@ saveSndChatItems ::
|
||||
CM [Either ChatError (ChatItem c 'MDSnd)]
|
||||
saveSndChatItems user cd itemsData itemTimed live = do
|
||||
createdAt <- liftIO getCurrentTime
|
||||
vr <- chatVersionRange
|
||||
when (contactChatDeleted cd || any (\NewSndChatItemData {content} -> ciRequiresAttention content) (rights itemsData)) $
|
||||
withStore' (\db -> updateChatTsStats db user cd createdAt Nothing)
|
||||
void $ withStore' (\db -> updateChatTsStats db vr user cd createdAt Nothing)
|
||||
lift $ withStoreBatch (\db -> map (bindRight $ createItem db createdAt) itemsData)
|
||||
where
|
||||
createItem :: DB.Connection -> UTCTime -> NewSndChatItemData c -> IO (Either ChatError (ChatItem c 'MDSnd))
|
||||
@@ -2045,19 +2046,20 @@ saveSndChatItems user cd itemsData itemTimed live = do
|
||||
CDGroupSnd g _scope | not (null itemMentions) -> createGroupCIMentions db g ci itemMentions
|
||||
_ -> pure ci
|
||||
|
||||
saveRcvChatItemNoParse :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> UTCTime -> CIContent 'MDRcv -> CM (ChatItem c 'MDRcv)
|
||||
saveRcvChatItemNoParse :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> UTCTime -> CIContent 'MDRcv -> CM (ChatItem c 'MDRcv, ChatInfo c)
|
||||
saveRcvChatItemNoParse user cd msg brokerTs = saveRcvChatItem user cd msg brokerTs . ciContentNoParse
|
||||
|
||||
saveRcvChatItem :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> UTCTime -> (CIContent 'MDRcv, (Text, Maybe MarkdownList)) -> CM (ChatItem c 'MDRcv)
|
||||
saveRcvChatItem :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> UTCTime -> (CIContent 'MDRcv, (Text, Maybe MarkdownList)) -> CM (ChatItem c 'MDRcv, ChatInfo c)
|
||||
saveRcvChatItem user cd msg@RcvMessage {sharedMsgId_} brokerTs content =
|
||||
saveRcvChatItem' user cd msg sharedMsgId_ brokerTs content Nothing Nothing False M.empty
|
||||
|
||||
ciContentNoParse :: CIContent 'MDRcv -> (CIContent 'MDRcv, (Text, Maybe MarkdownList))
|
||||
ciContentNoParse content = (content, (ciContentToText content, Nothing))
|
||||
|
||||
saveRcvChatItem' :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> Maybe SharedMsgId -> UTCTime -> (CIContent 'MDRcv, (Text, Maybe MarkdownList)) -> Maybe (CIFile 'MDRcv) -> Maybe CITimed -> Bool -> Map MemberName MsgMention -> CM (ChatItem c 'MDRcv)
|
||||
saveRcvChatItem' :: (ChatTypeI c, ChatTypeQuotable c) => User -> ChatDirection c 'MDRcv -> RcvMessage -> Maybe SharedMsgId -> UTCTime -> (CIContent 'MDRcv, (Text, Maybe MarkdownList)) -> Maybe (CIFile 'MDRcv) -> Maybe CITimed -> Bool -> Map MemberName MsgMention -> CM (ChatItem c 'MDRcv, ChatInfo c)
|
||||
saveRcvChatItem' user cd msg@RcvMessage {chatMsgEvent, forwardedByMember} sharedMsgId_ brokerTs (content, (t, ft_)) ciFile itemTimed live mentions = do
|
||||
createdAt <- liftIO getCurrentTime
|
||||
vr <- chatVersionRange
|
||||
withStore' $ \db -> do
|
||||
(mentions' :: Map MemberName CIMention, userMention) <- case cd of
|
||||
CDGroupRcv g@GroupInfo {membership} _scope _m -> do
|
||||
@@ -2068,13 +2070,16 @@ saveRcvChatItem' user cd msg@RcvMessage {chatMsgEvent, forwardedByMember} shared
|
||||
userMention' = userReply || any (\CIMention {memberId} -> sameMemberId memberId membership) mentions'
|
||||
in pure (mentions', userMention')
|
||||
CDDirectRcv _ -> pure (M.empty, False)
|
||||
when (ciRequiresAttention content || contactChatDeleted cd) $ updateChatTsStats db user cd createdAt (chatStatsCounts userMention)
|
||||
cInfo' <- if (ciRequiresAttention content || contactChatDeleted cd)
|
||||
then updateChatTsStats db vr user cd createdAt (chatStatsCounts userMention)
|
||||
else pure $ toChatInfo cd
|
||||
(ciId, quotedItem, itemForwarded) <- createNewRcvChatItem db user cd msg sharedMsgId_ content itemTimed live userMention brokerTs createdAt
|
||||
forM_ ciFile $ \CIFile {fileId} -> updateFileTransferChatItemId db fileId ciId createdAt
|
||||
let ci = mkChatItem_ cd ciId content (t, ft_) ciFile quotedItem sharedMsgId_ itemForwarded itemTimed live userMention brokerTs forwardedByMember createdAt
|
||||
case cd of
|
||||
ci' <- case cd of
|
||||
CDGroupRcv g _scope _m | not (null mentions') -> createGroupCIMentions db g ci mentions'
|
||||
_ -> pure ci
|
||||
pure (ci', cInfo')
|
||||
where
|
||||
chatStatsCounts :: Bool -> Maybe (Int, MemberAttention, Int)
|
||||
chatStatsCounts userMention = case cd of
|
||||
@@ -2297,12 +2302,13 @@ createInternalItemsForChats ::
|
||||
createInternalItemsForChats user itemTs_ dirsCIContents = do
|
||||
createdAt <- liftIO getCurrentTime
|
||||
let itemTs = fromMaybe createdAt itemTs_
|
||||
void . withStoreBatch' $ \db -> map (uncurry $ updateChat db createdAt) dirsCIContents
|
||||
vr <- chatVersionRange'
|
||||
void . withStoreBatch' $ \db -> map (uncurry $ updateChat db vr createdAt) dirsCIContents
|
||||
withStoreBatch' $ \db -> concatMap (uncurry $ createACIs db itemTs createdAt) dirsCIContents
|
||||
where
|
||||
updateChat :: DB.Connection -> UTCTime -> ChatDirection c d -> [CIContent d] -> IO ()
|
||||
updateChat db createdAt cd contents
|
||||
| any ciRequiresAttention contents || contactChatDeleted cd = updateChatTsStats db user cd createdAt chatStatsCounts
|
||||
updateChat :: DB.Connection -> VersionRangeChat -> UTCTime -> ChatDirection c d -> [CIContent d] -> IO ()
|
||||
updateChat db vr createdAt cd contents
|
||||
| any ciRequiresAttention contents || contactChatDeleted cd = void $ updateChatTsStats db vr user cd createdAt chatStatsCounts
|
||||
| otherwise = pure ()
|
||||
where
|
||||
chatStatsCounts :: Maybe (Int, MemberAttention, Int)
|
||||
@@ -2331,7 +2337,8 @@ createLocalChatItems ::
|
||||
UTCTime ->
|
||||
CM [ChatItem 'CTLocal 'MDSnd]
|
||||
createLocalChatItems user cd itemsData createdAt = do
|
||||
withStore' $ \db -> updateChatTsStats db user cd createdAt Nothing
|
||||
vr <- chatVersionRange
|
||||
void $ withStore' $ \db -> updateChatTsStats db vr user cd createdAt Nothing
|
||||
(errs, items) <- lift $ partitionEithers <$> withStoreBatch' (\db -> map (createItem db) $ L.toList itemsData)
|
||||
unless (null errs) $ toView $ CEvtChatErrors errs
|
||||
pure items
|
||||
|
||||
@@ -784,29 +784,33 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
memberConnectedChatItem gInfo' scopeInfo m'
|
||||
unless (memberPending membership) $ maybeCreateGroupDescrLocal gInfo' m'
|
||||
GCInviteeMember -> do
|
||||
mStatus <-
|
||||
(gInfo', mStatus) <-
|
||||
if not (memberPending m)
|
||||
then withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected $> GSMemConnected
|
||||
else pure $ memberStatus m
|
||||
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
|
||||
memberConnectedChatItem gInfo' scopeInfo m'
|
||||
then do
|
||||
mStatus <- withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected $> GSMemConnected
|
||||
pure (gInfo, mStatus)
|
||||
else do
|
||||
gInfo' <- withStore' $ \db -> increaseGroupMembersRequireAttention db user gInfo
|
||||
pure (gInfo', memberStatus m)
|
||||
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo' m
|
||||
memberConnectedChatItem gInfo'' scopeInfo m'
|
||||
case scopeInfo of
|
||||
Just (GCSIMemberSupport _) -> do
|
||||
createInternalChatItem user (CDGroupRcv gInfo' scopeInfo m') (CIRcvGroupEvent RGENewMemberPendingReview) Nothing
|
||||
createInternalChatItem user (CDGroupRcv gInfo'' scopeInfo m') (CIRcvGroupEvent RGENewMemberPendingReview) Nothing
|
||||
_ -> pure ()
|
||||
toView $ CEvtJoinedGroupMember user gInfo' m' {memberStatus = mStatus}
|
||||
toView $ CEvtJoinedGroupMember user gInfo'' m' {memberStatus = mStatus}
|
||||
let Connection {viaUserContactLink} = conn
|
||||
when (isJust viaUserContactLink && isNothing (memberContactId m')) $ sendXGrpLinkMem gInfo'
|
||||
when (isJust viaUserContactLink && isNothing (memberContactId m')) $ sendXGrpLinkMem gInfo''
|
||||
when (connChatVersion < batchSend2Version) sendGroupAutoReply
|
||||
case mStatus of
|
||||
GSMemPendingApproval -> pure ()
|
||||
GSMemPendingReview -> introduceToModerators vr user gInfo' m'
|
||||
GSMemPendingReview -> introduceToModerators vr user gInfo'' m'
|
||||
_ -> do
|
||||
introduceToAll vr user gInfo' m'
|
||||
when (groupFeatureAllowed SGFHistory gInfo') $ sendHistory user gInfo' m'
|
||||
introduceToAll vr user gInfo'' m'
|
||||
when (groupFeatureAllowed SGFHistory gInfo'') $ sendHistory user gInfo'' m'
|
||||
where
|
||||
sendXGrpLinkMem gInfo' = do
|
||||
let profileMode = ExistingIncognito <$> incognitoMembershipProfile gInfo'
|
||||
sendXGrpLinkMem gInfo'' = do
|
||||
let profileMode = ExistingIncognito <$> incognitoMembershipProfile gInfo''
|
||||
profileToSend = profileToSendOnAccept user profileMode True
|
||||
void $ sendDirectMemberMessage conn (XGrpLinkMem profileToSend) groupId
|
||||
_ -> do
|
||||
@@ -1466,9 +1470,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
where
|
||||
brokerTs = metaBrokerTs msgMeta
|
||||
newChatItem content ciFile_ timed_ live = do
|
||||
ci <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content ciFile_ timed_ live M.empty
|
||||
(ci, cInfo) <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content ciFile_ timed_ live M.empty
|
||||
reactions <- maybe (pure []) (\sharedMsgId -> withStore' $ \db -> getDirectCIReactions db ct sharedMsgId) sharedMsgId_
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci {reactions}]
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci {reactions}]
|
||||
|
||||
autoAcceptFile :: Maybe (RcvFileTransfer, CIFile 'MDRcv) -> CM ()
|
||||
autoAcceptFile = mapM_ $ \(ft, CIFile {fileSize}) -> do
|
||||
@@ -1534,11 +1538,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
-- Chat item and update message which created it will have different sharedMsgId in this case...
|
||||
let timed_ = rcvContactCITimed ct ttl
|
||||
ts = ciContentTexts content
|
||||
ci <- saveRcvChatItem' user (CDDirectRcv ct) msg (Just sharedMsgId) brokerTs (content, ts) Nothing timed_ live M.empty
|
||||
(ci, cInfo) <- saveRcvChatItem' user (CDDirectRcv ct) msg (Just sharedMsgId) brokerTs (content, ts) Nothing timed_ live M.empty
|
||||
ci' <- withStore' $ \db -> do
|
||||
createChatItemVersion db (chatItemId' ci) brokerTs mc
|
||||
updateDirectChatItem' db user contactId ci content True live Nothing Nothing
|
||||
toView $ CEvtChatItemUpdated user (AChatItem SCTDirect SMDRcv (DirectChat ct) ci')
|
||||
toView $ CEvtChatItemUpdated user (AChatItem SCTDirect SMDRcv cInfo ci')
|
||||
where
|
||||
brokerTs = metaBrokerTs msgMeta
|
||||
content = CIRcvMsgContent mc
|
||||
@@ -1658,27 +1662,26 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
| groupFeatureAllowed SGFFullDelete gInfo = do
|
||||
(gInfo', m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
|
||||
-- ignores member role when blocked by admin
|
||||
ci <- saveRcvCI gInfo' scopeInfo m' (ciContentNoParse CIRcvBlocked) Nothing timed' False M.empty
|
||||
(ci, cInfo) <- saveRcvCI gInfo' scopeInfo m' (ciContentNoParse CIRcvBlocked) Nothing timed' False M.empty
|
||||
ci' <- withStore' $ \db -> updateGroupCIBlockedByAdmin db user gInfo' ci brokerTs
|
||||
groupMsgToView gInfo' scopeInfo ci'
|
||||
groupMsgToView cInfo ci'
|
||||
| otherwise = do
|
||||
file_ <- processFileInv
|
||||
ci <- createNonLive file_
|
||||
(ci, cInfo) <- createNonLive file_
|
||||
ci' <- withStore' $ \db -> markGroupCIBlockedByAdmin db user gInfo ci
|
||||
(gInfo', _m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
|
||||
groupMsgToView gInfo' scopeInfo ci'
|
||||
groupMsgToView cInfo ci'
|
||||
applyModeration CIModeration {moderatorMember = moderator@GroupMember {memberRole = moderatorRole}, moderatedAt}
|
||||
| moderatorRole < GRModerator || moderatorRole < memberRole =
|
||||
createContentItem
|
||||
| groupFeatureMemberAllowed SGFFullDelete moderator gInfo = do
|
||||
(gInfo', m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
|
||||
ci <- saveRcvCI gInfo' scopeInfo m' (ciContentNoParse CIRcvModerated) Nothing timed' False M.empty
|
||||
(ci, cInfo) <- saveRcvCI gInfo' scopeInfo m' (ciContentNoParse CIRcvModerated) Nothing timed' False M.empty
|
||||
ci' <- withStore' $ \db -> updateGroupChatItemModerated db user gInfo' ci moderator moderatedAt
|
||||
groupMsgToView gInfo' scopeInfo ci'
|
||||
groupMsgToView cInfo ci'
|
||||
| otherwise = do
|
||||
(gInfo', _m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
|
||||
file_ <- processFileInv
|
||||
ci <- createNonLive file_
|
||||
(ci, _cInfo) <- createNonLive file_
|
||||
deletions <- markGroupCIsDeleted user gInfo' scopeInfo [CChatItem SMDRcv ci] (Just moderator) moderatedAt
|
||||
toView $ CEvtChatItemsDeleted user deletions False False
|
||||
createNonLive file_ = do
|
||||
@@ -1693,10 +1696,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
newChatItem ciContent ciFile_ timed_ live = do
|
||||
let mentions' = if showMessages (memberSettings m) then mentions else []
|
||||
(gInfo', m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
|
||||
ci <- saveRcvCI gInfo' scopeInfo m' ciContent ciFile_ timed_ live mentions'
|
||||
(ci, cInfo) <- saveRcvCI gInfo' scopeInfo m' ciContent ciFile_ timed_ live mentions'
|
||||
ci' <- blockedMember m' ci $ withStore' $ \db -> markGroupChatItemBlocked db user gInfo' ci
|
||||
reactions <- maybe (pure []) (\sharedMsgId -> withStore' $ \db -> getGroupCIReactions db gInfo' memberId sharedMsgId) sharedMsgId_
|
||||
groupMsgToView gInfo' scopeInfo ci' {reactions}
|
||||
groupMsgToView cInfo ci' {reactions}
|
||||
|
||||
groupMessageUpdate :: GroupInfo -> GroupMember -> SharedMsgId -> MsgContent -> Map MemberName MsgMention -> Maybe MsgScope -> RcvMessage -> UTCTime -> Maybe Int -> Maybe Bool -> CM ()
|
||||
groupMessageUpdate gInfo@GroupInfo {groupId} m@GroupMember {groupMemberId, memberId} sharedMsgId mc mentions msgScope_ msg@RcvMessage {msgId} brokerTs ttl_ live_
|
||||
@@ -1710,12 +1713,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
let timed_ = rcvGroupCITimed gInfo ttl_
|
||||
mentions' = if showMessages (memberSettings m) then mentions else []
|
||||
(gInfo', m', scopeInfo) <- mkGetMessageChatScope vr user gInfo m msgScope_
|
||||
ci <- saveRcvChatItem' user (CDGroupRcv gInfo' scopeInfo m') msg (Just sharedMsgId) brokerTs (content, ts) Nothing timed_ live mentions'
|
||||
(ci, cInfo) <- saveRcvChatItem' user (CDGroupRcv gInfo' scopeInfo m') msg (Just sharedMsgId) brokerTs (content, ts) Nothing timed_ live mentions'
|
||||
ci' <- withStore' $ \db -> do
|
||||
createChatItemVersion db (chatItemId' ci) brokerTs mc
|
||||
ci' <- updateGroupChatItem db user groupId ci content True live Nothing
|
||||
blockedMember m' ci' $ markGroupChatItemBlocked db user gInfo' ci'
|
||||
toView $ CEvtChatItemUpdated user (AChatItem SCTGroup SMDRcv (GroupChat gInfo' scopeInfo) ci')
|
||||
toView $ CEvtChatItemUpdated user (AChatItem SCTGroup SMDRcv cInfo ci')
|
||||
where
|
||||
content = CIRcvMsgContent mc
|
||||
ts@(_, ft_) = msgContentTexts mc
|
||||
@@ -1802,8 +1805,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
let fileProtocol = if isJust xftpRcvFile then FPXFTP else FPSMP
|
||||
ciFile = Just $ CIFile {fileId, fileName, fileSize, fileSource = Nothing, fileStatus = CIFSRcvInvitation, fileProtocol}
|
||||
content = ciContentNoParse $ CIRcvMsgContent $ MCFile ""
|
||||
ci <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content ciFile Nothing False M.empty
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci]
|
||||
(ci, cInfo) <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content ciFile Nothing False M.empty
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
|
||||
where
|
||||
brokerTs = metaBrokerTs msgMeta
|
||||
|
||||
@@ -1816,9 +1819,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
let fileProtocol = if isJust xftpRcvFile then FPXFTP else FPSMP
|
||||
ciFile = Just $ CIFile {fileId, fileName, fileSize, fileSource = Nothing, fileStatus = CIFSRcvInvitation, fileProtocol}
|
||||
content = ciContentNoParse $ CIRcvMsgContent $ MCFile ""
|
||||
ci <- saveRcvChatItem' user (CDGroupRcv gInfo Nothing m) msg sharedMsgId_ brokerTs content ciFile Nothing False M.empty
|
||||
(ci, cInfo) <- saveRcvChatItem' user (CDGroupRcv gInfo Nothing m) msg sharedMsgId_ brokerTs content ciFile Nothing False M.empty
|
||||
ci' <- blockedMember m ci $ withStore' $ \db -> markGroupChatItemBlocked db user gInfo ci
|
||||
groupMsgToView gInfo Nothing ci'
|
||||
groupMsgToView cInfo ci'
|
||||
|
||||
blockedMember :: Monad m' => GroupMember -> ChatItem c d -> m' (ChatItem c d) -> m' (ChatItem c d)
|
||||
blockedMember m ci blockedCI
|
||||
@@ -1969,9 +1972,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
_ -> messageError "x.file.acpt.inv: member connection is not active"
|
||||
else messageError "x.file.acpt.inv: fileName is different from expected"
|
||||
|
||||
groupMsgToView :: forall d. MsgDirectionI d => GroupInfo -> Maybe GroupChatScopeInfo -> ChatItem 'CTGroup d -> CM ()
|
||||
groupMsgToView gInfo chatScopeInfo ci = do
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTGroup (msgDirection @d) (GroupChat gInfo chatScopeInfo) ci]
|
||||
groupMsgToView :: forall d. MsgDirectionI d => ChatInfo 'CTGroup -> ChatItem 'CTGroup d -> CM ()
|
||||
groupMsgToView cInfo ci = do
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTGroup (msgDirection @d) cInfo ci]
|
||||
|
||||
processGroupInvitation :: Contact -> GroupInvitation -> RcvMessage -> MsgMeta -> CM ()
|
||||
processGroupInvitation ct inv msg msgMeta = do
|
||||
@@ -1996,9 +1999,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
toView $ CEvtUserAcceptedGroupSent user gInfo {membership = membership {memberStatus = GSMemAccepted}} (Just ct)
|
||||
else do
|
||||
let content = CIRcvGroupInvitation (CIGroupInvitation {groupId, groupMemberId, localDisplayName, groupProfile, status = CIGISPending}) memRole
|
||||
ci <- saveRcvChatItemNoParse user (CDDirectRcv ct) msg brokerTs content
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDDirectRcv ct) msg brokerTs content
|
||||
withStore' $ \db -> setGroupInvitationChatItemId db user groupId (chatItemId' ci)
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci]
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
|
||||
toView $ CEvtReceivedGroupInvitation {user, groupInfo = gInfo, contact = ct, fromMemberRole = fromRole, memberRole = memRole}
|
||||
where
|
||||
brokerTs = metaBrokerTs msgMeta
|
||||
@@ -2024,8 +2027,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
forM_ contactConns $ \conn -> withStore' $ \db -> updateConnectionStatus db conn ConnDeleted
|
||||
activeConn' <- forM (contactConn ct') $ \conn -> pure conn {connStatus = ConnDeleted}
|
||||
let ct'' = ct' {activeConn = activeConn'} :: Contact
|
||||
ci <- saveRcvChatItemNoParse user (CDDirectRcv ct'') msg brokerTs (CIRcvDirectEvent RDEContactDeleted)
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct'') ci]
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDDirectRcv ct'') msg brokerTs (CIRcvDirectEvent RDEContactDeleted)
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
|
||||
toView $ CEvtContactDeletedByContact user ct''
|
||||
else do
|
||||
contactConns <- withStore' $ \db -> getContactConnections db vr userId c
|
||||
@@ -2096,13 +2099,16 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
withStore' (\db -> runExceptT $ getGroupMemberByMemberId db vr user gInfo memberId) >>= \case
|
||||
Left _ -> messageError "x.grp.link.acpt error: referenced member does not exist"
|
||||
Right referencedMember -> do
|
||||
referencedMember' <- withFastStore' $ \db -> updateGroupMemberAccepted db user referencedMember (newMemberStatus referencedMember) role
|
||||
(referencedMember', gInfo') <- withFastStore' $ \db -> do
|
||||
referencedMember' <- updateGroupMemberAccepted db user referencedMember (newMemberStatus referencedMember) role
|
||||
gInfo' <- updateGroupMembersRequireAttention db user gInfo referencedMember referencedMember'
|
||||
pure (referencedMember', gInfo')
|
||||
when (memberCategory referencedMember == GCInviteeMember) $ introduceToRemainingMembers referencedMember'
|
||||
let scopeInfo = Just $ GCSIMemberSupport {groupMember_ = Just referencedMember'}
|
||||
gEvent = RGEMemberAccepted (groupMemberId' referencedMember') (fromLocalProfile $ memberProfile referencedMember')
|
||||
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo scopeInfo m) msg brokerTs (CIRcvGroupEvent gEvent)
|
||||
groupMsgToView gInfo scopeInfo ci
|
||||
toView $ CEvtMemberAcceptedByOther user gInfo m referencedMember'
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m) msg brokerTs (CIRcvGroupEvent gEvent)
|
||||
groupMsgToView cInfo ci
|
||||
toView $ CEvtMemberAcceptedByOther user gInfo' m referencedMember'
|
||||
where
|
||||
newMemberStatus refMem = case memberConn refMem of
|
||||
Just c | connReady c -> GSMemConnected
|
||||
@@ -2112,8 +2118,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
GAAccepted -> do
|
||||
membership' <- withStore' $ \db -> updateGroupMemberAccepted db user membership GSMemConnected role
|
||||
let scopeInfo = Just $ GCSIMemberSupport {groupMember_ = Nothing}
|
||||
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo scopeInfo m) msg brokerTs (CIRcvGroupEvent RGEUserAccepted)
|
||||
groupMsgToView gInfo scopeInfo ci
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo scopeInfo m) msg brokerTs (CIRcvGroupEvent RGEUserAccepted)
|
||||
groupMsgToView cInfo ci
|
||||
toView $ CEvtUserJoinedGroup user gInfo {membership = membership'} m
|
||||
let cd = CDGroupRcv gInfo Nothing m
|
||||
createInternalChatItem user cd (CIRcvGroupE2EEInfo E2EInfo {pqEnabled = PQEncOff}) Nothing
|
||||
@@ -2265,7 +2271,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
then do
|
||||
g <- asks random
|
||||
dhKeyPair <- atomically $ if encryptedCall callType then Just <$> C.generateKeyPair g else pure Nothing
|
||||
ci <- saveCallItem CISCallPending
|
||||
(ci, cInfo) <- saveCallItem CISCallPending
|
||||
callUUID <- UUID.toText <$> liftIO V4.nextRandom
|
||||
let sharedKey = C.Key . C.dhBytes' <$> (C.dh' <$> callDhPubKey <*> (snd <$> dhKeyPair))
|
||||
callState = CallInvitationReceived {peerCallType = callType, localDhPubKey = fst <$> dhKeyPair, sharedKey}
|
||||
@@ -2278,15 +2284,15 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
call_ <- atomically (TM.lookupInsert contactId call' calls)
|
||||
forM_ call_ $ \call -> updateCallItemStatus user ct call WCSDisconnected Nothing
|
||||
toView $ CEvtCallInvitation RcvCallInvitation {user, contact = ct, callType, sharedKey, callUUID, callTs = chatItemTs' ci}
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci]
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
|
||||
else featureRejected CFCalls
|
||||
where
|
||||
brokerTs = metaBrokerTs msgMeta
|
||||
saveCallItem status = saveRcvChatItemNoParse user (CDDirectRcv ct) msg brokerTs (CIRcvCall status 0)
|
||||
featureRejected f = do
|
||||
let content = ciContentNoParse $ CIRcvChatFeatureRejected f
|
||||
ci <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content Nothing Nothing False M.empty
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat ct) ci]
|
||||
(ci, cInfo) <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ brokerTs content Nothing Nothing False M.empty
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
|
||||
|
||||
-- to party initiating call
|
||||
xCallOffer :: Contact -> CallId -> CallOffer -> RcvMessage -> CM ()
|
||||
@@ -2457,27 +2463,37 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
unless (sameMemberId memId $ membership gInfo) $
|
||||
withStore' (\db -> runExceptT $ getGroupMemberByMemberId db vr user gInfo memId) >>= \case
|
||||
Right unknownMember@GroupMember {memberStatus = GSMemUnknown} -> do
|
||||
updatedMember <- withStore $ \db -> updateUnknownMemberAnnounced db vr user m unknownMember memInfo initialStatus
|
||||
toView $ CEvtUnknownMemberAnnounced user gInfo m unknownMember updatedMember
|
||||
memberAnnouncedToView updatedMember
|
||||
(updatedMember, gInfo') <- withStore $ \db -> do
|
||||
updatedMember <- updateUnknownMemberAnnounced db vr user m unknownMember memInfo initialStatus
|
||||
gInfo' <- if memberPending updatedMember
|
||||
then liftIO $ increaseGroupMembersRequireAttention db user gInfo
|
||||
else pure gInfo
|
||||
pure (updatedMember, gInfo')
|
||||
toView $ CEvtUnknownMemberAnnounced user gInfo' m unknownMember updatedMember
|
||||
memberAnnouncedToView updatedMember gInfo'
|
||||
Right _ -> messageError "x.grp.mem.new error: member already exists"
|
||||
Left _ -> do
|
||||
newMember <- withStore $ \db -> createNewGroupMember db user gInfo m memInfo GCPostMember initialStatus
|
||||
memberAnnouncedToView newMember
|
||||
(newMember, gInfo') <- withStore $ \db -> do
|
||||
newMember <- createNewGroupMember db user gInfo m memInfo GCPostMember initialStatus
|
||||
gInfo' <- if memberPending newMember
|
||||
then liftIO $ increaseGroupMembersRequireAttention db user gInfo
|
||||
else pure gInfo
|
||||
pure (newMember, gInfo')
|
||||
memberAnnouncedToView newMember gInfo'
|
||||
where
|
||||
initialStatus = case msgScope_ of
|
||||
Just (MSMember _) -> GSMemPendingReview
|
||||
_ -> GSMemAnnounced
|
||||
memberAnnouncedToView announcedMember@GroupMember {groupMemberId, memberProfile} = do
|
||||
memberAnnouncedToView announcedMember@GroupMember {groupMemberId, memberProfile} gInfo' = do
|
||||
(announcedMember', scopeInfo) <- getMemNewChatScope announcedMember
|
||||
let event = RGEMemberAdded groupMemberId (fromLocalProfile memberProfile)
|
||||
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo scopeInfo m) msg brokerTs (CIRcvGroupEvent event)
|
||||
groupMsgToView gInfo scopeInfo ci
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m) msg brokerTs (CIRcvGroupEvent event)
|
||||
groupMsgToView cInfo ci
|
||||
case scopeInfo of
|
||||
Just (GCSIMemberSupport _) -> do
|
||||
createInternalChatItem user (CDGroupRcv gInfo scopeInfo m) (CIRcvGroupEvent RGENewMemberPendingReview) (Just brokerTs)
|
||||
createInternalChatItem user (CDGroupRcv gInfo' scopeInfo m) (CIRcvGroupEvent RGENewMemberPendingReview) (Just brokerTs)
|
||||
_ -> pure ()
|
||||
toView $ CEvtJoinedGroupMemberConnecting user gInfo m announcedMember'
|
||||
toView $ CEvtJoinedGroupMemberConnecting user gInfo' m announcedMember'
|
||||
getMemNewChatScope announcedMember = case msgScope_ of
|
||||
Nothing -> pure (announcedMember, Nothing)
|
||||
Just (MSMember _) -> do
|
||||
@@ -2571,8 +2587,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
| otherwise = do
|
||||
withStore' $ \db -> updateGroupMemberRole db user member memRole
|
||||
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo' m
|
||||
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
|
||||
groupMsgToView gInfo'' scopeInfo ci
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
|
||||
groupMsgToView cInfo ci
|
||||
toView CEvtMemberRole {user, groupInfo = gInfo'', byMember = m', member = member {memberRole = memRole}, fromRole, toRole = memRole}
|
||||
|
||||
checkHostRole :: GroupMember -> GroupMemberRole -> CM ()
|
||||
@@ -2600,8 +2616,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
toggleNtf bm' (not blocked)
|
||||
let ciContent = CIRcvGroupEvent $ RGEMemberBlocked bmId (fromLocalProfile bmp) blocked
|
||||
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
|
||||
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs ciContent
|
||||
groupMsgToView gInfo' scopeInfo ci
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs ciContent
|
||||
groupMsgToView cInfo ci
|
||||
toView CEvtMemberBlockedForAll {user, groupInfo = gInfo', byMember = m', member = bm, blocked}
|
||||
Left (SEGroupMemberNotFoundByMemberId _) -> do
|
||||
bm <- createUnknownMember gInfo memId
|
||||
@@ -2685,8 +2701,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
| otherwise = a
|
||||
deleteMemberItem gEvent = do
|
||||
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
|
||||
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
|
||||
groupMsgToView gInfo' scopeInfo ci
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
|
||||
groupMsgToView cInfo ci
|
||||
deleteMessages :: MsgDirectionI d => GroupMember -> SMsgDirection d -> CM ()
|
||||
deleteMessages delMem msgDir
|
||||
| groupFeatureMemberAllowed SGFFullDelete m gInfo = deleteGroupMemberCIs user gInfo delMem m msgDir
|
||||
@@ -2698,8 +2714,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
-- member record is not deleted to allow creation of "member left" chat item
|
||||
withStore' $ \db -> updateGroupMemberStatus db userId m GSMemLeft
|
||||
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
|
||||
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEMemberLeft)
|
||||
groupMsgToView gInfo' scopeInfo ci
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEMemberLeft)
|
||||
groupMsgToView cInfo ci
|
||||
toView $ CEvtLeftMember user gInfo' m' {memberStatus = GSMemLeft}
|
||||
|
||||
xGrpDel :: GroupInfo -> GroupMember -> RcvMessage -> UTCTime -> CM ()
|
||||
@@ -2712,8 +2728,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
-- member records are not deleted to keep history
|
||||
deleteMembersConnections user ms
|
||||
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo m
|
||||
ci <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEGroupDeleted)
|
||||
groupMsgToView gInfo'' scopeInfo ci
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEGroupDeleted)
|
||||
groupMsgToView cInfo ci
|
||||
toView $ CEvtGroupDeleted user gInfo'' {membership = membership {memberStatus = GSMemGroupDeleted}} m'
|
||||
|
||||
xGrpInfo :: GroupInfo -> GroupMember -> GroupProfile -> RcvMessage -> UTCTime -> CM ()
|
||||
@@ -2726,8 +2742,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
toView $ CEvtGroupUpdated user g g'' (Just m')
|
||||
let cd = CDGroupRcv g'' scopeInfo m'
|
||||
unless (sameGroupProfileInfo p p') $ do
|
||||
ci <- saveRcvChatItemNoParse user cd msg brokerTs (CIRcvGroupEvent $ RGEGroupUpdated p')
|
||||
groupMsgToView g'' scopeInfo ci
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user cd msg brokerTs (CIRcvGroupEvent $ RGEGroupUpdated p')
|
||||
groupMsgToView cInfo ci
|
||||
createGroupFeatureChangedItems user cd CIRcvGroupFeature g g''
|
||||
Just _ -> updateGroupPrefs_ g m $ fromMaybe defaultBusinessGroupPrefs $ groupPreferences p'
|
||||
|
||||
@@ -2787,8 +2803,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
createInternalChatItem user (CDGroupRcv g' scopeInfo m'') (CIRcvGroupEvent RGEMemberCreatedContact) Nothing
|
||||
toView $ CEvtNewMemberContactReceivedInv user mCt' g' m''
|
||||
forM_ mContent_ $ \mc -> do
|
||||
ci <- saveRcvChatItem user (CDDirectRcv mCt') msg brokerTs (CIRcvMsgContent mc, msgContentTexts mc)
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv (DirectChat mCt') ci]
|
||||
(ci, cInfo) <- saveRcvChatItem user (CDDirectRcv mCt') msg brokerTs (CIRcvMsgContent mc, msgContentTexts mc)
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
|
||||
|
||||
securityCodeChanged :: Contact -> CM ()
|
||||
securityCodeChanged ct = do
|
||||
|
||||
@@ -350,14 +350,13 @@ data ChatStats = ChatStats
|
||||
{ unreadCount :: Int, -- returned both in /_get chat initial API and in /_get chats API
|
||||
unreadMentions :: Int, -- returned both in /_get chat initial API and in /_get chats API
|
||||
reportsCount :: Int, -- returned both in /_get chat initial API and in /_get chats API
|
||||
supportChatsUnreadCount :: Int, -- returned both in /_get chat initial API and in /_get chats API
|
||||
minUnreadItemId :: ChatItemId,
|
||||
unreadChat :: Bool
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
emptyChatStats :: ChatStats
|
||||
emptyChatStats = ChatStats 0 0 0 0 0 False
|
||||
emptyChatStats = ChatStats 0 0 0 0 False
|
||||
|
||||
data NavigationInfo = NavigationInfo
|
||||
{ afterUnread :: Int,
|
||||
|
||||
@@ -137,7 +137,8 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
|
||||
@@ -80,6 +80,8 @@ module Simplex.Chat.Store.Groups
|
||||
updateGroupMemberStatus,
|
||||
updateGroupMemberStatusById,
|
||||
updateGroupMemberAccepted,
|
||||
updateGroupMembersRequireAttention,
|
||||
increaseGroupMembersRequireAttention,
|
||||
createNewGroupMember,
|
||||
checkGroupMemberHasItems,
|
||||
deleteGroupMember,
|
||||
@@ -279,7 +281,8 @@ getGroupAndMember db User {userId, userContactId} groupMemberId vr = do
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -362,7 +365,8 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
customData = Nothing
|
||||
customData = Nothing,
|
||||
membersRequireAttention = 0
|
||||
}
|
||||
|
||||
-- | creates a new group record for the group the current user was invited to, or returns an existing one
|
||||
@@ -432,7 +436,8 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
|
||||
chatTags = [],
|
||||
chatItemTTL = Nothing,
|
||||
uiThemes = Nothing,
|
||||
customData = Nothing
|
||||
customData = Nothing,
|
||||
membersRequireAttention = 0
|
||||
},
|
||||
groupMemberId
|
||||
)
|
||||
@@ -768,7 +773,8 @@ getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
|
||||
SELECT
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
|
||||
mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
||||
mu.created_at, mu.updated_at,
|
||||
@@ -1224,6 +1230,37 @@ updateGroupMemberAccepted db User {userId} m@GroupMember {groupMemberId} status
|
||||
(status, role, currentTs, userId, groupMemberId)
|
||||
pure m {memberStatus = status, memberRole = role, updatedAt = currentTs}
|
||||
|
||||
updateGroupMembersRequireAttention :: DB.Connection -> User -> GroupInfo -> GroupMember -> GroupMember -> IO GroupInfo
|
||||
updateGroupMembersRequireAttention db user@User {userId} g@GroupInfo {groupId, membersRequireAttention} member member'
|
||||
| nowRequires && not didRequire =
|
||||
increaseGroupMembersRequireAttention db user g
|
||||
| not nowRequires && didRequire = do
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE groups
|
||||
SET members_require_attention = members_require_attention - 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|]
|
||||
(userId, groupId)
|
||||
pure g {membersRequireAttention = membersRequireAttention - 1}
|
||||
| otherwise = pure g
|
||||
where
|
||||
didRequire = gmRequiresAttention member
|
||||
nowRequires = gmRequiresAttention member'
|
||||
|
||||
increaseGroupMembersRequireAttention :: DB.Connection -> User -> GroupInfo -> IO GroupInfo
|
||||
increaseGroupMembersRequireAttention db User {userId} g@GroupInfo {groupId, membersRequireAttention} = do
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE groups
|
||||
SET members_require_attention = members_require_attention + 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|]
|
||||
(userId, groupId)
|
||||
pure g {membersRequireAttention = membersRequireAttention + 1}
|
||||
|
||||
-- | add new member with profile
|
||||
createNewGroupMember :: DB.Connection -> User -> GroupInfo -> GroupMember -> MemberInfo -> GroupMemberCategory -> GroupMemberStatus -> ExceptT StoreError IO GroupMember
|
||||
createNewGroupMember db user gInfo invitingMember memInfo@MemberInfo {profile} memCategory memStatus = do
|
||||
@@ -1561,7 +1598,8 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = do
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
|
||||
@@ -369,56 +369,103 @@ type NewQuoteRow = (Maybe SharedMsgId, Maybe UTCTime, Maybe MsgContent, Maybe Bo
|
||||
data MemberAttention = MAInc Int | MAReset
|
||||
deriving (Show)
|
||||
|
||||
updateChatTsStats :: DB.Connection -> User -> ChatDirection c d -> UTCTime -> Maybe (Int, MemberAttention, Int) -> IO ()
|
||||
updateChatTsStats db User {userId} chatDirection chatTs chatStats_ = case toChatInfo chatDirection of
|
||||
DirectChat Contact {contactId} ->
|
||||
updateChatTsStats :: DB.Connection -> VersionRangeChat -> User -> ChatDirection c d -> UTCTime -> Maybe (Int, MemberAttention, Int) -> IO (ChatInfo c)
|
||||
updateChatTsStats db vr user@User {userId} chatDirection chatTs chatStats_ = case toChatInfo chatDirection of
|
||||
DirectChat ct@Contact {contactId} -> do
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE contacts SET chat_ts = ?, chat_deleted = 0 WHERE user_id = ? AND contact_id = ?"
|
||||
(chatTs, userId, contactId)
|
||||
GroupChat GroupInfo {groupId, membership} scopeInfo -> do
|
||||
pure $ DirectChat ct {chatTs = Just chatTs}
|
||||
GroupChat g@GroupInfo {groupId} Nothing -> do
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE groups SET chat_ts = ? WHERE user_id = ? AND group_id = ?"
|
||||
(chatTs, userId, groupId)
|
||||
forM_ scopeInfo $ \GCSIMemberSupport {groupMember_} -> do
|
||||
let gmId = groupMemberId' $ fromMaybe membership groupMember_
|
||||
case chatStats_ of
|
||||
Nothing ->
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE group_members SET support_chat_ts = ? WHERE group_member_id = ?"
|
||||
(chatTs, gmId)
|
||||
Just (unread, MAInc unanswered, mentions) ->
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET support_chat_ts = ?,
|
||||
support_chat_items_unread = support_chat_items_unread + ?,
|
||||
support_chat_items_member_attention = support_chat_items_member_attention + ?,
|
||||
support_chat_items_mentions = support_chat_items_mentions + ?
|
||||
WHERE group_member_id = ?
|
||||
|]
|
||||
(chatTs, unread, unanswered, mentions, gmId)
|
||||
Just (unread, MAReset, mentions) ->
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET support_chat_ts = ?,
|
||||
support_chat_items_unread = support_chat_items_unread + ?,
|
||||
support_chat_items_member_attention = 0,
|
||||
support_chat_items_mentions = support_chat_items_mentions + ?
|
||||
WHERE group_member_id = ?
|
||||
|]
|
||||
(chatTs, unread, mentions, gmId)
|
||||
LocalChat NoteFolder {noteFolderId} ->
|
||||
pure $ GroupChat g {chatTs = Just chatTs} Nothing
|
||||
GroupChat g@GroupInfo {groupId, membership, membersRequireAttention} (Just GCSIMemberSupport {groupMember_}) ->
|
||||
case groupMember_ of
|
||||
Nothing -> do
|
||||
membership' <- updateGMStats membership
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE groups SET chat_ts = ? WHERE user_id = ? AND group_id = ?"
|
||||
(chatTs, userId, groupId)
|
||||
pure $ GroupChat g {membership = membership', chatTs = Just chatTs} (Just $ GCSIMemberSupport Nothing)
|
||||
Just member -> do
|
||||
member' <- updateGMStats member
|
||||
let didRequire = gmRequiresAttention member
|
||||
nowRequires = gmRequiresAttention member'
|
||||
if
|
||||
| nowRequires && not didRequire -> do
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE groups
|
||||
SET chat_ts = ?,
|
||||
members_require_attention = members_require_attention + 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|]
|
||||
(chatTs, userId, groupId)
|
||||
pure $ GroupChat g {membersRequireAttention = membersRequireAttention + 1, chatTs = Just chatTs} (Just $ GCSIMemberSupport (Just member'))
|
||||
| not nowRequires && didRequire -> do
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE groups
|
||||
SET chat_ts = ?,
|
||||
members_require_attention = members_require_attention - 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|]
|
||||
(chatTs, userId, groupId)
|
||||
pure $ GroupChat g {membersRequireAttention = membersRequireAttention - 1, chatTs = Just chatTs} (Just $ GCSIMemberSupport (Just member'))
|
||||
| otherwise -> do
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE groups SET chat_ts = ? WHERE user_id = ? AND group_id = ?"
|
||||
(chatTs, userId, groupId)
|
||||
pure $ GroupChat g {chatTs = Just chatTs} (Just $ GCSIMemberSupport (Just member'))
|
||||
where
|
||||
updateGMStats m@GroupMember {groupMemberId} = do
|
||||
case chatStats_ of
|
||||
Nothing ->
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE group_members SET support_chat_ts = ? WHERE group_member_id = ?"
|
||||
(chatTs, groupMemberId)
|
||||
Just (unread, MAInc unanswered, mentions) ->
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET support_chat_ts = ?,
|
||||
support_chat_items_unread = support_chat_items_unread + ?,
|
||||
support_chat_items_member_attention = support_chat_items_member_attention + ?,
|
||||
support_chat_items_mentions = support_chat_items_mentions + ?
|
||||
WHERE group_member_id = ?
|
||||
|]
|
||||
(chatTs, unread, unanswered, mentions, groupMemberId)
|
||||
Just (unread, MAReset, mentions) ->
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET support_chat_ts = ?,
|
||||
support_chat_items_unread = support_chat_items_unread + ?,
|
||||
support_chat_items_member_attention = 0,
|
||||
support_chat_items_mentions = support_chat_items_mentions + ?
|
||||
WHERE group_member_id = ?
|
||||
|]
|
||||
(chatTs, unread, mentions, groupMemberId)
|
||||
m_ <- runExceptT $ getGroupMemberById db vr user groupMemberId
|
||||
pure $ either (const m) id m_ -- Left shouldn't happen, but types require it
|
||||
LocalChat nf@NoteFolder {noteFolderId} -> do
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE note_folders SET chat_ts = ? WHERE user_id = ? AND note_folder_id = ?"
|
||||
(chatTs, userId, noteFolderId)
|
||||
_ -> pure ()
|
||||
pure $ LocalChat nf {chatTs = chatTs}
|
||||
cInfo -> pure cInfo
|
||||
|
||||
setSupportChatTs :: DB.Connection -> GroupMemberId -> UTCTime -> IO ()
|
||||
setSupportChatTs db groupMemberId chatTs =
|
||||
@@ -636,13 +683,13 @@ type ChatStatsRow = (Int, ChatItemId, BoolInt)
|
||||
|
||||
toChatStats :: ChatStatsRow -> ChatStats
|
||||
toChatStats (unreadCount, minUnreadItemId, BI unreadChat) =
|
||||
ChatStats {unreadCount, unreadMentions = 0, reportsCount = 0, supportChatsUnreadCount = 0, minUnreadItemId, unreadChat}
|
||||
ChatStats {unreadCount, unreadMentions = 0, reportsCount = 0, minUnreadItemId, unreadChat}
|
||||
|
||||
type GroupStatsRow = (Int, Int, Int, Int, ChatItemId, BoolInt)
|
||||
type GroupStatsRow = (Int, Int, Int, ChatItemId, BoolInt)
|
||||
|
||||
toGroupStats :: GroupStatsRow -> ChatStats
|
||||
toGroupStats (unreadCount, unreadMentions, reportsCount, supportChatsUnreadCount, minUnreadItemId, BI unreadChat) =
|
||||
ChatStats {unreadCount, unreadMentions, reportsCount, supportChatsUnreadCount, minUnreadItemId, unreadChat}
|
||||
toGroupStats (unreadCount, unreadMentions, reportsCount, minUnreadItemId, BI unreadChat) =
|
||||
ChatStats {unreadCount, unreadMentions, reportsCount, minUnreadItemId, unreadChat}
|
||||
|
||||
findDirectChatPreviews_ :: DB.Connection -> User -> PaginationByTime -> ChatListQuery -> IO [AChatPreviewData]
|
||||
findDirectChatPreviews_ db User {userId} pagination clq =
|
||||
@@ -763,7 +810,6 @@ findGroupChatPreviews_ db User {userId} pagination clq =
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -780,16 +826,8 @@ findGroupChatPreviews_ db User {userId} pagination clq =
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
|]
|
||||
baseParams = (userId, userId, CISRcvNew, userId, MCReport_, BI False, userId, CISRcvNew)
|
||||
baseParams = (userId, userId, CISRcvNew, userId, MCReport_, BI False)
|
||||
getPreviews = case clq of
|
||||
CLQFilters {favorite = False, unread = False} -> do
|
||||
let q = baseQuery <> " WHERE g.user_id = ?"
|
||||
@@ -1494,16 +1532,14 @@ getGroupChatInitial_ db user g scopeInfo_ contentFilter count = do
|
||||
where
|
||||
getStats minUnreadItemId (unreadCount, unreadMentions) = do
|
||||
reportsCount <- getGroupReportsCount_ db user g False
|
||||
supportChatsUnreadCount <- getGroupSupportChatsUnreadCount_ db user g
|
||||
pure ChatStats {unreadCount, unreadMentions, reportsCount, supportChatsUnreadCount, minUnreadItemId, unreadChat = False}
|
||||
pure ChatStats {unreadCount, unreadMentions, reportsCount, minUnreadItemId, unreadChat = False}
|
||||
|
||||
getGroupStats_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> ExceptT StoreError IO ChatStats
|
||||
getGroupStats_ db user g scopeInfo_ = do
|
||||
minUnreadItemId <- fromMaybe 0 <$> getGroupMinUnreadId_ db user g scopeInfo_ Nothing
|
||||
(unreadCount, unreadMentions) <- getGroupUnreadCount_ db user g scopeInfo_ Nothing
|
||||
reportsCount <- liftIO $ getGroupReportsCount_ db user g False
|
||||
supportChatsUnreadCount <- liftIO $ getGroupSupportChatsUnreadCount_ db user g
|
||||
pure ChatStats {unreadCount, unreadMentions, reportsCount, supportChatsUnreadCount, minUnreadItemId, unreadChat = False}
|
||||
pure ChatStats {unreadCount, unreadMentions, reportsCount, minUnreadItemId, unreadChat = False}
|
||||
|
||||
getGroupMinUnreadId_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> ExceptT StoreError IO (Maybe ChatItemId)
|
||||
getGroupMinUnreadId_ db user g scopeInfo_ contentFilter =
|
||||
@@ -1527,14 +1563,6 @@ getGroupReportsCount_ db User {userId} GroupInfo {groupId} archived =
|
||||
"SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0"
|
||||
(userId, groupId, MCReport_, BI archived)
|
||||
|
||||
getGroupSupportChatsUnreadCount_ :: DB.Connection -> User -> GroupInfo -> IO Int
|
||||
getGroupSupportChatsUnreadCount_ db User {userId} GroupInfo {groupId} =
|
||||
fromOnly . head
|
||||
<$> DB.query
|
||||
db
|
||||
"SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND group_scope_tag IS NOT NULL AND item_status = ?"
|
||||
(userId, groupId, CISRcvNew)
|
||||
|
||||
queryUnreadGroupItems :: FromRow r => DB.Connection -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> Maybe MsgContentTag -> Query -> Query -> ExceptT StoreError IO [r]
|
||||
queryUnreadGroupItems db User {userId} GroupInfo {groupId} scopeInfo_ contentFilter baseQuery orderLimit =
|
||||
case (scopeInfo_, contentFilter) of
|
||||
@@ -1954,14 +1982,14 @@ getGroupUnreadTimedItems db User {userId} groupId =
|
||||
|]
|
||||
(userId, groupId, CISRcvNew)
|
||||
|
||||
updateGroupChatItemsReadList :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatScope -> NonEmpty ChatItemId -> IO [(ChatItemId, Int)]
|
||||
updateGroupChatItemsReadList db User {userId} GroupInfo {groupId, membership} scope itemIds = do
|
||||
currentTs <- getCurrentTime
|
||||
updateGroupChatItemsReadList :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> Maybe GroupChatScope -> NonEmpty ChatItemId -> ExceptT StoreError IO ([(ChatItemId, Int)], GroupInfo)
|
||||
updateGroupChatItemsReadList db vr user@User {userId} g@GroupInfo {groupId, membership, membersRequireAttention} scope itemIds = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
-- Possible improvement is to differentiate retrieval queries for each scope,
|
||||
-- but we rely on UI to not pass item IDs from incorrect scope.
|
||||
readItemsData <- catMaybes . L.toList <$> mapM (getUpdateGroupItem currentTs) itemIds
|
||||
updateChatStats readItemsData
|
||||
pure $ timedItems readItemsData
|
||||
readItemsData <- liftIO $ catMaybes . L.toList <$> mapM (getUpdateGroupItem currentTs) itemIds
|
||||
g' <- updateChatStats readItemsData
|
||||
pure (timedItems readItemsData, g')
|
||||
where
|
||||
getUpdateGroupItem :: UTCTime -> ChatItemId -> IO (Maybe (ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt))
|
||||
getUpdateGroupItem currentTs itemId =
|
||||
@@ -1974,36 +2002,61 @@ updateGroupChatItemsReadList db User {userId} GroupInfo {groupId, membership} sc
|
||||
RETURNING chat_item_id, timed_ttl, timed_delete_at, group_member_id, user_mention
|
||||
|]
|
||||
(CISRcvRead, currentTs, userId, groupId, CISRcvNew, itemId)
|
||||
updateChatStats :: [(ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt)] -> IO ()
|
||||
updateChatStats :: [(ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt)] -> ExceptT StoreError IO GroupInfo
|
||||
updateChatStats readItemsData = case scope of
|
||||
Nothing -> pure ()
|
||||
Just GCSMemberSupport {groupMemberId_} -> do
|
||||
let unread = length readItemsData
|
||||
(unanswered, mentions) = decStats
|
||||
gmId = fromMaybe (groupMemberId' membership) groupMemberId_
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET support_chat_items_unread = support_chat_items_unread - ?,
|
||||
support_chat_items_member_attention = support_chat_items_member_attention - ?,
|
||||
support_chat_items_mentions = support_chat_items_mentions - ?
|
||||
WHERE group_member_id = ?
|
||||
|]
|
||||
(unread, unanswered, mentions, gmId)
|
||||
Nothing -> pure g
|
||||
Just GCSMemberSupport {groupMemberId_} -> case groupMemberId_ of
|
||||
Nothing -> do
|
||||
membership' <- updateGMStats membership
|
||||
pure g {membership = membership'}
|
||||
Just groupMemberId -> do
|
||||
member <- getGroupMemberById db vr user groupMemberId
|
||||
member' <- updateGMStats member
|
||||
let didRequire = gmRequiresAttention member
|
||||
nowRequires = gmRequiresAttention member'
|
||||
if (not nowRequires && didRequire)
|
||||
then do
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE groups
|
||||
SET members_require_attention = members_require_attention - 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|]
|
||||
(userId, groupId)
|
||||
pure g {membersRequireAttention = membersRequireAttention - 1}
|
||||
else
|
||||
pure g
|
||||
where
|
||||
decStats :: (Int, Int)
|
||||
decStats = foldl' countItem (0, 0) readItemsData
|
||||
updateGMStats GroupMember {groupMemberId} = do
|
||||
let unread = length readItemsData
|
||||
(unanswered, mentions) = decStats
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET support_chat_items_unread = support_chat_items_unread - ?,
|
||||
support_chat_items_member_attention = support_chat_items_member_attention - ?,
|
||||
support_chat_items_mentions = support_chat_items_mentions - ?
|
||||
WHERE group_member_id = ?
|
||||
|]
|
||||
(unread, unanswered, mentions, groupMemberId)
|
||||
getGroupMemberById db vr user groupMemberId
|
||||
where
|
||||
countItem :: (Int, Int) -> (ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt) -> (Int, Int)
|
||||
countItem (!unanswered, !mentions) (_, _, _, itemGMId_, userMention_) =
|
||||
let unanswered' = case (groupMemberId_, itemGMId_) of
|
||||
(Just scopeGMId, Just itemGMId) | itemGMId == scopeGMId -> unanswered + 1
|
||||
_ -> unanswered
|
||||
mentions' = case userMention_ of
|
||||
Just (BI True) -> mentions + 1
|
||||
_ -> mentions
|
||||
in (unanswered', mentions')
|
||||
decStats :: (Int, Int)
|
||||
decStats = foldl' countItem (0, 0) readItemsData
|
||||
where
|
||||
countItem :: (Int, Int) -> (ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt) -> (Int, Int)
|
||||
countItem (!unanswered, !mentions) (_, _, _, itemGMId_, userMention_) =
|
||||
let unanswered' = case (groupMemberId_, itemGMId_) of
|
||||
(Just scopeGMId, Just itemGMId) | itemGMId == scopeGMId -> unanswered + 1
|
||||
_ -> unanswered
|
||||
mentions' = case userMention_ of
|
||||
Just (BI True) -> mentions + 1
|
||||
_ -> mentions
|
||||
in (unanswered', mentions')
|
||||
timedItems :: [(ChatItemId, Maybe Int, Maybe UTCTime, Maybe GroupMemberId, Maybe BoolInt)] -> [(ChatItemId, Int)]
|
||||
timedItems = foldl' addTimedItem []
|
||||
where
|
||||
|
||||
@@ -17,6 +17,8 @@ ALTER TABLE group_members ADD COLUMN support_chat_items_unread BIGINT NOT NULL D
|
||||
ALTER TABLE group_members ADD COLUMN support_chat_items_member_attention BIGINT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE group_members ADD COLUMN support_chat_items_mentions BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE groups ADD COLUMN members_require_attention BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE chat_items ADD COLUMN group_scope_tag TEXT;
|
||||
ALTER TABLE chat_items ADD COLUMN group_scope_group_member_id BIGINT REFERENCES group_members(group_member_id) ON DELETE CASCADE;
|
||||
|
||||
@@ -53,6 +55,8 @@ DROP INDEX idx_chat_items_group_scope_group_member_id;
|
||||
ALTER TABLE chat_items DROP COLUMN group_scope_tag;
|
||||
ALTER TABLE chat_items DROP COLUMN group_scope_group_member_id;
|
||||
|
||||
ALTER TABLE groups DROP COLUMN members_require_attention;
|
||||
|
||||
ALTER TABLE group_members DROP COLUMN support_chat_ts;
|
||||
ALTER TABLE group_members DROP COLUMN support_chat_items_unread;
|
||||
ALTER TABLE group_members DROP COLUMN support_chat_items_member_attention;
|
||||
|
||||
@@ -15,6 +15,8 @@ ALTER TABLE group_members ADD COLUMN support_chat_items_unread INTEGER NOT NULL
|
||||
ALTER TABLE group_members ADD COLUMN support_chat_items_member_attention INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE group_members ADD COLUMN support_chat_items_mentions INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE groups ADD COLUMN members_require_attention INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE chat_items ADD COLUMN group_scope_tag TEXT;
|
||||
ALTER TABLE chat_items ADD COLUMN group_scope_group_member_id INTEGER REFERENCES group_members(group_member_id) ON DELETE CASCADE;
|
||||
|
||||
@@ -50,6 +52,8 @@ DROP INDEX idx_chat_items_group_scope_group_member_id;
|
||||
ALTER TABLE chat_items DROP COLUMN group_scope_tag;
|
||||
ALTER TABLE chat_items DROP COLUMN group_scope_group_member_id;
|
||||
|
||||
ALTER TABLE groups DROP COLUMN members_require_attention;
|
||||
|
||||
ALTER TABLE group_members DROP COLUMN support_chat_ts;
|
||||
ALTER TABLE group_members DROP COLUMN support_chat_items_unread;
|
||||
ALTER TABLE group_members DROP COLUMN support_chat_items_member_attention;
|
||||
|
||||
@@ -1,3 +1,39 @@
|
||||
Query:
|
||||
UPDATE groups
|
||||
SET members_require_attention = members_require_attention - 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE group_members
|
||||
SET support_chat_items_unread = support_chat_items_unread - ?,
|
||||
support_chat_items_member_attention = support_chat_items_member_attention - ?,
|
||||
support_chat_items_mentions = support_chat_items_mentions - ?
|
||||
WHERE group_member_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE groups
|
||||
SET chat_ts = ?,
|
||||
members_require_attention = members_require_attention + 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE groups
|
||||
SET chat_ts = ?,
|
||||
members_require_attention = members_require_attention - 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO group_members
|
||||
( group_id, member_id, member_role, member_category, member_status, invited_by, invited_by_group_member_id,
|
||||
@@ -36,7 +72,8 @@ Query:
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -81,6 +118,28 @@ Query:
|
||||
Plan:
|
||||
SEARCH contact_requests USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE group_members
|
||||
SET support_chat_ts = ?,
|
||||
support_chat_items_unread = support_chat_items_unread + ?,
|
||||
support_chat_items_member_attention = 0,
|
||||
support_chat_items_mentions = support_chat_items_mentions + ?
|
||||
WHERE group_member_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE group_members
|
||||
SET support_chat_ts = ?,
|
||||
support_chat_items_unread = support_chat_items_unread + ?,
|
||||
support_chat_items_member_attention = support_chat_items_member_attention + ?,
|
||||
support_chat_items_mentions = support_chat_items_mentions + ?
|
||||
WHERE group_member_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
INSERT INTO contact_requests
|
||||
(user_contact_link_id, agent_invitation_id, peer_chat_min_version, peer_chat_max_version, contact_profile_id, local_display_name, user_id,
|
||||
@@ -621,38 +680,6 @@ Query:
|
||||
Plan:
|
||||
SEARCH chat_items USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE group_members
|
||||
SET support_chat_items_unread = support_chat_items_unread - ?,
|
||||
support_chat_items_member_attention = support_chat_items_member_attention - ?,
|
||||
support_chat_items_mentions = support_chat_items_mentions - ?
|
||||
WHERE group_member_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE group_members
|
||||
SET support_chat_ts = ?,
|
||||
support_chat_items_unread = support_chat_items_unread + ?,
|
||||
support_chat_items_member_attention = 0,
|
||||
support_chat_items_mentions = support_chat_items_mentions + ?
|
||||
WHERE group_member_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE group_members
|
||||
SET support_chat_ts = ?,
|
||||
support_chat_items_unread = support_chat_items_unread + ?,
|
||||
support_chat_items_member_attention = support_chat_items_member_attention + ?,
|
||||
support_chat_items_mentions = support_chat_items_mentions + ?
|
||||
WHERE group_member_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
DELETE FROM chat_item_reactions
|
||||
WHERE contact_id = ? AND shared_msg_id = ? AND reaction_sent = ? AND reaction = ?
|
||||
@@ -840,7 +867,8 @@ Query:
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -887,7 +915,8 @@ Query:
|
||||
SELECT
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
|
||||
mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
||||
mu.created_at, mu.updated_at,
|
||||
@@ -1270,16 +1299,6 @@ Query:
|
||||
Plan:
|
||||
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE group_members
|
||||
SET support_chat_items_unread = 0,
|
||||
support_chat_items_member_attention = 0,
|
||||
support_chat_items_mentions = 0
|
||||
WHERE group_member_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE group_profiles
|
||||
SET display_name = ?, full_name = ?, description = ?, image = ?, preferences = ?, member_admission = ?, updated_at = ?
|
||||
@@ -1294,6 +1313,14 @@ SEARCH group_profiles USING INTEGER PRIMARY KEY (rowid=?)
|
||||
LIST SUBQUERY 1
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE groups
|
||||
SET members_require_attention = members_require_attention - 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE user_contact_links
|
||||
SET auto_accept = ?, business_address = ?, auto_accept_incognito = ?, auto_reply_msg_content = ?
|
||||
@@ -1948,7 +1975,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -1965,14 +1991,6 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
WHERE g.user_id = ?
|
||||
@@ -1988,13 +2006,10 @@ MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
|
||||
SEARCH gp USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2012,7 +2027,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2029,14 +2043,6 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
|
||||
WHERE g.user_id = ?
|
||||
AND (g.favorite = 1
|
||||
@@ -2047,12 +2053,9 @@ MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2070,7 +2073,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2087,14 +2089,6 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
|
||||
WHERE g.user_id = ?
|
||||
AND (g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
||||
@@ -2104,12 +2098,9 @@ MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts<?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2127,7 +2118,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2144,14 +2134,6 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
|
||||
WHERE g.user_id = ?
|
||||
AND (g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
||||
@@ -2161,12 +2143,9 @@ MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts>?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2184,7 +2163,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2201,14 +2179,6 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
|
||||
WHERE g.user_id = ?
|
||||
AND (g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
||||
@@ -2218,12 +2188,9 @@ MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2241,7 +2208,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2258,14 +2224,6 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
|
||||
WHERE g.user_id = ?
|
||||
AND g.favorite = 1
|
||||
@@ -2275,12 +2233,9 @@ MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts<?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2298,7 +2253,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2315,14 +2269,6 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
|
||||
WHERE g.user_id = ?
|
||||
AND g.favorite = 1
|
||||
@@ -2332,12 +2278,9 @@ MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts>?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2355,7 +2298,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2372,14 +2314,6 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
|
||||
WHERE g.user_id = ?
|
||||
AND g.favorite = 1
|
||||
@@ -2389,12 +2323,9 @@ MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2412,7 +2343,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2429,26 +2359,15 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
WHERE g.user_id = ? AND g.chat_ts < ? ORDER BY g.chat_ts DESC LIMIT ?
|
||||
Plan:
|
||||
MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts<?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2466,7 +2385,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2483,26 +2401,15 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
WHERE g.user_id = ? AND g.chat_ts > ? ORDER BY g.chat_ts ASC LIMIT ?
|
||||
Plan:
|
||||
MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=? AND chat_ts>?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -2520,7 +2427,6 @@ Query:
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ChatStats.UnreadMentions, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(SupportChatsUnreadCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
@@ -2537,26 +2443,15 @@ Query:
|
||||
AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
GROUP BY group_id
|
||||
) ReportCount ON ReportCount.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS Count
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
AND group_scope_tag IS NOT NULL
|
||||
AND item_status = ?
|
||||
GROUP BY group_id
|
||||
) SupportChatsUnreadCount ON SupportChatsUnreadCount.group_id = g.group_id
|
||||
WHERE g.user_id = ? ORDER BY g.chat_ts DESC LIMIT ?
|
||||
Plan:
|
||||
MATERIALIZE ChatStats
|
||||
SEARCH chat_items USING INDEX idx_chat_items_groups_user_mention (user_id=? AND group_id>?)
|
||||
MATERIALIZE ReportCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id>?)
|
||||
MATERIALIZE SupportChatsUnreadCount
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id>?)
|
||||
SEARCH g USING INDEX idx_groups_chat_ts (user_id=?)
|
||||
SEARCH ChatStats USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH ReportCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
SEARCH SupportChatsUnreadCount USING AUTOMATIC COVERING INDEX (group_id=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH ci USING INDEX idx_chat_items_groups_item_ts (user_id=? AND group_id=?)
|
||||
|
||||
@@ -4526,6 +4421,14 @@ Query:
|
||||
Plan:
|
||||
SEARCH group_snd_item_statuses USING INDEX idx_group_snd_item_statuses_chat_item_id_group_member_id (chat_item_id=? AND group_member_id=?)
|
||||
|
||||
Query:
|
||||
UPDATE groups
|
||||
SET members_require_attention = members_require_attention + 1
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE groups
|
||||
SET via_group_link_uri_hash = (SELECT via_contact_uri_hash FROM connections WHERE connection_id = ?)
|
||||
@@ -4625,7 +4528,8 @@ Query:
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -4648,7 +4552,8 @@ Query:
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
@@ -5638,10 +5543,6 @@ Query: SELECT COUNT(1) FROM chat_item_versions WHERE chat_item_id = ?
|
||||
Plan:
|
||||
SEARCH chat_item_versions USING COVERING INDEX idx_chat_item_versions_chat_item_id (chat_item_id=?)
|
||||
|
||||
Query: SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND group_scope_tag IS NOT NULL AND item_status = ?
|
||||
Plan:
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_item_status (user_id=? AND group_id=? AND group_scope_tag>?)
|
||||
|
||||
Query: SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0
|
||||
Plan:
|
||||
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id=? AND msg_content_tag=? AND item_deleted=? AND item_sent=?)
|
||||
|
||||
@@ -134,7 +134,8 @@ CREATE TABLE groups(
|
||||
business_xcontact_id BLOB NULL,
|
||||
customer_member_id BLOB NULL,
|
||||
chat_item_ttl INTEGER,
|
||||
local_alias TEXT DEFAULT '', -- received
|
||||
local_alias TEXT DEFAULT '',
|
||||
members_require_attention INTEGER NOT NULL DEFAULT 0, -- received
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
|
||||
@@ -579,18 +579,18 @@ safeDeleteLDN db User {userId} localDisplayName = do
|
||||
|
||||
type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe MemberId)
|
||||
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Text, Maybe Text, Maybe ImageData, Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData, Maybe Int64) :. GroupMemberRow
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Text, Maybe Text, Maybe ImageData, Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData, Maybe Int64, Int) :. GroupMemberRow
|
||||
|
||||
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId, ProfileId, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, Maybe Preferences) :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64)
|
||||
|
||||
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
|
||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, localAlias, description, image, enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. businessRow :. (uiThemes, customData, chatItemTTL) :. userMemberRow) =
|
||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, localAlias, description, image, enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. businessRow :. (uiThemes, customData, chatItemTTL, membersRequireAttention) :. userMemberRow) =
|
||||
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
fullGroupPreferences = mergeGroupPreferences groupPreferences
|
||||
groupProfile = GroupProfile {displayName, fullName, description, image, groupPreferences, memberAdmission}
|
||||
businessChat = toBusinessChatInfo businessRow
|
||||
in GroupInfo {groupId, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, chatTags, chatItemTTL, uiThemes, customData}
|
||||
in GroupInfo {groupId, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, chatTags, chatItemTTL, uiThemes, customData, membersRequireAttention}
|
||||
|
||||
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
|
||||
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId, profileId, displayName, fullName, image, contactLink, localAlias, preferences) :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions)) =
|
||||
@@ -622,7 +622,8 @@ groupInfoQuery =
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
|
||||
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.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data, g.chat_item_ttl,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id,
|
||||
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
|
||||
-- GroupMember - membership
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
|
||||
@@ -421,7 +421,8 @@ data GroupInfo = GroupInfo
|
||||
chatTags :: [ChatTagId],
|
||||
chatItemTTL :: Maybe Int64,
|
||||
uiThemes :: Maybe UIThemeEntityOverrides,
|
||||
customData :: Maybe CustomData
|
||||
customData :: Maybe CustomData,
|
||||
membersRequireAttention :: Int
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
@@ -854,6 +855,13 @@ data GroupSupportChat = GroupSupportChat
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
gmRequiresAttention :: GroupMember -> Bool
|
||||
gmRequiresAttention m@GroupMember {supportChat} =
|
||||
memberPending m || maybe False supportChatAttention supportChat
|
||||
where
|
||||
supportChatAttention GroupSupportChat {memberAttention, mentions} =
|
||||
memberAttention > 0 || mentions > 0
|
||||
|
||||
data GroupMemberRef = GroupMemberRef {groupMemberId :: Int64, profile :: Profile}
|
||||
deriving (Eq, Show)
|
||||
|
||||
|
||||
@@ -192,6 +192,7 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
CRSentConfirmation u _ -> ttyUser u ["confirmation sent!"]
|
||||
CRSentInvitation u _ customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
|
||||
CRSentInvitationToContact u _c customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
|
||||
CRItemsReadForChat u chatId -> ttyUser u ["items read for chat"]
|
||||
CRContactDeleted u c -> ttyUser u [ttyContact' c <> ": contact is deleted"]
|
||||
CRChatCleared u chatInfo -> ttyUser u $ viewChatCleared chatInfo
|
||||
CRAcceptingContactRequest u c -> ttyUser u $ viewAcceptingContactRequest c
|
||||
|
||||
@@ -372,7 +372,7 @@ testMarkReadDirect = testChat2 aliceProfile bobProfile $ \alice bob -> do
|
||||
bob ##> "/last_item_id"
|
||||
i :: ChatItemId <- read <$> getTermLine bob
|
||||
let itemIds = intercalate "," $ map show [i - 3 .. i]
|
||||
bob #$> ("/_read chat items @2 " <> itemIds, id, "ok")
|
||||
bob #$> ("/_read chat items @2 " <> itemIds, id, "items read for chat")
|
||||
|
||||
testChatPaginationInitial :: HasCallStack => TestParams -> IO ()
|
||||
testChatPaginationInitial = testChatOpts2 opts aliceProfile bobProfile $ \alice bob -> do
|
||||
@@ -391,7 +391,7 @@ testChatPaginationInitial = testChatOpts2 opts aliceProfile bobProfile $ \alice
|
||||
|
||||
-- Read next 2 items
|
||||
let itemIds = intercalate "," $ map itemId [1 .. 2]
|
||||
bob #$> ("/_read chat items @2 " <> itemIds, id, "ok")
|
||||
bob #$> ("/_read chat items @2 " <> itemIds, id, "items read for chat")
|
||||
bob #$> ("/_get chat @2 initial=2", chat, [(0, "1"), (0, "2"), (0, "3"), (0, "4"), (0, "5")])
|
||||
|
||||
-- Read all items
|
||||
|
||||
@@ -371,7 +371,7 @@ testMarkReadGroup = testChat2 aliceProfile bobProfile $ \alice bob -> do
|
||||
bob ##> "/last_item_id"
|
||||
i :: ChatItemId <- read <$> getTermLine bob
|
||||
let itemIds = intercalate "," $ map show [i - 3 .. i]
|
||||
bob #$> ("/_read chat items #1 " <> itemIds, id, "ok")
|
||||
bob #$> ("/_read chat items #1 " <> itemIds, id, "items read for chat")
|
||||
|
||||
testChatPaginationInitial :: HasCallStack => TestParams -> IO ()
|
||||
testChatPaginationInitial = testChatOpts2 opts aliceProfile bobProfile $ \alice bob -> do
|
||||
@@ -392,7 +392,7 @@ testChatPaginationInitial = testChatOpts2 opts aliceProfile bobProfile $ \alice
|
||||
|
||||
-- Read next 2 items
|
||||
let itemIds = intercalate "," $ map groupItemId [1 .. 2]
|
||||
bob #$> ("/_read chat items #1 " <> itemIds, id, "ok")
|
||||
bob #$> ("/_read chat items #1 " <> itemIds, id, "items read for chat")
|
||||
bob #$> ("/_get chat #1 initial=2", chat, [(0, "1"), (0, "2"), (0, "3"), (0, "4"), (0, "5")])
|
||||
|
||||
-- Read all items
|
||||
@@ -7072,12 +7072,12 @@ testScopedSupportUnreadStats =
|
||||
bob ##> "/member support chats #team"
|
||||
bob <## "support: unread: 3, require attention: 0, mentions: 0"
|
||||
|
||||
alice #$> ("/_read chat items #1(_support:2) " <> aliceMentionedByDanItemId, id, "ok")
|
||||
alice #$> ("/_read chat items #1(_support:2) " <> aliceMentionedByDanItemId, id, "items read for chat")
|
||||
|
||||
alice ##> "/member support chats #team"
|
||||
alice <## "bob (Bob) (id 2): unread: 4, require attention: 2, mentions: 1"
|
||||
|
||||
alice #$> ("/_read chat items #1(_support:2) " <> aliceMentionedByBobItemId, id, "ok")
|
||||
alice #$> ("/_read chat items #1(_support:2) " <> aliceMentionedByBobItemId, id, "items read for chat")
|
||||
|
||||
alice ##> "/member support chats #team"
|
||||
alice <## "bob (Bob) (id 2): unread: 3, require attention: 1, mentions: 0"
|
||||
|
||||
Reference in New Issue
Block a user