mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-06 01:18:11 +00:00
core, tests, ui: keep member order with role index, skip down-migration schema comparison, reload support list on return with older remote hosts
This commit is contained in:
@@ -424,7 +424,6 @@ final class ChatModel: ObservableObject {
|
||||
@Published var groupMembers: [GMember] = []
|
||||
@Published var groupMembersIndexes: Dictionary<Int64, Int> = [:] // groupMemberId to index in groupMembers list
|
||||
@Published var membersLoaded = false
|
||||
var membersLoadedGroupId: Int64?
|
||||
// Runtime-only relay hostnames for pre-join channel display, not persisted — lost on app restart.
|
||||
// APIConnectPreparedGroup re-fetches fresh relays at connect time, so stale data doesn't affect join.
|
||||
@Published var channelRelayHostnames: [Int64: [String]] = [:]
|
||||
@@ -578,12 +577,9 @@ final class ChatModel: ObservableObject {
|
||||
let groupMembers = await apiListMembers(groupInfo.groupId)
|
||||
await MainActor.run {
|
||||
if chatId == groupInfo.id {
|
||||
if let groupMembers {
|
||||
self.groupMembers = groupMembers.map { GMember.init($0) }
|
||||
self.populateGroupMembersIndexes()
|
||||
self.membersLoaded = true
|
||||
self.membersLoadedGroupId = groupInfo.groupId
|
||||
}
|
||||
self.groupMembers = groupMembers.map { GMember.init($0) }
|
||||
self.populateGroupMembersIndexes()
|
||||
self.membersLoaded = true
|
||||
updateView()
|
||||
}
|
||||
}
|
||||
@@ -1331,15 +1327,11 @@ final class ChatModel: ObservableObject {
|
||||
}
|
||||
|
||||
func upsertSupportChatMember(_ cInfo: ChatInfo) {
|
||||
if case let .group(groupInfo, .memberSupport(member?)?) = cInfo, chatId == groupInfo.id {
|
||||
var m = member
|
||||
var supportChatAdded = false
|
||||
if let current = getGroupMember(member.groupMemberId)?.wrapped {
|
||||
supportChatAdded = current.supportChat == nil && member.supportChat != nil
|
||||
m = current
|
||||
m.supportChat = member.supportChat
|
||||
m.memberProfile = member.memberProfile
|
||||
}
|
||||
if case let .group(groupInfo, .memberSupport(member?)?) = cInfo {
|
||||
var m = getGroupMember(member.groupMemberId)?.wrapped ?? member
|
||||
let supportChatAdded = m.supportChat == nil && member.supportChat != nil
|
||||
m.supportChat = member.supportChat
|
||||
m.memberProfile = member.memberProfile
|
||||
_ = upsertGroupMember(groupInfo, m)
|
||||
if supportChatAdded {
|
||||
objectWillChange.send()
|
||||
|
||||
@@ -592,10 +592,8 @@ private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async
|
||||
} else {
|
||||
r = await chatApiSendCmd(cmd, bgDelay: msgDelay)
|
||||
if case let .result(.newChatItems(_, aChatItems)) = r {
|
||||
await MainActor.run {
|
||||
for aChatItem in aChatItems {
|
||||
chatModel.upsertSupportChatMember(aChatItem.chatInfo)
|
||||
}
|
||||
if let last = aChatItems.last {
|
||||
await MainActor.run { chatModel.upsertSupportChatMember(last.chatInfo) }
|
||||
}
|
||||
return aChatItems.map { $0.chatItem }
|
||||
}
|
||||
@@ -2062,10 +2060,10 @@ func apiLeaveGroup(_ groupId: Int64) async throws -> GroupInfo {
|
||||
}
|
||||
|
||||
// use ChatModel's loadGroupMembers from views
|
||||
func apiListMembers(_ groupId: Int64) async -> [GroupMember]? {
|
||||
func apiListMembers(_ groupId: Int64) async -> [GroupMember] {
|
||||
let r: APIResult<ChatResponse2> = await chatApiSendCmd(.apiListMembers(groupId: groupId))
|
||||
if case let .result(.groupMembers(_, group)) = r { return group.members }
|
||||
return nil
|
||||
return []
|
||||
}
|
||||
|
||||
func filterMembersToAdd(_ ms: [GMember]) -> [Contact] {
|
||||
|
||||
@@ -359,9 +359,6 @@ struct ChatView: View {
|
||||
revealedItems = Set()
|
||||
stopAudioPlayer()
|
||||
if let cId {
|
||||
chatModel.groupMembers = []
|
||||
chatModel.groupMembersIndexes.removeAll()
|
||||
chatModel.membersLoaded = false
|
||||
if let c = chatModel.getChat(cId) {
|
||||
chat = c
|
||||
}
|
||||
@@ -3083,7 +3080,6 @@ func archiveReports(_ chat: Chat, _ itemIds: [Int64], _ forAll: Bool, _ onSucces
|
||||
}
|
||||
if let updatedChatInfo = deleted.last?.deletedChatItem.chatInfo {
|
||||
ChatModel.shared.updateChatInfo(updatedChatInfo)
|
||||
ChatModel.shared.upsertSupportChatMember(updatedChatInfo)
|
||||
}
|
||||
}
|
||||
await onSuccess()
|
||||
|
||||
@@ -82,7 +82,7 @@ func acceptMember(_ groupInfo: GroupInfo, _ member: GroupMember, _ role: GroupMe
|
||||
do {
|
||||
let (gInfo, acceptedMember) = try await apiAcceptMember(groupInfo.groupId, member.groupMemberId, role)
|
||||
await MainActor.run {
|
||||
_ = ChatModel.shared.upsertGroupMember(gInfo, acceptedMember)
|
||||
_ = ChatModel.shared.upsertGroupMember(gInfo, ChatModel.shared.withLoadedSupportChat(acceptedMember))
|
||||
ChatModel.shared.updateGroup(gInfo)
|
||||
dismiss?()
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ struct GroupMemberInfoView: View {
|
||||
do {
|
||||
let (_, stats) = try await apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
|
||||
let getCode = (member.memberActive || (groupInfo.useRelays && member.memberCurrent)) && member.memberRole != .relay
|
||||
let (mem, code) = getCode ? try await apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
|
||||
let (mem, code) = getCode ? try await apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (chatModel.withLoadedSupportChat(member), nil)
|
||||
await MainActor.run {
|
||||
_ = chatModel.upsertGroupMember(groupInfo, mem)
|
||||
connectionStats = stats
|
||||
@@ -578,7 +578,7 @@ struct GroupMemberInfoView: View {
|
||||
connectionCode: code,
|
||||
connectionVerified: member.verified,
|
||||
verify: { code in
|
||||
var member = groupMember.wrapped
|
||||
var member = chatModel.withLoadedSupportChat(groupMember.wrapped)
|
||||
if let r = apiVerifyGroupMember(member.groupId, member.groupMemberId, connectionCode: code) {
|
||||
let (verified, existingCode) = r
|
||||
let connCode = verified ? SecurityCode(securityCode: existingCode, verifiedAt: .now) : nil
|
||||
@@ -773,7 +773,7 @@ struct GroupMemberInfoView: View {
|
||||
let stats = try apiSwitchGroupMember(groupInfo.apiId, groupMember.groupMemberId)
|
||||
connectionStats = stats
|
||||
await MainActor.run {
|
||||
chatModel.updateGroupMemberConnectionStats(groupInfo, groupMember.wrapped, stats)
|
||||
chatModel.updateGroupMemberConnectionStats(groupInfo, chatModel.withLoadedSupportChat(groupMember.wrapped), stats)
|
||||
dismiss()
|
||||
}
|
||||
} catch let error {
|
||||
@@ -791,7 +791,7 @@ struct GroupMemberInfoView: View {
|
||||
let stats = try apiAbortSwitchGroupMember(groupInfo.apiId, groupMember.groupMemberId)
|
||||
connectionStats = stats
|
||||
await MainActor.run {
|
||||
chatModel.updateGroupMemberConnectionStats(groupInfo, groupMember.wrapped, stats)
|
||||
chatModel.updateGroupMemberConnectionStats(groupInfo, chatModel.withLoadedSupportChat(groupMember.wrapped), stats)
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("abortSwitchMemberAddress apiAbortSwitchGroupMember error: \(responseError(error))")
|
||||
@@ -869,7 +869,7 @@ func updateMemberSettings(_ gInfo: GroupInfo, _ member: GroupMember, _ memberSet
|
||||
do {
|
||||
try await apiSetMemberSettings(gInfo.groupId, member.groupMemberId, memberSettings)
|
||||
await MainActor.run {
|
||||
var mem = member
|
||||
var mem = ChatModel.shared.withLoadedSupportChat(member)
|
||||
mem.memberSettings = memberSettings
|
||||
_ = ChatModel.shared.upsertGroupMember(gInfo, mem)
|
||||
}
|
||||
|
||||
@@ -13,25 +13,17 @@ struct MemberSupportView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@State private var searchText: String = ""
|
||||
@State private var membersLoading = false
|
||||
@FocusState private var searchFocussed
|
||||
var groupInfo: GroupInfo
|
||||
@Binding var scrollToItemId: ChatItem.ID?
|
||||
|
||||
var body: some View {
|
||||
viewBody()
|
||||
.onAppear { loadMembersIfNeeded() }
|
||||
.onChange(of: chatModel.membersLoaded) { _ in loadMembersIfNeeded() }
|
||||
}
|
||||
|
||||
private func loadMembersIfNeeded() {
|
||||
if (!chatModel.membersLoaded || chatModel.membersLoadedGroupId != groupInfo.groupId) && chatModel.chatId == groupInfo.id && !membersLoading {
|
||||
membersLoading = true
|
||||
Task {
|
||||
await chatModel.loadGroupMembers(groupInfo)
|
||||
await MainActor.run { membersLoading = false }
|
||||
.onAppear {
|
||||
if !chatModel.membersLoaded {
|
||||
Task { await chatModel.loadGroupMembers(groupInfo) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func viewBody() -> some View {
|
||||
|
||||
Reference in New Issue
Block a user