mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-23 19:23:36 +00:00
Merge branch 'master' into master-android
This commit is contained in:
25 files changed
+209
-116
No files matched your search
@@ -1646,9 +1646,9 @@ func apiAcceptMember(_ groupId: Int64, _ groupMemberId: Int64, _ memberRole: Gro
|
||||
throw r.unexpected
|
||||
}
|
||||
|
||||
func apiRemoveMembers(_ groupId: Int64, _ memberIds: [Int64], _ withMessages: Bool = false) async throws -> [GroupMember] {
|
||||
func apiRemoveMembers(_ groupId: Int64, _ memberIds: [Int64], _ withMessages: Bool = false) async throws -> (GroupInfo, [GroupMember]) {
|
||||
let r: ChatResponse2 = try await chatSendCmd(.apiRemoveMembers(groupId: groupId, memberIds: memberIds, withMessages: withMessages), bgTask: false)
|
||||
if case let .userDeletedMembers(_, _, members, withMessages) = r { return members }
|
||||
if case let .userDeletedMembers(_, updatedGroupInfo, members, _withMessages) = r { return (updatedGroupInfo, members) }
|
||||
throw r.unexpected
|
||||
}
|
||||
|
||||
@@ -2267,6 +2267,7 @@ func processReceivedMsg(_ res: ChatEvent) async {
|
||||
case let .deletedMember(user, groupInfo, byMember, deletedMember, withMessages):
|
||||
if active(user) {
|
||||
await MainActor.run {
|
||||
m.updateGroup(groupInfo)
|
||||
_ = m.upsertGroupMember(groupInfo, deletedMember)
|
||||
if withMessages {
|
||||
m.removeMemberItems(deletedMember, byMember: byMember, groupInfo)
|
||||
@@ -2276,6 +2277,7 @@ func processReceivedMsg(_ res: ChatEvent) async {
|
||||
case let .leftMember(user, groupInfo, member):
|
||||
if active(user) {
|
||||
await MainActor.run {
|
||||
m.updateGroup(groupInfo)
|
||||
_ = m.upsertGroupMember(groupInfo, member)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ struct GroupChatInfoView: View {
|
||||
if groupInfo.canDelete {
|
||||
deleteGroupButton()
|
||||
}
|
||||
if groupInfo.membership.memberCurrent {
|
||||
if groupInfo.membership.memberCurrentOrPending {
|
||||
leaveGroupButton()
|
||||
}
|
||||
}
|
||||
@@ -797,10 +797,11 @@ struct GroupChatInfoView: View {
|
||||
func removeMember(_ groupInfo: GroupInfo, _ mem: GroupMember, dismiss: DismissAction? = nil) {
|
||||
Task {
|
||||
do {
|
||||
let updatedMembers = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId])
|
||||
let (updatedGroupInfo, updatedMembers) = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId])
|
||||
await MainActor.run {
|
||||
ChatModel.shared.updateGroup(updatedGroupInfo)
|
||||
updatedMembers.forEach { updatedMember in
|
||||
_ = ChatModel.shared.upsertGroupMember(groupInfo, updatedMember)
|
||||
_ = ChatModel.shared.upsertGroupMember(updatedGroupInfo, updatedMember)
|
||||
}
|
||||
dismiss?()
|
||||
}
|
||||
|
||||
@@ -640,10 +640,11 @@ struct GroupMemberInfoView: View {
|
||||
primaryButton: .destructive(Text("Remove")) {
|
||||
Task {
|
||||
do {
|
||||
let updatedMembers = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId])
|
||||
let (updatedGroupInfo, updatedMembers) = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId])
|
||||
await MainActor.run {
|
||||
chatModel.updateGroup(updatedGroupInfo)
|
||||
updatedMembers.forEach { updatedMember in
|
||||
_ = chatModel.upsertGroupMember(groupInfo, updatedMember)
|
||||
_ = chatModel.upsertGroupMember(updatedGroupInfo, updatedMember)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ struct ChatListNavLink: View {
|
||||
}
|
||||
.swipeActions(edge: .trailing) {
|
||||
tagChatButton(chat)
|
||||
if (groupInfo.membership.memberCurrent) {
|
||||
if (groupInfo.membership.memberCurrentOrPending) {
|
||||
leaveGroupChatButton(groupInfo)
|
||||
}
|
||||
if groupInfo.canDelete {
|
||||
@@ -214,7 +214,7 @@ struct ChatListNavLink: View {
|
||||
let showReportsButton = chat.chatStats.reportsCount > 0 && groupInfo.membership.memberRole >= .moderator
|
||||
let showClearButton = !chat.chatItems.isEmpty
|
||||
let showDeleteGroup = groupInfo.canDelete
|
||||
let showLeaveGroup = groupInfo.membership.memberCurrent
|
||||
let showLeaveGroup = groupInfo.membership.memberCurrentOrPending
|
||||
let totalNumberOfButtons = 1 + (showReportsButton ? 1 : 0) + (showClearButton ? 1 : 0) + (showDeleteGroup ? 1 : 0) + (showLeaveGroup ? 1 : 0)
|
||||
|
||||
if showClearButton && totalNumberOfButtons <= 3 {
|
||||
|
||||
@@ -184,8 +184,8 @@
|
||||
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; };
|
||||
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; };
|
||||
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; };
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO.a */; };
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh.a */; };
|
||||
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; };
|
||||
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
|
||||
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; };
|
||||
@@ -553,8 +553,8 @@
|
||||
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = "<group>"; };
|
||||
64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO.a"; sourceTree = "<group>"; };
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh.a"; sourceTree = "<group>"; };
|
||||
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = "<group>"; };
|
||||
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = "<group>"; };
|
||||
@@ -712,8 +712,8 @@
|
||||
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
|
||||
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
|
||||
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh.a in Frameworks */,
|
||||
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -798,8 +798,8 @@
|
||||
64C829992D54AEEE006B9E89 /* libffi.a */,
|
||||
64C829982D54AEED006B9E89 /* libgmp.a */,
|
||||
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.4.1-Cm6JGiMgJjo4088oWn41JO.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.0-Adp18CY8iMhDelg0G0VSjh.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@@ -2043,7 +2043,7 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable {
|
||||
}
|
||||
|
||||
public var canDelete: Bool {
|
||||
return membership.memberRole == .owner || !membership.memberCurrent
|
||||
return membership.memberRole == .owner || !membership.memberCurrentOrPending
|
||||
}
|
||||
|
||||
public var canAddMembers: Bool {
|
||||
@@ -2275,6 +2275,10 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
public var memberCurrentOrPending: Bool {
|
||||
memberCurrent || memberPending
|
||||
}
|
||||
|
||||
public func canBeRemoved(groupInfo: GroupInfo) -> Bool {
|
||||
let userRole = groupInfo.membership.memberRole
|
||||
return memberStatus != .memRemoved && memberStatus != .memLeft
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ fun UserPickerUserBox(
|
||||
ProfileImageForActiveCall(size = USER_PICKER_IMAGE_SIZE, image = userInfo.user.profile.image, color = MaterialTheme.colors.secondaryVariant)
|
||||
|
||||
if (userInfo.unreadCount > 0 && !userInfo.user.activeUser) {
|
||||
unreadBadge(userInfo.unreadCount, userInfo.user.showNtfs, false)
|
||||
userUnreadBadge(userInfo.unreadCount, userInfo.user.showNtfs, false)
|
||||
}
|
||||
}
|
||||
val user = userInfo.user
|
||||
|
||||
+10
-1
@@ -323,6 +323,12 @@ object ChatModel {
|
||||
is SecondaryContextFilter.MsgContentTagContext -> null
|
||||
}
|
||||
|
||||
val isUserSupportChat: Boolean =
|
||||
when (groupScopeInfo) {
|
||||
null -> false
|
||||
is GroupChatScopeInfo.MemberSupport -> groupScopeInfo.groupMember_ == null
|
||||
}
|
||||
|
||||
suspend fun addChat(chat: Chat) {
|
||||
chats.add(index = 0, chat)
|
||||
popChatCollector.throttlePopChat(chat.remoteHostId, chat.id, currentPosition = 0)
|
||||
@@ -1877,7 +1883,7 @@ data class GroupInfo (
|
||||
get() = membership.memberRole == GroupMemberRole.Owner && membership.memberCurrent
|
||||
|
||||
val canDelete: Boolean
|
||||
get() = membership.memberRole == GroupMemberRole.Owner || !membership.memberCurrent
|
||||
get() = membership.memberRole == GroupMemberRole.Owner || !membership.memberCurrentOrPending
|
||||
|
||||
val canAddMembers: Boolean
|
||||
get() = membership.memberRole >= GroupMemberRole.Admin && membership.memberActive
|
||||
@@ -2084,6 +2090,9 @@ data class GroupMember (
|
||||
else -> false
|
||||
}
|
||||
|
||||
val memberCurrentOrPending: Boolean get() =
|
||||
memberCurrent || memberPending
|
||||
|
||||
fun canBeRemoved(groupInfo: GroupInfo): Boolean {
|
||||
val userRole = groupInfo.membership.memberRole
|
||||
return memberStatus != GroupMemberStatus.MemRemoved && memberStatus != GroupMemberStatus.MemLeft
|
||||
|
||||
+4
-2
@@ -1911,9 +1911,9 @@ object ChatController {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiRemoveMembers(rh: Long?, groupId: Long, memberIds: List<Long>, withMessages: Boolean = false): List<GroupMember>? {
|
||||
suspend fun apiRemoveMembers(rh: Long?, groupId: Long, memberIds: List<Long>, withMessages: Boolean = false): Pair<GroupInfo, List<GroupMember>>? {
|
||||
val r = sendCmd(rh, CC.ApiRemoveMembers(groupId, memberIds, withMessages))
|
||||
if (r is API.Result && r.res is CR.UserDeletedMembers) return r.res.members
|
||||
if (r is API.Result && r.res is CR.UserDeletedMembers) return r.res.groupInfo to r.res.members
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiRemoveMembers", generalGetString(MR.strings.error_removing_member), r)
|
||||
}
|
||||
@@ -2603,6 +2603,7 @@ object ChatController {
|
||||
is CR.DeletedMember ->
|
||||
if (active(r.user)) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.deletedMember)
|
||||
if (r.withMessages) {
|
||||
chatModel.chatsContext.removeMemberItems(rhId, r.deletedMember, byMember = r.byMember, r.groupInfo)
|
||||
@@ -2618,6 +2619,7 @@ object ChatController {
|
||||
is CR.LeftMember ->
|
||||
if (active(r.user)) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
+5
-4
@@ -898,7 +898,6 @@ fun ChatLayout(
|
||||
if (oneHandUI.value && chatBottomBar.value) {
|
||||
if (
|
||||
chatInfo is ChatInfo.Group
|
||||
&& chatInfo.groupInfo.canModerate
|
||||
&& chatsCtx.secondaryContextFilter == null
|
||||
&& (reportsCount > 0 || supportUnreadCount > 0)
|
||||
) {
|
||||
@@ -960,8 +959,6 @@ fun ChatLayout(
|
||||
}
|
||||
if (
|
||||
chatInfo is ChatInfo.Group
|
||||
&& chatInfo.groupInfo.canModerate
|
||||
&& chatsCtx.contentTag == null
|
||||
&& (reportsCount > 0 || supportUnreadCount > 0)
|
||||
&& (!oneHandUI.value || !chatBottomBar.value)
|
||||
) {
|
||||
@@ -1212,7 +1209,11 @@ private fun SupportChatsCountToolbar(
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
if (reportsCount > 0) {
|
||||
if (
|
||||
chatInfo is ChatInfo.Group
|
||||
&& chatInfo.groupInfo.canModerate
|
||||
&& reportsCount > 0
|
||||
) {
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
+38
-14
@@ -331,13 +331,14 @@ fun AddGroupMembersButton(
|
||||
|
||||
@Composable
|
||||
fun UserSupportChatButton(
|
||||
chat: Chat,
|
||||
groupInfo: GroupInfo,
|
||||
scrollToItemId: MutableState<Long?>
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_flag),
|
||||
SettingsActionItemWithContent(
|
||||
painterResource(if (chat.supportUnreadCount > 0) MR.images.ic_flag_filled else MR.images.ic_flag),
|
||||
stringResource(MR.strings.button_support_chat),
|
||||
click = {
|
||||
val scopeInfo = GroupChatScopeInfo.MemberSupport(groupMember_ = null)
|
||||
@@ -351,8 +352,15 @@ fun UserSupportChatButton(
|
||||
)
|
||||
}
|
||||
},
|
||||
iconColor = MaterialTheme.colors.secondary,
|
||||
)
|
||||
iconColor = (if (chat.supportUnreadCount > 0) MaterialTheme.colors.primary else MaterialTheme.colors.secondary),
|
||||
) {
|
||||
if (chat.supportUnreadCount > 0) {
|
||||
UnreadBadge(
|
||||
text = unreadCountStr(chat.supportUnreadCount),
|
||||
backgroundColor = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -488,7 +496,7 @@ fun ModalData.GroupChatInfoLayout(
|
||||
(groupInfo.membership.memberRole < GroupMemberRole.Moderator || groupInfo.membership.supportChat != null)
|
||||
) {
|
||||
anyTopSectionRowShow = true
|
||||
UserSupportChatButton(groupInfo, scrollToItemId)
|
||||
UserSupportChatButton(chat, groupInfo, scrollToItemId)
|
||||
}
|
||||
}
|
||||
if (anyTopSectionRowShow) {
|
||||
@@ -588,7 +596,7 @@ fun ModalData.GroupChatInfoLayout(
|
||||
val titleId = if (groupInfo.businessChat == null) MR.strings.button_delete_group else MR.strings.button_delete_chat
|
||||
DeleteGroupButton(titleId, deleteGroup)
|
||||
}
|
||||
if (groupInfo.membership.memberCurrent) {
|
||||
if (groupInfo.membership.memberCurrentOrPending) {
|
||||
val titleId = if (groupInfo.businessChat == null) MR.strings.button_leave_group else MR.strings.button_leave_chat
|
||||
LeaveGroupButton(titleId, leaveGroup)
|
||||
}
|
||||
@@ -743,12 +751,19 @@ private fun GroupChatInfoHeader(cInfo: ChatInfo, groupInfo: GroupInfo) {
|
||||
|
||||
@Composable
|
||||
private fun MemberSupportButton(chat: Chat, onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
SettingsActionItemWithContent(
|
||||
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.supportUnreadCount > 0) MaterialTheme.colors.primary else MaterialTheme.colors.secondary)
|
||||
)
|
||||
) {
|
||||
if (chat.supportUnreadCount > 0) {
|
||||
UnreadBadge(
|
||||
text = unreadCountStr(chat.supportUnreadCount),
|
||||
backgroundColor = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -762,12 +777,19 @@ private fun GroupPreferencesButton(titleId: StringResource, onClick: () -> Unit)
|
||||
|
||||
@Composable
|
||||
private fun GroupReportsButton(chat: Chat, onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
SettingsActionItemWithContent(
|
||||
painterResource(if (chat.chatStats.reportsCount > 0) MR.images.ic_flag_filled else MR.images.ic_flag),
|
||||
stringResource(MR.strings.group_reports_member_reports),
|
||||
click = onClick,
|
||||
iconColor = (if (chat.chatStats.reportsCount > 0) Color.Red else MaterialTheme.colors.secondary)
|
||||
)
|
||||
) {
|
||||
if (chat.chatStats.reportsCount > 0) {
|
||||
UnreadBadge(
|
||||
text = unreadCountStr(chat.chatStats.reportsCount),
|
||||
backgroundColor = Color.Red
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1033,16 +1055,18 @@ private fun setGroupAlias(chat: Chat, localAlias: String, chatModel: ChatModel)
|
||||
|
||||
fun removeMembers(rhId: Long?, groupInfo: GroupInfo, memberIds: List<Long>, onSuccess: () -> Unit = {}) {
|
||||
withBGApi {
|
||||
val updatedMembers = chatModel.controller.apiRemoveMembers(rhId, groupInfo.groupId, memberIds)
|
||||
if (updatedMembers != null) {
|
||||
val r = chatModel.controller.apiRemoveMembers(rhId, groupInfo.groupId, memberIds)
|
||||
if (r != null) {
|
||||
val (updatedGroupInfo, updatedMembers) = r
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, updatedGroupInfo)
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, updatedGroupInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, updatedGroupInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
|
||||
+6
-4
@@ -246,16 +246,18 @@ fun removeMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, c
|
||||
confirmText = generalGetString(MR.strings.remove_member_confirmation),
|
||||
onConfirm = {
|
||||
withBGApi {
|
||||
val removedMembers = chatModel.controller.apiRemoveMembers(rhId, member.groupId, listOf(member.groupMemberId))
|
||||
if (removedMembers != null) {
|
||||
val r = chatModel.controller.apiRemoveMembers(rhId, member.groupId, listOf(member.groupMemberId))
|
||||
if (r != null) {
|
||||
val (updatedGroupInfo, removedMembers) = r
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, updatedGroupInfo)
|
||||
removedMembers.forEach { removedMember ->
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, updatedGroupInfo, removedMember)
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
removedMembers.forEach { removedMember ->
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, updatedGroupInfo, removedMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-1
@@ -22,6 +22,7 @@ import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@Composable
|
||||
private fun MemberSupportChatView(
|
||||
chatInfo: ChatInfo,
|
||||
memberSupportChatsCtx: ChatModel.ChatsContext,
|
||||
staleChatId: State<String?>,
|
||||
scrollToItemId: MutableState<Long?>
|
||||
@@ -29,6 +30,21 @@ private fun MemberSupportChatView(
|
||||
KeyChangeEffect(chatModel.chatId.value) {
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
if (appPlatform.isAndroid) {
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
val chat = chatModel.chats.value.firstOrNull { ch -> ch.id == chatInfo.id }
|
||||
if (
|
||||
memberSupportChatsCtx.isUserSupportChat
|
||||
&& chat?.chatInfo?.groupInfo_?.membership?.memberPending == true
|
||||
) {
|
||||
withBGApi {
|
||||
chatModel.chatId.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ChatView(memberSupportChatsCtx, staleChatId, scrollToItemId, onComposed = {})
|
||||
}
|
||||
|
||||
@@ -118,7 +134,7 @@ suspend fun showMemberSupportChatView(staleChatId: State<String?>, scrollToItemI
|
||||
ModalManager.end.showCustomModal(true, id = ModalViewId.SECONDARY_CHAT) { close ->
|
||||
ModalView({}, showAppBar = false) {
|
||||
if (chatInfo is ChatInfo.Group && chatInfo.groupChatScope != null) {
|
||||
MemberSupportChatView(memberSupportChatsCtx, staleChatId, scrollToItemId)
|
||||
MemberSupportChatView(chatInfo, memberSupportChatsCtx, staleChatId, scrollToItemId)
|
||||
} else {
|
||||
LaunchedEffect(Unit) {
|
||||
close()
|
||||
|
||||
+9
-17
@@ -25,8 +25,7 @@ import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
import chat.simplex.common.views.chat.item.ItemAction
|
||||
import chat.simplex.common.views.chatlist.setGroupMembers
|
||||
import chat.simplex.common.views.chatlist.unreadCountStr
|
||||
import chat.simplex.common.views.chatlist.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -179,12 +178,12 @@ fun SupportChatRow(member: GroupMember) {
|
||||
Box(Modifier.widthIn(min = 34.sp.toDp()), contentAlignment = Alignment.TopEnd) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.sp.toDp())) {
|
||||
if (supportChat.unread > 0 || supportChat.mentions > 0 || supportChat.memberAttention > 0) {
|
||||
val indicatorTint = when {
|
||||
val unreadBadgeColor = when {
|
||||
supportChat.mentions > 0 || supportChat.memberAttention > 0 -> MaterialTheme.colors.primaryVariant
|
||||
else -> MaterialTheme.colors.secondary
|
||||
}
|
||||
if (supportChat.mentions == 1 && supportChat.unread == 1) {
|
||||
Box(modifier = Modifier.offset(y = 2.sp.toDp()).size(15.sp.toDp()).background(indicatorTint, shape = CircleShape), contentAlignment = Alignment.Center) {
|
||||
Box(modifier = Modifier.offset(y = 2.sp.toDp()).size(15.sp.toDp()).background(unreadBadgeColor, shape = CircleShape), contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_alternate_email),
|
||||
contentDescription = generalGetString(MR.strings.notifications),
|
||||
@@ -197,22 +196,15 @@ fun SupportChatRow(member: GroupMember) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_alternate_email),
|
||||
contentDescription = generalGetString(MR.strings.notifications),
|
||||
tint = indicatorTint,
|
||||
modifier = Modifier.size(12.sp.toDp()).offset(y = 3.sp.toDp())
|
||||
tint = unreadBadgeColor,
|
||||
modifier = Modifier.size(12.sp.toDp()).offset(y = 2.sp.toDp())
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
unreadCountStr(supportChat.unread),
|
||||
color = Color.White,
|
||||
fontSize = 10.sp,
|
||||
style = TextStyle(textAlign = TextAlign.Center),
|
||||
modifier = Modifier
|
||||
.offset(y = 3.sp.toDp())
|
||||
.background(indicatorTint, shape = CircleShape)
|
||||
.badgeLayout()
|
||||
.padding(horizontal = 2.sp.toDp())
|
||||
.padding(vertical = 1.sp.toDp())
|
||||
UnreadBadge(
|
||||
text = unreadCountStr(supportChat.unread),
|
||||
backgroundColor = unreadBadgeColor,
|
||||
yOffset = 2.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -304,7 +304,7 @@ fun GroupMenuItems(
|
||||
}
|
||||
}
|
||||
GroupMemberStatus.MemAccepted -> {
|
||||
if (groupInfo.membership.memberCurrent) {
|
||||
if (groupInfo.membership.memberCurrentOrPending) {
|
||||
LeaveGroupAction(chat.remoteHostId, groupInfo, chatModel, showMenu)
|
||||
}
|
||||
if (groupInfo.canDelete) {
|
||||
@@ -326,7 +326,7 @@ fun GroupMenuItems(
|
||||
}
|
||||
}
|
||||
ClearChatAction(chat, showMenu)
|
||||
if (groupInfo.membership.memberCurrent) {
|
||||
if (groupInfo.membership.memberCurrentOrPending) {
|
||||
LeaveGroupAction(chat.remoteHostId, groupInfo, chatModel, showMenu)
|
||||
}
|
||||
if (groupInfo.canDelete) {
|
||||
|
||||
+24
-11
@@ -471,17 +471,10 @@ fun ChatPreviewView(
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
if (n > 0) unreadCountStr(n) else "",
|
||||
color = Color.White,
|
||||
fontSize = 10.sp,
|
||||
style = TextStyle(textAlign = TextAlign.Center),
|
||||
modifier = Modifier
|
||||
.offset(y = 3.sp.toDp())
|
||||
.background(if (disabled || showNtfsIcon) MaterialTheme.colors.secondary else MaterialTheme.colors.primaryVariant, shape = CircleShape)
|
||||
.badgeLayout()
|
||||
.padding(horizontal = 2.sp.toDp())
|
||||
.padding(vertical = 1.sp.toDp())
|
||||
UnreadBadge(
|
||||
text = if (n > 0) unreadCountStr(n) else "",
|
||||
backgroundColor = if (disabled || showNtfsIcon) MaterialTheme.colors.secondary else MaterialTheme.colors.primaryVariant,
|
||||
yOffset = 3.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -573,6 +566,26 @@ private fun groupInvitationPreviewText(currentUserProfileDisplayName: String?, g
|
||||
stringResource(MR.strings.group_preview_you_are_invited)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UnreadBadge(
|
||||
text: String,
|
||||
backgroundColor: Color,
|
||||
yOffset: Dp? = null
|
||||
) {
|
||||
Text(
|
||||
text,
|
||||
color = Color.White,
|
||||
fontSize = 10.sp,
|
||||
style = TextStyle(textAlign = TextAlign.Center),
|
||||
modifier = Modifier
|
||||
.offset(y = yOffset ?: 0.dp)
|
||||
.background(backgroundColor, shape = CircleShape)
|
||||
.badgeLayout()
|
||||
.padding(horizontal = 2.sp.toDp())
|
||||
.padding(vertical = 1.sp.toDp())
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun unreadCountStr(n: Int): String {
|
||||
return if (n < 1000) "$n" else "${n / 1000}" + stringResource(MR.strings.thousand_abbreviation)
|
||||
|
||||
+1
-1
@@ -634,7 +634,7 @@ fun HostDisconnectButton(onClick: (() -> Unit)?) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BoxScope.unreadBadge(unreadCount: Int, userMuted: Boolean, hasPadding: Boolean) {
|
||||
fun BoxScope.userUnreadBadge(unreadCount: Int, userMuted: Boolean, hasPadding: Boolean) {
|
||||
Text(
|
||||
if (unreadCount > 0) unreadCountStr(unreadCount) else "",
|
||||
color = Color.White,
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ actual fun UserPickerUsersSection(
|
||||
ProfileImage(size = 55.dp, image = user.profile.image, color = iconColor)
|
||||
|
||||
if (u.unreadCount > 0 && !user.activeUser) {
|
||||
unreadBadge(u.unreadCount, user.showNtfs, true)
|
||||
userUnreadBadge(u.unreadCount, user.showNtfs, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: simplex-chat
|
||||
version: 6.4.0.0
|
||||
version: 6.4.0.1
|
||||
category: Web, System, Services, Cryptography
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
author: simplex.chat
|
||||
|
||||
@@ -2077,8 +2077,8 @@ processChatCommand' vr = \case
|
||||
void $ sendGroupMessage user gInfo scope ([m] <> rcpModMs') msg
|
||||
when (maxVersion (memberChatVRange m) < groupKnockingVersion) $
|
||||
forM_ (memberConn m) $ \mConn -> do
|
||||
let msg = XMsgNew $ MCSimple $ extMsgContent (MCText acceptedToGroupMessage) Nothing
|
||||
void $ sendDirectMemberMessage mConn msg groupId
|
||||
let msg2 = XMsgNew $ MCSimple $ extMsgContent (MCText acceptedToGroupMessage) Nothing
|
||||
void $ sendDirectMemberMessage mConn msg2 groupId
|
||||
(m', gInfo') <- withFastStore' $ \db -> do
|
||||
m' <- updateGroupMemberAccepted db user m newMemberStatus role
|
||||
gInfo' <- updateGroupMembersRequireAttention db user gInfo m m'
|
||||
@@ -2227,10 +2227,13 @@ processChatCommand' vr = \case
|
||||
let acis = acis2 <> acis3 <> acis4
|
||||
errs = errs1 <> errs2 <> errs3 <> errs4
|
||||
deleted = deleted1 <> deleted2 <> deleted3 <> deleted4
|
||||
unless (null acis) $ toView $ CEvtNewChatItems user acis
|
||||
-- Read group info with updated membersRequireAttention
|
||||
gInfo' <- withFastStore $ \db -> getGroupInfo db vr user groupId
|
||||
let acis' = map (updateCIGroupInfo gInfo') acis
|
||||
unless (null acis') $ toView $ CEvtNewChatItems user acis'
|
||||
unless (null errs) $ toView $ CEvtChatErrors errs
|
||||
when withMessages $ deleteMessages user gInfo deleted
|
||||
pure $ CRUserDeletedMembers user gInfo deleted withMessages -- same order is not guaranteed
|
||||
when withMessages $ deleteMessages user gInfo' deleted
|
||||
pure $ CRUserDeletedMembers user gInfo' deleted withMessages -- same order is not guaranteed
|
||||
where
|
||||
selectMembers :: [GroupMember] -> (Int, [GroupMember], [GroupMember], [GroupMember], [GroupMember], GroupMemberRole, Bool)
|
||||
selectMembers = foldl' addMember (0, [], [], [], [], GRObserver, False)
|
||||
@@ -2280,8 +2283,17 @@ processChatCommand' vr = \case
|
||||
ts = ciContentTexts content
|
||||
in NewSndChatItemData msg content ts M.empty Nothing Nothing Nothing
|
||||
delMember db m = do
|
||||
deleteOrUpdateMemberRecordIO db user m
|
||||
-- We're in a function used in batch member deletion, and since we're passing same gInfo for each member,
|
||||
-- voided result (updated group info) may have incorrect state of membersRequireAttention.
|
||||
-- To avoid complicating code by chaining group info updates,
|
||||
-- instead we re-read it once after deleting all members before response.
|
||||
void $ deleteOrUpdateMemberRecordIO db user gInfo m
|
||||
pure m {memberStatus = GSMemRemoved}
|
||||
updateCIGroupInfo :: GroupInfo -> AChatItem -> AChatItem
|
||||
updateCIGroupInfo gInfo' = \case
|
||||
AChatItem SCTGroup SMDSnd (GroupChat _gInfo chatScopeInfo) ci ->
|
||||
AChatItem SCTGroup SMDSnd (GroupChat gInfo' chatScopeInfo) ci
|
||||
aci -> aci
|
||||
deleteMessages user gInfo@GroupInfo {membership} ms
|
||||
| groupFeatureMemberAllowed SGFFullDelete membership gInfo = deleteGroupMembersCIs user gInfo ms membership
|
||||
| otherwise = markGroupMembersCIsDeleted user gInfo ms membership
|
||||
|
||||
@@ -1564,15 +1564,20 @@ deleteMemberConnection' GroupMember {activeConn} waitDelivery = do
|
||||
deleteAgentConnectionAsync' (aConnId conn) waitDelivery
|
||||
withStore' $ \db -> updateConnectionStatus db conn ConnDeleted
|
||||
|
||||
deleteOrUpdateMemberRecord :: User -> GroupMember -> CM ()
|
||||
deleteOrUpdateMemberRecord user member =
|
||||
withStore' $ \db -> deleteOrUpdateMemberRecordIO db user member
|
||||
deleteOrUpdateMemberRecord :: User -> GroupInfo -> GroupMember -> CM GroupInfo
|
||||
deleteOrUpdateMemberRecord user gInfo member =
|
||||
withStore' $ \db -> deleteOrUpdateMemberRecordIO db user gInfo member
|
||||
|
||||
deleteOrUpdateMemberRecordIO :: DB.Connection -> User -> GroupMember -> IO ()
|
||||
deleteOrUpdateMemberRecordIO db user@User {userId} member =
|
||||
deleteOrUpdateMemberRecordIO :: DB.Connection -> User -> GroupInfo -> GroupMember -> IO GroupInfo
|
||||
deleteOrUpdateMemberRecordIO db user@User {userId} gInfo member = do
|
||||
gInfo' <-
|
||||
if gmRequiresAttention member
|
||||
then decreaseGroupMembersRequireAttention db user gInfo
|
||||
else pure gInfo
|
||||
checkGroupMemberHasItems db user member >>= \case
|
||||
Just _ -> updateGroupMemberStatus db userId member GSMemRemoved
|
||||
Nothing -> deleteGroupMember db user member
|
||||
pure gInfo'
|
||||
|
||||
sendDirectContactMessages :: MsgEncodingI e => User -> Contact -> NonEmpty (ChatMsgEvent e) -> CM [Either ChatError SndMessage]
|
||||
sendDirectContactMessages user ct events = do
|
||||
|
||||
@@ -2697,10 +2697,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
-- ? prohibit deleting member if it's the sender - sender should use x.grp.leave
|
||||
deleteMemberConnection member
|
||||
-- undeleted "member connected" chat item will prevent deletion of member record
|
||||
deleteOrUpdateMemberRecord user member
|
||||
gInfo' <- deleteOrUpdateMemberRecord user gInfo member
|
||||
when withMessages $ deleteMessages member SMDRcv
|
||||
deleteMemberItem $ RGEMemberDeleted groupMemberId (fromLocalProfile memberProfile)
|
||||
toView $ CEvtDeletedMember user gInfo m member {memberStatus = GSMemRemoved} withMessages
|
||||
toView $ CEvtDeletedMember user gInfo' m member {memberStatus = GSMemRemoved} withMessages
|
||||
where
|
||||
checkRole GroupMember {memberRole} a
|
||||
| senderRole < GRAdmin || senderRole < memberRole =
|
||||
@@ -2719,11 +2719,15 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
xGrpLeave gInfo m msg brokerTs = do
|
||||
deleteMemberConnection m
|
||||
-- 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, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEMemberLeft)
|
||||
gInfo' <- withStore' $ \db -> do
|
||||
updateGroupMemberStatus db userId m GSMemLeft
|
||||
if gmRequiresAttention m
|
||||
then decreaseGroupMembersRequireAttention db user gInfo
|
||||
else pure gInfo
|
||||
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo' m
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEMemberLeft)
|
||||
groupMsgToView cInfo ci
|
||||
toView $ CEvtLeftMember user gInfo' m' {memberStatus = GSMemLeft}
|
||||
toView $ CEvtLeftMember user gInfo'' m' {memberStatus = GSMemLeft}
|
||||
|
||||
xGrpDel :: GroupInfo -> GroupMember -> RcvMessage -> UTCTime -> CM ()
|
||||
xGrpDel gInfo@GroupInfo {membership} m@GroupMember {memberRole} msg brokerTs = do
|
||||
|
||||
@@ -75,11 +75,11 @@ remoteFilesFolder = "simplex_v1_files"
|
||||
|
||||
-- when acting as host
|
||||
minRemoteCtrlVersion :: AppVersion
|
||||
minRemoteCtrlVersion = AppVersion [6, 4, 0, 0]
|
||||
minRemoteCtrlVersion = AppVersion [6, 4, 0, 1]
|
||||
|
||||
-- when acting as controller
|
||||
minRemoteHostVersion :: AppVersion
|
||||
minRemoteHostVersion = AppVersion [6, 4, 0, 0]
|
||||
minRemoteHostVersion = AppVersion [6, 4, 0, 1]
|
||||
|
||||
currentAppVersion :: AppVersion
|
||||
currentAppVersion = AppVersion SC.version
|
||||
|
||||
@@ -81,6 +81,7 @@ module Simplex.Chat.Store.Groups
|
||||
updateGroupMemberStatusById,
|
||||
updateGroupMemberAccepted,
|
||||
updateGroupMembersRequireAttention,
|
||||
decreaseGroupMembersRequireAttention,
|
||||
increaseGroupMembersRequireAttention,
|
||||
createNewGroupMember,
|
||||
checkGroupMemberHasItems,
|
||||
@@ -1231,24 +1232,28 @@ updateGroupMemberAccepted db User {userId} m@GroupMember {groupMemberId} status
|
||||
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'
|
||||
updateGroupMembersRequireAttention db user g 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}
|
||||
| not nowRequires && didRequire =
|
||||
decreaseGroupMembersRequireAttention db user g
|
||||
| otherwise = pure g
|
||||
where
|
||||
didRequire = gmRequiresAttention member
|
||||
nowRequires = gmRequiresAttention member'
|
||||
|
||||
decreaseGroupMembersRequireAttention :: DB.Connection -> User -> GroupInfo -> IO GroupInfo
|
||||
decreaseGroupMembersRequireAttention 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}
|
||||
|
||||
increaseGroupMembersRequireAttention :: DB.Connection -> User -> GroupInfo -> IO GroupInfo
|
||||
increaseGroupMembersRequireAttention db User {userId} g@GroupInfo {groupId, membersRequireAttention} = do
|
||||
DB.execute
|
||||
|
||||
@@ -1335,14 +1335,6 @@ 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 = ?
|
||||
@@ -4451,6 +4443,14 @@ Query:
|
||||
Plan:
|
||||
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 groups
|
||||
SET via_group_link_uri_hash = (SELECT via_contact_uri_hash FROM connections WHERE connection_id = ?)
|
||||
|
||||
Reference in new issue
Block a user