2022-07-26 12:33:10 +04:00
//
// G r o u p M e m b e r 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 2 5 . 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 GroupMemberInfoView : View {
@ EnvironmentObject var chatModel : ChatModel
2022-07-27 11:16:07 +04:00
@ Environment ( \ . dismiss ) var dismiss : DismissAction
var groupInfo : GroupInfo
2022-11-07 20:44:04 +04:00
@ Binding var member : GroupMember ?
2022-11-01 20:30:53 +00:00
@ Binding var connectionStats : ConnectionStats ?
2022-10-04 09:53:43 +01:00
@ State private var newRole : GroupMemberRole = . member
2022-07-27 11:16:07 +04:00
@ State private var alert : GroupMemberInfoViewAlert ?
2022-08-02 17:00:12 +04:00
@ AppStorage ( DEFAULT_DEVELOPER_TOOLS ) private var developerTools = false
2022-07-26 12:33:10 +04:00
enum GroupMemberInfoViewAlert : Identifiable {
2022-11-07 20:44:04 +04:00
case removeMemberAlert ( mem : GroupMember )
case changeMemberRoleAlert ( mem : GroupMember , role : GroupMemberRole )
2022-11-02 09:48:20 +00:00
case switchAddressAlert
2022-10-06 15:02:58 +01:00
case error ( title : LocalizedStringKey , error : LocalizedStringKey )
2022-07-26 12:33:10 +04:00
2022-10-04 09:53:43 +01:00
var id : String {
switch self {
case . removeMemberAlert : return " removeMemberAlert "
2022-11-07 20:44:04 +04:00
case let . changeMemberRoleAlert ( _ , role ) : return " changeMemberRoleAlert \( role . rawValue ) "
2022-11-02 09:48:20 +00:00
case . switchAddressAlert : return " switchAddressAlert "
2022-10-04 09:53:43 +01:00
case let . error ( title , _ ) : return " error \( title ) "
}
}
2022-07-26 12:33:10 +04:00
}
var body : some View {
NavigationView {
2022-11-07 20:44:04 +04:00
if let member = member {
List {
groupMemberInfoHeader ( member )
. listRowBackground ( Color . clear )
if let contactId = member . memberContactId {
Section {
openDirectChatButton ( contactId )
}
2022-08-29 14:08:46 +01:00
}
2022-08-26 17:27:38 +04:00
2022-11-07 20:44:04 +04:00
Section ( " Member " ) {
infoRow ( " Group " , groupInfo . displayName )
2022-10-04 09:53:43 +01:00
2022-10-22 18:40:21 +04:00
if let roles = member . canChangeRoleTo ( groupInfo : groupInfo ) {
Picker ( " Change role " , selection : $ newRole ) {
ForEach ( roles ) { role in
Text ( role . text )
}
}
} else {
2022-11-07 20:44:04 +04:00
infoRow ( " Role " , member . memberRole . text )
2022-10-22 18:40:21 +04:00
}
2022-11-07 20:44:04 +04:00
// T O D O i n v i t e d b y - n e e d t o g e t c o n t a c t b y c o n t a c t i d
if let conn = member . activeConn {
let connLevelDesc = conn . connLevel = = 0 ? NSLocalizedString ( " direct " , comment : " connection level description " ) : String . localizedStringWithFormat ( NSLocalizedString ( " indirect (%d) " , comment : " connection level description " ) , conn . connLevel )
infoRow ( " Connection " , connLevelDesc )
2022-10-22 18:40:21 +04:00
}
}
2022-10-04 09:53:43 +01:00
2022-11-07 20:44:04 +04:00
Section ( " Servers " ) {
// T O D O n e t w o r k c o n n e c t i o n s t a t u s
if developerTools {
Button ( " Change receiving address (BETA) " ) {
alert = . switchAddressAlert
}
}
if let connStats = connectionStats {
smpServers ( " Receiving via " , connStats . rcvServers )
smpServers ( " Sending via " , connStats . sndServers )
}
2022-07-27 11:16:07 +04:00
}
2022-11-07 20:44:04 +04:00
if member . canBeRemoved ( groupInfo : groupInfo ) {
Section {
removeMemberButton ( member )
2022-11-02 09:48:20 +00:00
}
2022-11-01 20:30:53 +00:00
}
2022-07-26 12:33:10 +04:00
2022-11-07 20:44:04 +04:00
if developerTools {
Section ( " For console " ) {
infoRow ( " Local name " , member . localDisplayName )
infoRow ( " Database ID " , " \( member . groupMemberId ) " )
}
2022-07-27 11:16:07 +04:00
}
}
2022-11-07 20:44:04 +04:00
. navigationBarHidden ( true )
. onAppear { newRole = member . memberRole }
. onChange ( of : newRole ) { _ in
if newRole != member . memberRole {
alert = . changeMemberRoleAlert ( mem : member , role : newRole )
2022-08-02 17:00:12 +04:00
}
2022-07-26 12:33:10 +04:00
}
}
}
. frame ( maxWidth : . infinity , maxHeight : . infinity , alignment : . top )
. alert ( item : $ alert ) { alertItem in
switch ( alertItem ) {
2022-11-07 20:44:04 +04:00
case let . removeMemberAlert ( mem ) : return removeMemberAlert ( mem )
case let . changeMemberRoleAlert ( mem , _ ) : return changeMemberRoleAlert ( mem )
2022-11-02 09:48:20 +00:00
case . switchAddressAlert : return switchAddressAlert ( switchMemberAddress )
2022-10-04 09:53:43 +01:00
case let . error ( title , error ) : return Alert ( title : Text ( title ) , message : Text ( error ) )
2022-07-26 12:33:10 +04:00
}
}
}
2022-08-26 17:27:38 +04:00
func openDirectChatButton ( _ contactId : Int64 ) -> some View {
Button {
2022-08-29 14:08:46 +01:00
var chat = chatModel . getContactChat ( contactId )
if chat = = nil {
do {
chat = try apiGetChat ( type : . direct , id : contactId )
if let chat = chat {
// T O D O i t ' s n o t c o r r e c t t o b l i n d l y s e t n e t w o r k s t a t u s t o c o n n e c t e d - w e s h o u l d m a n a g e n e t w o r k s t a t u s i n m o d e l / b a c k e n d
chat . serverInfo = Chat . ServerInfo ( networkStatus : . connected )
chatModel . addChat ( chat )
}
} catch let error {
logger . error ( " openDirectChatButton apiGetChat error: \( responseError ( error ) ) " )
2022-08-26 17:27:38 +04:00
}
2022-08-29 14:08:46 +01:00
}
if let chat = chat {
2022-08-26 17:27:38 +04:00
dismissAllSheets ( animated : true )
2022-08-29 14:08:46 +01:00
chatModel . chatId = chat . id
2022-08-26 17:27:38 +04:00
}
} label : {
Label ( " Send direct message " , systemImage : " message " )
}
}
2022-11-07 20:44:04 +04:00
private func groupMemberInfoHeader ( _ mem : GroupMember ) -> some View {
2022-07-26 12:33:10 +04:00
VStack {
2022-11-07 20:44:04 +04:00
ProfileImage ( imageStr : mem . image , color : Color ( uiColor : . tertiarySystemFill ) )
2022-07-26 12:33:10 +04:00
. frame ( width : 192 , height : 192 )
. padding ( . top , 12 )
. padding ( )
2022-11-07 20:44:04 +04:00
Text ( mem . displayName )
2022-07-26 12:33:10 +04:00
. font ( . largeTitle )
. lineLimit ( 1 )
. padding ( . bottom , 2 )
2022-11-07 20:44:04 +04:00
if mem . fullName != " " && mem . fullName != mem . displayName {
Text ( mem . fullName )
2022-07-27 11:16:07 +04:00
. font ( . title2 )
. lineLimit ( 2 )
}
2022-07-26 12:33:10 +04:00
}
. frame ( maxWidth : . infinity , alignment : . center )
}
2022-11-07 20:44:04 +04:00
func removeMemberButton ( _ mem : GroupMember ) -> some View {
2022-07-26 12:33:10 +04:00
Button ( role : . destructive ) {
2022-11-07 20:44:04 +04:00
alert = . removeMemberAlert ( mem : mem )
2022-07-26 12:33:10 +04:00
} label : {
Label ( " Remove member " , systemImage : " trash " )
. foregroundColor ( Color . red )
}
}
2022-11-07 20:44:04 +04:00
private func removeMemberAlert ( _ mem : GroupMember ) -> Alert {
2022-07-26 12:33:10 +04:00
Alert (
title : Text ( " Remove member? " ) ,
message : Text ( " Member will be removed from group - this cannot be undone! " ) ,
primaryButton : . destructive ( Text ( " Remove " ) ) {
Task {
do {
2022-11-07 20:44:04 +04:00
let updatedMember = try await apiRemoveMember ( groupInfo . groupId , mem . groupMemberId )
2022-08-09 13:43:19 +04:00
await MainActor . run {
2022-11-07 20:44:04 +04:00
_ = chatModel . upsertGroupMember ( groupInfo , updatedMember )
2022-08-09 13:43:19 +04:00
dismiss ( )
}
2022-07-26 12:33:10 +04:00
} catch let error {
2022-08-09 13:43:19 +04:00
logger . error ( " apiRemoveMember error: \( responseError ( error ) ) " )
2022-10-06 15:02:58 +01:00
let a = getErrorAlert ( error , " Error removing member " )
alert = . error ( title : a . title , error : a . message )
2022-07-26 12:33:10 +04:00
}
}
} ,
secondaryButton : . cancel ( )
)
}
2022-10-04 09:53:43 +01:00
2022-11-07 20:44:04 +04:00
private func changeMemberRoleAlert ( _ mem : GroupMember ) -> Alert {
2022-10-04 09:53:43 +01:00
Alert (
title : Text ( " Change member role? " ) ,
2022-11-07 20:44:04 +04:00
message : mem . memberCurrent ? Text ( " Member role will be changed to \" \( newRole . text ) \" . All group members will be notified. " ) : Text ( " Member role will be changed to \" \( newRole . text ) \" . The member will receive a new invitation. " ) ,
2022-10-04 09:53:43 +01:00
primaryButton : . default ( Text ( " Change " ) ) {
Task {
do {
2022-11-07 20:44:04 +04:00
let updatedMember = try await apiMemberRole ( groupInfo . groupId , mem . groupMemberId , newRole )
2022-10-04 09:53:43 +01:00
await MainActor . run {
2022-11-07 20:44:04 +04:00
member = updatedMember
_ = chatModel . upsertGroupMember ( groupInfo , updatedMember )
2022-10-04 09:53:43 +01:00
}
2022-11-07 20:44:04 +04:00
2022-10-04 09:53:43 +01:00
} catch let error {
2022-11-07 20:44:04 +04:00
newRole = mem . memberRole
2022-10-04 09:53:43 +01:00
logger . error ( " apiMemberRole error: \( responseError ( error ) ) " )
2022-10-06 15:02:58 +01:00
let a = getErrorAlert ( error , " Error changing role " )
alert = . error ( title : a . title , error : a . message )
2022-10-04 09:53:43 +01:00
}
}
} ,
secondaryButton : . cancel {
2022-11-07 20:44:04 +04:00
newRole = mem . memberRole
2022-10-04 09:53:43 +01:00
}
)
}
2022-11-02 09:48:20 +00:00
private func switchMemberAddress ( ) {
Task {
do {
2022-11-07 20:44:04 +04:00
if let member = member {
try await apiSwitchGroupMember ( groupInfo . apiId , member . groupMemberId )
}
2022-11-02 09:48:20 +00:00
} catch let error {
logger . error ( " switchMemberAddress apiSwitchGroupMember error: \( responseError ( error ) ) " )
let a = getErrorAlert ( error , " Error changing address " )
await MainActor . run {
alert = . error ( title : a . title , error : a . message )
}
}
}
}
2022-07-26 12:33:10 +04:00
}
struct GroupMemberInfoView_Previews : PreviewProvider {
static var previews : some View {
2022-11-01 20:30:53 +00:00
GroupMemberInfoView (
groupInfo : GroupInfo . sampleData ,
2022-11-07 20:44:04 +04:00
member : Binding . constant ( GroupMember . sampleData ) ,
2022-11-01 20:30:53 +00:00
connectionStats : Binding . constant ( nil )
)
2022-07-26 12:33:10 +04:00
}
}