2022-07-14 16:40:32 +04:00
//
// G r o u p C h a t I n f o V i e w . s w i f t
// S i m p l e X ( i O S )
//
// C r e a t e d b y J R o b e r t s o n 1 4 . 0 7 . 2 0 2 2 .
// C o p y r i g h t © 2 0 2 2 S i m p l e X C h a t . A l l r i g h t s r e s e r v e d .
//
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-11-16 20:26:43 +04:00
@ State 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 alert : GroupChatInfoViewAlert ? = nil
2022-10-15 18:09:25 +04:00
@ State private var groupLink : String ?
2022-07-26 12:33:10 +04:00
@ 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-08-02 14:48:31 +04:00
@ State private var connectionStats : ConnectionStats ?
2022-08-02 17:00:12 +04:00
@ AppStorage ( DEFAULT_DEVELOPER_TOOLS ) private var developerTools = 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-08-29 14:47:29 +04:00
case cantInviteIncognitoAlert
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 {
2022-08-09 13:43:19 +04:00
let members = chatModel . groupMembers
. filter { $0 . memberStatus != . memLeft && $0 . memberStatus != . memRemoved }
. sorted { $0 . displayName . lowercased ( ) < $1 . displayName . lowercased ( ) }
2022-07-26 12:33:10 +04:00
List {
groupInfoHeader ( )
. listRowBackground ( Color . clear )
2022-11-16 20:26:43 +04:00
Section {
NavigationLink {
GroupPreferencesView (
groupInfo : $ groupInfo ,
preferences : groupInfo . fullGroupPreferences ,
currentPreferences : groupInfo . fullGroupPreferences
)
. navigationBarTitle ( " Group preferences " )
. navigationBarTitleDisplayMode ( . large )
} label : {
2022-11-17 06:57:27 +00:00
settingsRow ( " switch.2 " ) {
Text ( " Group preferences " )
}
2022-11-16 20:26:43 +04:00
}
} header : {
Text ( " Preferences " )
} footer : {
Text ( " Only group owners can change group preferences. " )
}
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-10-15 18:09:25 +04:00
groupLinkButton ( )
2022-08-29 14:47:29 +04:00
if ( chat . chatInfo . incognito ) {
Label ( " Invite members " , systemImage : " plus " )
. foregroundColor ( Color ( uiColor : . tertiaryLabel ) )
. onTapGesture { alert = . cantInviteIncognitoAlert }
} else {
addMembersButton ( )
}
2022-07-27 11:16:07 +04:00
}
memberView ( groupInfo . membership , user : true )
2022-07-26 12:33:10 +04:00
ForEach ( members ) { member in
2022-08-02 14:48:31 +04:00
Button {
Task {
do {
2022-08-29 14:47:29 +04:00
let stats = try await apiGroupMemberInfo ( groupInfo . apiId , member . groupMemberId )
await MainActor . run { connectionStats = stats }
2022-08-02 14:48:31 +04:00
} catch let error {
logger . error ( " apiGroupMemberInfo error: \( responseError ( error ) ) " )
}
await MainActor . run { 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-08-09 13:43:19 +04:00
AddGroupMembersView ( chat : chat , groupInfo : groupInfo )
2022-07-27 11:16:07 +04:00
}
2022-11-07 20:44:04 +04:00
. sheet ( item : $ selectedMember , onDismiss : {
selectedMember = nil
connectionStats = nil
} ) { _ in
GroupMemberInfoView ( groupInfo : groupInfo , member : $ selectedMember , connectionStats : $ connectionStats )
2022-07-27 11:16:07 +04:00
}
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 ( )
}
}
2022-08-02 17:00:12 +04:00
if developerTools {
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-08-29 14:47:29 +04:00
case . cantInviteIncognitoAlert : return cantInviteIncognitoAlert ( )
2022-10-15 18:09:25 +04:00
}
}
. onAppear {
do {
groupLink = try apiGetGroupLink ( groupInfo . groupId )
} catch let error {
logger . error ( " GroupChatInfoView apiGetGroupLink: \( responseError ( error ) ) " )
2022-07-14 16:40:32 +04:00
}
}
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 {
2022-08-09 13:43:19 +04:00
Task {
let groupMembers = await apiListMembers ( groupInfo . groupId )
await MainActor . run {
ChatModel . shared . groupMembers = groupMembers
showAddMembersSheet = true
}
}
2022-07-26 12:33:10 +04:00
} 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 )
// T O D O s e r v e r c o n n e c t i o n s t a t u s
VStack ( alignment : . leading ) {
Text ( member . chatViewName )
. lineLimit ( 1 )
2022-08-23 18:18:12 +04:00
. foregroundColor ( member . memberIncognito ? . indigo : . primary )
2022-07-27 11:16:07 +04:00
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-10-15 18:09:25 +04:00
private func groupLinkButton ( ) -> some View {
NavigationLink {
GroupLinkView ( groupId : groupInfo . groupId , groupLink : $ groupLink )
. navigationBarTitleDisplayMode ( . inline )
} label : {
Label ( " Group link " , systemImage : " link " )
. foregroundColor ( . accentColor )
}
}
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 )
}
}
// T O D O r e u s e t h i s a n d c l e a r C h a t A l e r t w i t h C h a t I n f o V i e w
private func deleteGroupAlert ( ) -> Alert {
2022-09-14 21:45:59 +04:00
return Alert (
2022-07-26 12:33:10 +04:00
title : Text ( " Delete group? " ) ,
2022-09-14 21:45:59 +04:00
message : deleteGroupAlertMessage ( ) ,
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-08-29 14:08:46 +01:00
chatModel . chatId = nil
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 ( )
)
}
2022-09-14 21:45:59 +04:00
private func deleteGroupAlertMessage ( ) -> Text {
groupInfo . membership . memberCurrent ? Text ( " Group will be deleted for all members - this cannot be undone! " ) : Text ( " Group will be deleted for you - this cannot be undone! " )
}
2022-07-14 16:40:32 +04:00
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 ( )
)
}
}
2022-08-29 14:47:29 +04:00
func cantInviteIncognitoAlert ( ) -> Alert {
Alert (
title : Text ( " Can't invite contacts! " ) ,
message : Text ( " You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed " )
)
}
2022-07-14 16:40:32 +04:00
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
}
}