@ -26,7 +26,7 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Editabl
public let editableState : EditableState < TableItem > = EditableState ( )
public let state : TableDataState < Section , TableItem > = TableDataState ( )
public let observableState : ObservableTableSourceState < Section , TableItem > = ObservableTableSourceState ( )
private let selectedIdsSubject : CurrentValueSubject < Set < String > , Never > = CurrentValueSubject ( [] )
private let selectedIdsSubject : CurrentValueSubject < ( name : String , ids : Set < String > ) , Never > = CurrentValueSubject ( (" " , [] ) )
private let threadId : String
private let userSessionId : SessionId
@ -258,8 +258,8 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Editabl
leadingAccessory : . icon ( UIImage ( named : " icon_invite " ) ? . withRenderingMode ( . alwaysTemplate ) ) ,
title : " membersInvite " . localized ( ) ,
accessibility : Accessibility (
identifier : " Add members " ,
label : " Add members "
identifier : " Invite button " ,
label : " Invite button "
) ,
onTap : { [ weak self ] in self ? . inviteContacts ( currentGroupName : state . group . name ) }
) ,
@ -315,7 +315,7 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Editabl
case ( . standard , . failed ) , ( . standard , . notSentYet ) , ( . standard , . pending ) :
return . highlightingBackgroundLabelAndRadio (
title : " resend " . localized ( ) ,
isSelected : selectedIdsSubject . value . contains ( memberInfo . profileId ) ,
isSelected : selectedIdsSubject . value . ids . contains ( memberInfo . profileId ) ,
labelAccessibility : Accessibility (
identifier : " Resend invite button " ,
label : " Resend invite button "
@ -328,7 +328,7 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Editabl
case ( . standard , . accepted ) , ( . zombie , _ ) :
return . radio (
isSelected : selectedIdsSubject . value . contains ( memberInfo . profileId )
isSelected : selectedIdsSubject . value . ids . contains ( memberInfo . profileId )
)
}
} ( ) ,
@ -357,11 +357,17 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Editabl
case ( . standard , . failed , _ ) , ( . standard , . notSentYet , _ ) , ( . standard , . pending , _ ) ,
( . standard , . accepted , _ ) , ( . zombie , _ , _ ) :
if ! selectedIdsSubject . value . contains ( memberInfo . profileId ) {
selectedIdsSubject . send ( selectedIdsSubject . value . inserting ( memberInfo . profileId ) )
if ! selectedIdsSubject . value . ids . contains ( memberInfo . profileId ) {
selectedIdsSubject . send ( (
state . group . name ,
selectedIdsSubject . value . ids . inserting ( memberInfo . profileId )
) )
}
else {
selectedIdsSubject . send ( selectedIdsSubject . value . removing ( memberInfo . profileId ) )
selectedIdsSubject . send ( (
state . group . name ,
selectedIdsSubject . value . ids . removing ( memberInfo . profileId )
) )
}
// F o r c e t h e t a b l e d a t a t o b e r e f r e s h e d ( t h e d a t a b a s e w o u l d n ' t
@ -377,7 +383,7 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Editabl
lazy var footerButtonInfo : AnyPublisher < SessionButton . Info ? , Never > = selectedIdsSubject
. prepend ( [ ] )
. map { selectedIds in
. map { currentGroupName, selectedIds in
SessionButton . Info (
style : . destructive ,
title : " remove " . localized ( ) ,
@ -385,7 +391,7 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Editabl
accessibility : Accessibility (
identifier : " Remove contact button "
) ,
onTap : { [ weak self ] in self ? . removeMembers ( memberIds: selectedIds ) }
onTap : { [ weak self ] in self ? . removeMembers ( currentGroupName: currentGroupName , memberIds: selectedIds ) }
)
}
. eraseToAnyPublisher ( )
@ -423,9 +429,16 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Editabl
\ ( groupMember [ . groupId ] ) = \ ( threadId ) AND
\ ( groupMember [ . profileId ] ) = \ ( contact [ . id ] )
)
WHERE \ ( groupMember [ . profileId ] ) IS NULL
WHERE (
\ ( groupMember [ . profileId ] ) IS NULL AND
\ ( contact [ . isApproved ] ) = TRUE AND
\ ( contact [ . didApproveMe ] ) = TRUE
)
" " " ),
footerTitle : " membersInviteTitle " . localized ( ) ,
footerAccessibility : Accessibility (
identifier : " Confirm invite button "
) ,
onSubmit : { [ weak self , threadId , dependencies ] in
switch try ? SessionId . Prefix ( from : threadId ) {
case . group :
@ -636,94 +649,144 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Editabl
self . showToast ( text : " groupInviteSending " . putNumber ( 1 ) . localized ( ) )
}
private func removeMembers ( memberIds: Set < String > ) {
private func removeMembers ( currentGroupName: String , memberIds: Set < String > ) {
guard ! memberIds . isEmpty else { return }
switch try ? SessionId . Prefix ( from : threadId ) {
case . group :
MessageSender
. removeGroupMembers (
groupSessionId : threadId ,
memberIds : memberIds ,
removeTheirMessages : dependencies [ feature : . updatedGroupsRemoveMessagesOnKick ] ,
sendMemberChangedMessage : true ,
using : dependencies
)
. subscribe ( on : DispatchQueue . global ( qos : . userInitiated ) , using : dependencies )
. sinkUntilComplete ( )
self . selectedIdsSubject . send ( [ ] )
case . standard : // A s s u m e i t ' s a l e g a c y g r o u p
let updatedMemberIds : Set < String > = ( tableData
. first ( where : { $0 . model = = . members } ) ?
. elements
. compactMap { item -> String ? in
switch item . id {
case . member ( let profileId ) : return profileId
default : return nil
}
} )
. defaulting ( to : [ ] )
. asSet ( )
. removing ( contentsOf : memberIds )
let memberNames : [ String ] = memberIds
. compactMap { memberId in
guard
let section : SectionModel = self . tableData
. first ( where : { section in section . model = = . members } ) ,
let info : SessionCell . Info < TableItem > = section . elements
. first ( where : { info in
switch info . id {
case . member ( let infoMemberId ) : return infoMemberId = = memberId
default : return false
}
} )
else {
return Profile . truncated ( id : memberId , truncating : . middle )
}
let viewController = ModalActivityIndicatorViewController ( canCancel : false ) { [ weak self , dependencies , threadId ] modalActivityIndicator in
let currentGroupName : String = dependencies [ singleton : . storage ]
. read { db in
try ClosedGroup
. filter ( id : threadId )
. select ( . name )
. asRequest ( of : String . self )
. fetchOne ( db )
}
. defaulting ( to : " groupUnknown " . localized ( ) )
MessageSender
. update (
legacyGroupSessionId : threadId ,
with : updatedMemberIds ,
name : currentGroupName ,
using : dependencies
)
. eraseToAnyPublisher ( )
. subscribe ( on : DispatchQueue . global ( qos : . userInitiated ) )
. receive ( on : DispatchQueue . main )
. sinkUntilComplete (
receiveCompletion : { [ weak self ] result in
modalActivityIndicator . dismiss ( completion : {
switch result {
case . finished : self ? . selectedIdsSubject . send ( [ ] )
case . failure :
self ? . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " theError " . localized ( ) ,
body : . text ( " deleteAfterLegacyGroupsGroupUpdateErrorTitle " . localized ( ) ) ,
cancelTitle : " okay " . localized ( ) ,
cancelStyle : . alert_text
return info . title ? . text
}
let confirmationBody : NSAttributedString = {
switch memberNames . count {
case 1 :
return " groupRemoveDescription "
. put ( key : " name " , value : memberNames [ 0 ] )
. put ( key : " group_name " , value : currentGroupName )
. localizedFormatted ( baseFont : . systemFont ( ofSize : Values . smallFontSize ) )
case 2 :
return " groupRemoveDescriptionTwo "
. put ( key : " name " , value : memberNames [ 0 ] )
. put ( key : " other_name " , value : memberNames [ 1 ] )
. put ( key : " group_name " , value : currentGroupName )
. localizedFormatted ( baseFont : . systemFont ( ofSize : Values . smallFontSize ) )
default :
return " groupRemoveDescriptionMultiple "
. put ( key : " name " , value : memberNames [ 0 ] )
. put ( key : " count " , value : memberNames . count - 1 )
. put ( key : " group_name " , value : currentGroupName )
. localizedFormatted ( baseFont : . systemFont ( ofSize : Values . smallFontSize ) )
}
} ( )
let confirmationModal : ConfirmationModal = ConfirmationModal (
info : ConfirmationModal . Info (
title : " remove " . localized ( ) ,
body : . attributedText ( confirmationBody ) ,
confirmTitle : " remove " . localized ( ) ,
confirmStyle : . danger ,
cancelStyle : . alert_text ,
dismissOnConfirm : false ,
onConfirm : { [ weak self , threadId , dependencies ] modal in
switch try ? SessionId . Prefix ( from : threadId ) {
case . group :
MessageSender
. removeGroupMembers (
groupSessionId : threadId ,
memberIds : memberIds ,
removeTheirMessages : dependencies [ feature : . updatedGroupsRemoveMessagesOnKick ] ,
sendMemberChangedMessage : true ,
using : dependencies
)
. subscribe ( on : DispatchQueue . global ( qos : . userInitiated ) , using : dependencies )
. sinkUntilComplete ( )
self ? . selectedIdsSubject . send ( ( currentGroupName , [ ] ) )
modal . dismiss ( animated : true )
case . standard : // A s s u m e i t ' s a l e g a c y g r o u p
let updatedMemberIds : Set < String > = ( self ? . tableData
. first ( where : { $0 . model = = . members } ) ?
. elements
. compactMap { item -> String ? in
switch item . id {
case . member ( let profileId ) : return profileId
default : return nil
}
} )
. defaulting ( to : [ ] )
. asSet ( )
. removing ( contentsOf : memberIds )
let viewController = ModalActivityIndicatorViewController ( canCancel : false ) { [ weak self , dependencies , threadId ] modalActivityIndicator in
MessageSender
. update (
legacyGroupSessionId : threadId ,
with : updatedMemberIds ,
name : currentGroupName ,
using : dependencies
)
. eraseToAnyPublisher ( )
. subscribe ( on : DispatchQueue . global ( qos : . userInitiated ) )
. receive ( on : DispatchQueue . main )
. sinkUntilComplete (
receiveCompletion : { [ weak self ] result in
modalActivityIndicator . dismiss ( completion : {
switch result {
case . finished :
self ? . selectedIdsSubject . send ( ( currentGroupName , [ ] ) )
modalActivityIndicator . dismiss {
modal . dismiss ( animated : true )
}
case . failure :
self ? . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " theError " . localized ( ) ,
body : . text ( " deleteAfterLegacyGroupsGroupUpdateErrorTitle " . localized ( ) ) ,
cancelTitle : " okay " . localized ( ) ,
cancelStyle : . alert_text
)
) ,
transitionType : . present
)
) ,
transitionType : . present
)
}
} )
}
} )
}
)
}
self ? . transitionToScreen ( viewController , transitionType : . present )
default :
self ? . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " theError " . localized ( ) ,
body : . text ( " deleteAfterLegacyGroupsGroupUpdateErrorTitle " . localized ( ) ) ,
cancelTitle : " okay " . localized ( ) ,
cancelStyle : . alert_text
)
) ,
transitionType : . present
)
}
}
self . transitionToScreen ( viewController , transitionType : . present )
default :
self . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " theError " . localized ( ) ,
body : . text ( " deleteAfterLegacyGroupsGroupUpdateErrorTitle " . localized ( ) ) ,
cancelTitle : " okay " . localized ( ) ,
cancelStyle : . alert_text
)
) ,
transitionType : . present
)
}
)
)
self . transitionToScreen ( confirmationModal , transitionType : . present )
}
}