2022-07-14 16:40:32 +04:00
|
|
|
//
|
|
|
|
|
// GroupChatInfoView.swift
|
|
|
|
|
// SimpleX (iOS)
|
|
|
|
|
//
|
|
|
|
|
// Created by JRoberts on 14.07.2022.
|
|
|
|
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import SwiftUI
|
|
|
|
|
import SimpleXChat
|
|
|
|
|
|
|
|
|
|
struct GroupChatInfoView: View {
|
|
|
|
|
@EnvironmentObject var chatModel: ChatModel
|
2022-07-30 13:03:44 +01:00
|
|
|
@Environment(\.dismiss) var dismiss: DismissAction
|
2022-07-14 16:40:32 +04:00
|
|
|
@ObservedObject var chat: Chat
|
2022-07-26 12:33:10 +04:00
|
|
|
var groupInfo: GroupInfo
|
2022-07-30 13:03:44 +01:00
|
|
|
@ObservedObject private var alertManager = AlertManager.shared
|
2022-07-26 12:33:10 +04:00
|
|
|
@State private var members: [GroupMember] = []
|
|
|
|
|
@State private var alert: GroupChatInfoViewAlert? = nil
|
|
|
|
|
@State private var showAddMembersSheet: Bool = false
|
2022-07-27 11:16:07 +04:00
|
|
|
@State private var selectedMember: GroupMember? = nil
|
2022-07-30 13:03:44 +01:00
|
|
|
@State private var showGroupProfile: Bool = false
|
2022-07-14 16:40:32 +04:00
|
|
|
|
2022-07-26 12:33:10 +04:00
|
|
|
enum GroupChatInfoViewAlert: Identifiable {
|
|
|
|
|
case deleteGroupAlert
|
2022-07-14 16:40:32 +04:00
|
|
|
case clearChatAlert
|
2022-07-26 12:33:10 +04:00
|
|
|
case leaveGroupAlert
|
2022-07-14 16:40:32 +04:00
|
|
|
|
2022-07-26 12:33:10 +04:00
|
|
|
var id: GroupChatInfoViewAlert { get { self } }
|
2022-07-14 16:40:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var body: some View {
|
2022-07-26 12:33:10 +04:00
|
|
|
NavigationView {
|
|
|
|
|
List {
|
|
|
|
|
groupInfoHeader()
|
|
|
|
|
.listRowBackground(Color.clear)
|
|
|
|
|
|
2022-07-30 13:03:44 +01:00
|
|
|
Section("\(members.count + 1) members") {
|
2022-07-30 16:49:34 +04:00
|
|
|
if groupInfo.canAddMembers {
|
2022-07-27 11:16:07 +04:00
|
|
|
addMembersButton()
|
|
|
|
|
}
|
|
|
|
|
memberView(groupInfo.membership, user: true)
|
2022-07-26 12:33:10 +04:00
|
|
|
ForEach(members) { member in
|
2022-07-27 11:16:07 +04:00
|
|
|
Button { selectedMember = member } label: { memberView(member) }
|
2022-07-26 12:33:10 +04:00
|
|
|
}
|
|
|
|
|
}
|
2022-07-27 11:16:07 +04:00
|
|
|
.sheet(isPresented: $showAddMembersSheet) {
|
2022-07-30 13:03:44 +01:00
|
|
|
AddGroupMembersView(chat: chat, groupInfo: groupInfo, membersToAdd: filterMembersToAdd(members))
|
2022-07-27 11:16:07 +04:00
|
|
|
}
|
|
|
|
|
.sheet(item: $selectedMember) { member in
|
|
|
|
|
GroupMemberInfoView(groupInfo: groupInfo, member: member)
|
|
|
|
|
}
|
2022-07-30 18:46:10 +01:00
|
|
|
.sheet(isPresented: $showGroupProfile) {
|
|
|
|
|
GroupProfileView(groupId: groupInfo.apiId, groupProfile: groupInfo.groupProfile)
|
|
|
|
|
}
|
2022-07-26 12:33:10 +04:00
|
|
|
|
|
|
|
|
Section {
|
2022-07-30 18:46:10 +01:00
|
|
|
if groupInfo.canEdit {
|
|
|
|
|
editGroupButton()
|
|
|
|
|
}
|
2022-07-26 12:33:10 +04:00
|
|
|
clearChatButton()
|
|
|
|
|
if groupInfo.canDelete {
|
|
|
|
|
deleteGroupButton()
|
|
|
|
|
}
|
2022-07-30 16:49:34 +04:00
|
|
|
if groupInfo.membership.memberCurrent {
|
2022-07-27 11:16:07 +04:00
|
|
|
leaveGroupButton()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Section(header: Text("For console")) {
|
|
|
|
|
infoRow("Local name", chat.chatInfo.localDisplayName)
|
|
|
|
|
infoRow("Database ID", "\(chat.chatInfo.apiId)")
|
2022-07-26 12:33:10 +04:00
|
|
|
}
|
2022-07-14 16:40:32 +04:00
|
|
|
}
|
2022-07-26 12:33:10 +04:00
|
|
|
.navigationBarHidden(true)
|
|
|
|
|
}
|
|
|
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
2022-07-14 16:40:32 +04:00
|
|
|
.alert(item: $alert) { alertItem in
|
|
|
|
|
switch(alertItem) {
|
2022-07-26 12:33:10 +04:00
|
|
|
case .deleteGroupAlert: return deleteGroupAlert()
|
2022-07-14 16:40:32 +04:00
|
|
|
case .clearChatAlert: return clearChatAlert()
|
2022-07-26 12:33:10 +04:00
|
|
|
case .leaveGroupAlert: return leaveGroupAlert()
|
2022-07-14 16:40:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
2022-07-26 12:33:10 +04:00
|
|
|
.task {
|
2022-07-30 13:03:44 +01:00
|
|
|
let ms = await apiListMembers(chat.chatInfo.apiId)
|
|
|
|
|
await MainActor.run {
|
|
|
|
|
members = ms.sorted { $0.displayName.lowercased() < $1.displayName.lowercased() }
|
|
|
|
|
}
|
2022-07-26 12:33:10 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupInfoHeader() -> some View {
|
|
|
|
|
VStack {
|
2022-07-27 11:16:07 +04:00
|
|
|
let cInfo = chat.chatInfo
|
2022-07-26 12:33:10 +04:00
|
|
|
ChatInfoImage(chat: chat, color: Color(uiColor: .tertiarySystemFill))
|
|
|
|
|
.frame(width: 192, height: 192)
|
|
|
|
|
.padding(.top, 12)
|
|
|
|
|
.padding()
|
2022-07-27 11:16:07 +04:00
|
|
|
Text(cInfo.displayName)
|
2022-07-26 12:33:10 +04:00
|
|
|
.font(.largeTitle)
|
|
|
|
|
.lineLimit(1)
|
|
|
|
|
.padding(.bottom, 2)
|
2022-07-27 11:16:07 +04:00
|
|
|
if cInfo.fullName != "" && cInfo.fullName != cInfo.displayName {
|
|
|
|
|
Text(cInfo.fullName)
|
|
|
|
|
.font(.title2)
|
|
|
|
|
.lineLimit(2)
|
|
|
|
|
}
|
2022-07-26 12:33:10 +04:00
|
|
|
}
|
|
|
|
|
.frame(maxWidth: .infinity, alignment: .center)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func addMembersButton() -> some View {
|
|
|
|
|
Button {
|
|
|
|
|
showAddMembersSheet = true
|
|
|
|
|
} label: {
|
|
|
|
|
Label("Invite members", systemImage: "plus")
|
|
|
|
|
}
|
2022-07-14 16:40:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func serverImage() -> some View {
|
|
|
|
|
let status = chat.serverInfo.networkStatus
|
|
|
|
|
return Image(systemName: status.imageName)
|
|
|
|
|
.foregroundColor(status == .connected ? .green : .secondary)
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-27 11:16:07 +04:00
|
|
|
func memberView(_ member: GroupMember, user: Bool = false) -> some View {
|
|
|
|
|
HStack{
|
|
|
|
|
ProfileImage(imageStr: member.image)
|
|
|
|
|
.frame(width: 38, height: 38)
|
|
|
|
|
.padding(.trailing, 2)
|
|
|
|
|
// TODO server connection status
|
|
|
|
|
VStack(alignment: .leading) {
|
|
|
|
|
Text(member.chatViewName)
|
|
|
|
|
.lineLimit(1)
|
|
|
|
|
.foregroundColor(.primary)
|
|
|
|
|
let s = Text(member.memberStatus.shortText)
|
|
|
|
|
(user ? Text ("you: ") + s : s)
|
|
|
|
|
.lineLimit(1)
|
|
|
|
|
.font(.caption)
|
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
|
}
|
|
|
|
|
Spacer()
|
|
|
|
|
let role = member.memberRole
|
|
|
|
|
if role == .owner || role == .admin {
|
|
|
|
|
Text(member.memberRole.text)
|
|
|
|
|
.foregroundColor(.secondary)
|
2022-07-26 12:33:10 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-30 18:46:10 +01:00
|
|
|
func editGroupButton() -> some View {
|
|
|
|
|
Button {
|
|
|
|
|
showGroupProfile = true
|
|
|
|
|
} label: {
|
|
|
|
|
Label("Edit group profile", systemImage: "pencil")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-26 12:33:10 +04:00
|
|
|
func deleteGroupButton() -> some View {
|
|
|
|
|
Button(role: .destructive) {
|
|
|
|
|
alert = .deleteGroupAlert
|
|
|
|
|
} label: {
|
|
|
|
|
Label("Delete group", systemImage: "trash")
|
|
|
|
|
.foregroundColor(Color.red)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func clearChatButton() -> some View {
|
|
|
|
|
Button() {
|
|
|
|
|
alert = .clearChatAlert
|
|
|
|
|
} label: {
|
|
|
|
|
Label("Clear conversation", systemImage: "gobackward")
|
|
|
|
|
.foregroundColor(Color.orange)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func leaveGroupButton() -> some View {
|
|
|
|
|
Button(role: .destructive) {
|
|
|
|
|
alert = .leaveGroupAlert
|
|
|
|
|
} label: {
|
|
|
|
|
Label("Leave group", systemImage: "rectangle.portrait.and.arrow.right")
|
|
|
|
|
.foregroundColor(Color.red)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO reuse this and clearChatAlert with ChatInfoView
|
|
|
|
|
private func deleteGroupAlert() -> Alert {
|
2022-07-14 16:40:32 +04:00
|
|
|
Alert(
|
2022-07-26 12:33:10 +04:00
|
|
|
title: Text("Delete group?"),
|
|
|
|
|
message: Text("Group will be deleted for all members - this cannot be undone!"),
|
2022-07-14 16:40:32 +04:00
|
|
|
primaryButton: .destructive(Text("Delete")) {
|
|
|
|
|
Task {
|
|
|
|
|
do {
|
2022-07-26 12:33:10 +04:00
|
|
|
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId)
|
|
|
|
|
await MainActor.run {
|
|
|
|
|
chatModel.removeChat(chat.chatInfo.id)
|
2022-07-30 13:03:44 +01:00
|
|
|
dismiss()
|
2022-07-14 16:40:32 +04:00
|
|
|
}
|
|
|
|
|
} catch let error {
|
2022-07-26 12:33:10 +04:00
|
|
|
logger.error("deleteGroupAlert apiDeleteChat error: \(error.localizedDescription)")
|
2022-07-14 16:40:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
secondaryButton: .cancel()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func clearChatAlert() -> Alert {
|
|
|
|
|
Alert(
|
|
|
|
|
title: Text("Clear conversation?"),
|
|
|
|
|
message: Text("All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you."),
|
|
|
|
|
primaryButton: .destructive(Text("Clear")) {
|
|
|
|
|
Task {
|
|
|
|
|
await clearChat(chat)
|
2022-07-30 13:03:44 +01:00
|
|
|
await MainActor.run { dismiss() }
|
2022-07-26 12:33:10 +04:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
secondaryButton: .cancel()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func leaveGroupAlert() -> Alert {
|
|
|
|
|
Alert(
|
|
|
|
|
title: Text("Leave group?"),
|
|
|
|
|
message: Text("You will stop receiving messages from this group. Chat history will be preserved."),
|
|
|
|
|
primaryButton: .destructive(Text("Leave")) {
|
|
|
|
|
Task {
|
|
|
|
|
await leaveGroup(chat.chatInfo.apiId)
|
2022-07-30 13:03:44 +01:00
|
|
|
await MainActor.run { dismiss() }
|
2022-07-14 16:40:32 +04:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
secondaryButton: .cancel()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct GroupChatInfoView_Previews: PreviewProvider {
|
|
|
|
|
static var previews: some View {
|
2022-07-30 13:03:44 +01:00
|
|
|
GroupChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.group, chatItems: []), groupInfo: GroupInfo.sampleData)
|
2022-07-14 16:40:32 +04:00
|
|
|
}
|
|
|
|
|
}
|