mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-30 14:15:55 +00:00
ui: navigation from support chat to member info (#6237)
This commit is contained in:
@@ -60,6 +60,7 @@ struct ChatView: View {
|
||||
@State private var animatedScrollingInProgress: Bool = false
|
||||
@State private var showUserSupportChatSheet = false
|
||||
@State private var showCommandsMenu = false
|
||||
@State private var supportChatMemberInfoLinkActive = false
|
||||
|
||||
@State private var scrollView: EndlessScrollView<MergedItem> = EndlessScrollView(frame: .zero)
|
||||
|
||||
@@ -178,6 +179,28 @@ struct ChatView: View {
|
||||
if im.showLoadingProgress == chat.id {
|
||||
ProgressView().scaleEffect(2)
|
||||
}
|
||||
if case let .group(groupInfo, _) = chat.chatInfo,
|
||||
case let .groupChatScopeContext(groupScopeInfo) = im.secondaryIMFilter,
|
||||
case let .memberSupport(groupMember_) = groupScopeInfo,
|
||||
let groupMember = groupMember_ {
|
||||
NavigationLink(isActive: $supportChatMemberInfoLinkActive) {
|
||||
GroupMemberInfoView(
|
||||
groupInfo: groupInfo,
|
||||
chat: chat,
|
||||
groupMember: GMember(groupMember),
|
||||
scrollToItemId: $scrollToItemId,
|
||||
openedFromSupportChat: true
|
||||
)
|
||||
.navigationBarHidden(false)
|
||||
.modifier(BackButton(disabled: Binding.constant(false)) {
|
||||
supportChatMemberInfoLinkActive = false
|
||||
})
|
||||
} label: {
|
||||
EmptyView()
|
||||
}
|
||||
.frame(width: 1, height: 1)
|
||||
.hidden()
|
||||
}
|
||||
}
|
||||
.safeAreaInset(edge: .top) {
|
||||
VStack(spacing: .zero) {
|
||||
@@ -222,7 +245,9 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.appSheet(item: $selectedMember) { member in
|
||||
.appSheet(item: $selectedMember, onDismiss: {
|
||||
chatModel.secondaryIM = nil
|
||||
}) { member in
|
||||
if case let .group(groupInfo, _) = chat.chatInfo {
|
||||
GroupMemberInfoView(
|
||||
groupInfo: groupInfo,
|
||||
@@ -459,7 +484,10 @@ struct ChatView: View {
|
||||
ChatInfoToolbar(chat: chat)
|
||||
.tint(theme.colors.primary)
|
||||
}
|
||||
.appSheet(isPresented: $showChatInfoSheet, onDismiss: { theme = buildTheme() }) {
|
||||
.appSheet(isPresented: $showChatInfoSheet, onDismiss: {
|
||||
chatModel.secondaryIM = nil
|
||||
theme = buildTheme()
|
||||
}) {
|
||||
GroupChatInfoView(
|
||||
chat: chat,
|
||||
groupInfo: Binding(
|
||||
@@ -562,7 +590,11 @@ struct ChatView: View {
|
||||
switch groupScopeInfo {
|
||||
case let .memberSupport(groupMember_):
|
||||
if let groupMember = groupMember_ {
|
||||
MemberSupportChatToolbar(groupMember: groupMember)
|
||||
Button {
|
||||
supportChatMemberInfoLinkActive = true
|
||||
} label: {
|
||||
MemberSupportChatToolbar(groupMember: groupMember)
|
||||
}
|
||||
} else {
|
||||
textChatToolbar("Chat with admins")
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ struct GroupMemberInfoView: View {
|
||||
@ObservedObject var groupMember: GMember
|
||||
@Binding var scrollToItemId: ChatItem.ID?
|
||||
var navigation: Bool = false
|
||||
var openedFromSupportChat: Bool = false
|
||||
@State private var connectionStats: ConnectionStats? = nil
|
||||
@State private var connectionCode: String? = nil
|
||||
@State private var connectionLoaded: Bool = false
|
||||
@@ -101,7 +102,8 @@ struct GroupMemberInfoView: View {
|
||||
|
||||
if member.memberActive {
|
||||
Section {
|
||||
if groupInfo.membership.memberRole >= .moderator
|
||||
if !openedFromSupportChat
|
||||
&& groupInfo.membership.memberRole >= .moderator
|
||||
&& (member.memberRole < .moderator || member.supportChat != nil) {
|
||||
MemberInfoSupportChatNavLink(groupInfo: groupInfo, member: groupMember, scrollToItemId: $scrollToItemId)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct SecondaryChatView: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@ObservedObject var chat: Chat
|
||||
@Binding var scrollToItemId: ChatItem.ID?
|
||||
@@ -23,9 +24,10 @@ struct SecondaryChatView: View {
|
||||
floatingButtonModel: FloatingButtonModel(im: im),
|
||||
scrollToItemId: $scrollToItemId
|
||||
)
|
||||
.onDisappear {
|
||||
.modifier(BackButton(disabled: Binding.constant(false)) {
|
||||
chatModel.secondaryIM = nil
|
||||
}
|
||||
dismiss()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,8 +178,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.4.4.2-IWC91cESq2l3JFgl07ryw1-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1.a */; };
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN.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 */; };
|
||||
@@ -545,8 +545,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.4.4.2-IWC91cESq2l3JFgl07ryw1-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1.a"; sourceTree = "<group>"; };
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN.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>"; };
|
||||
@@ -708,8 +708,8 @@
|
||||
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
|
||||
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
|
||||
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN.a in Frameworks */,
|
||||
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -795,8 +795,8 @@
|
||||
64C829992D54AEEE006B9E89 /* libffi.a */,
|
||||
64C829982D54AEED006B9E89 /* libgmp.a */,
|
||||
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-IWC91cESq2l3JFgl07ryw1.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.4.2-7qDqJsgFG1qLUMejyoXxtN.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@@ -2634,7 +2634,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
|
||||
}
|
||||
|
||||
public func canChangeRoleTo(groupInfo: GroupInfo) -> [GroupMemberRole]? {
|
||||
if !canBeRemoved(groupInfo: groupInfo) { return nil }
|
||||
if !canBeRemoved(groupInfo: groupInfo) || memberPending { return nil }
|
||||
let userRole = groupInfo.membership.memberRole
|
||||
return GroupMemberRole.supportedRoles.filter { $0 <= userRole }
|
||||
}
|
||||
@@ -2643,6 +2643,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
|
||||
let userRole = groupInfo.membership.memberRole
|
||||
return memberStatus != .memRemoved && memberStatus != .memLeft && memberRole < .moderator
|
||||
&& userRole >= .moderator && userRole >= memberRole && groupInfo.membership.memberActive
|
||||
&& !memberPending
|
||||
}
|
||||
|
||||
public var canReceiveReports: Bool {
|
||||
|
||||
@@ -2343,7 +2343,7 @@ data class GroupMember (
|
||||
}
|
||||
|
||||
fun canChangeRoleTo(groupInfo: GroupInfo): List<GroupMemberRole>? =
|
||||
if (!canBeRemoved(groupInfo)) null
|
||||
if (!canBeRemoved(groupInfo) || memberPending) null
|
||||
else groupInfo.membership.memberRole.let { userRole ->
|
||||
GroupMemberRole.selectableRoles.filter { it <= userRole }
|
||||
}
|
||||
@@ -2352,6 +2352,7 @@ data class GroupMember (
|
||||
val userRole = groupInfo.membership.memberRole
|
||||
return memberStatus != GroupMemberStatus.MemRemoved && memberStatus != GroupMemberStatus.MemLeft && memberRole < GroupMemberRole.Moderator
|
||||
&& userRole >= GroupMemberRole.Moderator && userRole >= memberRole && groupInfo.membership.memberActive
|
||||
&& !memberPending
|
||||
}
|
||||
|
||||
val versionRange: VersionRange = activeConn?.peerChatVRange ?: memberChatVRange
|
||||
|
||||
@@ -453,7 +453,7 @@ fun ChatView(
|
||||
}
|
||||
ModalManager.end.showModalCloseable(true) { close ->
|
||||
remember { derivedStateOf { chatModel.getGroupMember(member.groupMemberId) } }.value?.let { mem ->
|
||||
GroupMemberInfoView(chatRh, groupInfo, mem, scrollToItemId, stats, code, chatModel, close, close)
|
||||
GroupMemberInfoView(chatRh, groupInfo, mem, scrollToItemId, stats, code, chatModel, openedFromSupportChat = false, close, close)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1005,7 +1005,9 @@ fun ChatLayout(
|
||||
Column(if (oneHandUI.value && chatBottomBar.value) Modifier.align(Alignment.BottomStart).imePadding() else Modifier) {
|
||||
Box {
|
||||
if (selectedChatItems.value == null) {
|
||||
MemberSupportChatAppBar(chatsCtx, chatsCtx.secondaryContextFilter.groupScopeInfo.groupMember_, { ModalManager.end.closeModal() }, onSearchValueChanged)
|
||||
if (chat != null) {
|
||||
MemberSupportChatAppBar(chatsCtx, remoteHostId, chat, chatsCtx.secondaryContextFilter.groupScopeInfo.groupMember_, scrollToItemId, { ModalManager.end.closeModal() }, onSearchValueChanged)
|
||||
}
|
||||
} else {
|
||||
SelectedItemsCounterToolbar(selectedChatItems, !oneHandUI.value)
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ fun ModalData.GroupChatInfoView(
|
||||
}
|
||||
ModalManager.end.showModalCloseable(true) { closeCurrent ->
|
||||
remember { derivedStateOf { chatModel.getGroupMember(member.groupMemberId) } }.value?.let { mem ->
|
||||
GroupMemberInfoView(rhId, groupInfo, mem, scrollToItemId, stats, code, chatModel, closeCurrent) {
|
||||
GroupMemberInfoView(rhId, groupInfo, mem, scrollToItemId, stats, code, chatModel, openedFromSupportChat = false, closeCurrent) {
|
||||
closeCurrent()
|
||||
close()
|
||||
}
|
||||
|
||||
@@ -49,9 +49,13 @@ fun GroupMemberInfoView(
|
||||
connectionStats: ConnectionStats?,
|
||||
connectionCode: String?,
|
||||
chatModel: ChatModel,
|
||||
openedFromSupportChat: Boolean,
|
||||
close: () -> Unit,
|
||||
closeAll: () -> Unit, // Close all open windows up to ChatView
|
||||
) {
|
||||
KeyChangeEffect(chat.simplex.common.platform.chatModel.chatId.value) {
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
BackHandler(onBack = close)
|
||||
val chat = chatModel.chats.value.firstOrNull { ch -> ch.id == chatModel.chatId.value && ch.remoteHostId == rhId }
|
||||
val connStats = remember { mutableStateOf(connectionStats) }
|
||||
@@ -225,7 +229,8 @@ fun GroupMemberInfoView(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
openedFromSupportChat = openedFromSupportChat
|
||||
)
|
||||
|
||||
if (progressIndicator) {
|
||||
@@ -291,6 +296,7 @@ fun GroupMemberInfoLayout(
|
||||
syncMemberConnection: () -> Unit,
|
||||
syncMemberConnectionForce: () -> Unit,
|
||||
verifyClicked: () -> Unit,
|
||||
openedFromSupportChat: Boolean
|
||||
) {
|
||||
val cStats = connStats.value
|
||||
fun knownDirectChat(contactId: Long): Pair<Chat, Contact>? {
|
||||
@@ -440,6 +446,7 @@ fun GroupMemberInfoLayout(
|
||||
if (member.memberActive) {
|
||||
SectionView {
|
||||
if (
|
||||
!openedFromSupportChat &&
|
||||
groupInfo.membership.memberRole >= GroupMemberRole.Moderator &&
|
||||
(member.memberRole < GroupMemberRole.Moderator || member.supportChat != null)
|
||||
) {
|
||||
@@ -924,6 +931,7 @@ fun PreviewGroupMemberInfoLayout() {
|
||||
syncMemberConnection = {},
|
||||
syncMemberConnectionForce = {},
|
||||
verifyClicked = {},
|
||||
openedFromSupportChat = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,10 @@ private fun MemberSupportChatView(
|
||||
@Composable
|
||||
fun MemberSupportChatAppBar(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
rhId: Long?,
|
||||
chat: Chat,
|
||||
scopeMember_: GroupMember?,
|
||||
scrollToItemId: MutableState<Long?>,
|
||||
close: () -> Unit,
|
||||
onSearchValueChanged: (String) -> Unit
|
||||
) {
|
||||
@@ -67,11 +70,31 @@ fun MemberSupportChatAppBar(
|
||||
}
|
||||
}
|
||||
BackHandler(onBack = onBackClicked)
|
||||
if (scopeMember_ != null) {
|
||||
if (chat.chatInfo is ChatInfo.Group && scopeMember_ != null) {
|
||||
val groupInfo = chat.chatInfo.groupInfo
|
||||
DefaultAppBar(
|
||||
navigationButton = { NavigationButtonBack(onBackClicked) },
|
||||
title = { MemberSupportChatToolbarTitle(scopeMember_) },
|
||||
onTitleClick = null,
|
||||
onTitleClick = {
|
||||
withBGApi {
|
||||
val r = chatModel.controller.apiGroupMemberInfo(rhId, groupInfo.groupId, scopeMember_.groupMemberId)
|
||||
val stats = r?.second
|
||||
val code = if (scopeMember_.memberActive) {
|
||||
val memCode = chatModel.controller.apiGetGroupMemberCode(rhId, groupInfo.apiId, scopeMember_.groupMemberId)
|
||||
memCode?.second
|
||||
} else {
|
||||
null
|
||||
}
|
||||
ModalManager.end.showModalCloseable(true) { closeCurrent ->
|
||||
remember { derivedStateOf { chatModel.getGroupMember(scopeMember_.groupMemberId) } }.value?.let { mem ->
|
||||
GroupMemberInfoView(rhId, groupInfo, mem, scrollToItemId, stats, code, chatModel, openedFromSupportChat = true, close = closeCurrent) {
|
||||
closeCurrent()
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onTop = !oneHandUI.value || !chatBottomBar.value,
|
||||
showSearch = showSearch.value,
|
||||
onSearchValueChanged = onSearchValueChanged,
|
||||
|
||||
Reference in New Issue
Block a user