@ -11,18 +11,24 @@ import SignalUtilitiesKit
import SessionUtilitiesKit
import SessionSnodeKit
class ThreadSettingsViewModel : SessionTableViewModel , Navigat ionItemSource, NavigatableStateHolder , Edit ableStateHolder, ObservableTableSource {
class ThreadSettingsViewModel : SessionTableViewModel , Navigat ableStateHolder, ObservableTableSource {
public let dependencies : Dependencies
public let navigatableState : NavigatableState = NavigatableState ( )
public let editableState : EditableState < TableItem > = EditableState ( )
public let state : TableDataState < Section , TableItem > = TableDataState ( )
public let observableState : ObservableTableSourceState < Section , TableItem > = ObservableTableSourceState ( )
private let threadId : String
private let threadVariant : SessionThread . Variant
private let didTriggerSearch : ( ) -> ( )
private var oldDisplayName : String ?
private var editedDisplayName : String ?
private var updatedName : String ?
private var updatedDescription : String ?
private var onDisplayPictureSelected : ( ( ConfirmationModal . ValueUpdate ) -> Void ) ?
private lazy var imagePickerHandler : ImagePickerHandler = ImagePickerHandler (
onTransition : { [ weak self ] in self ? . transitionToScreen ( $0 , transitionType : $1 ) } ,
onImageDataPicked : { [ weak self ] resultImageData in
self ? . onDisplayPictureSelected ? ( . image ( resultImageData ) )
}
)
// MARK: - I n i t i a l i z a t i o n
@ -36,16 +42,6 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
self . threadId = threadId
self . threadVariant = threadVariant
self . didTriggerSearch = didTriggerSearch
self . oldDisplayName = ( threadVariant != . contact ?
nil :
dependencies [ singleton : . storage ] . read { db in
try Profile
. filter ( id : threadId )
. select ( . nickname )
. asRequest ( of : String . self )
. fetchOne ( db )
}
)
}
// MARK: - C o n f i g
@ -68,7 +64,7 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
public enum TableItem : Differentiable {
case avatar
case nickn ame
case displayN ame
case threadDescription
case sessionId
@ -87,99 +83,6 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
case blockUser
}
// MARK: - N a v i g a t i o n
lazy var navState : AnyPublisher < NavState , Never > = {
Publishers
. CombineLatest (
isEditing ,
textChanged
. handleEvents (
receiveOutput : { [ weak self ] value , _ in
self ? . editedDisplayName = value
}
)
. filter { _ in false }
. prepend ( ( nil , . nickname ) )
)
. map { isEditing , _ -> NavState in ( isEditing ? . editing : . standard ) }
. removeDuplicates ( )
. prepend ( . standard ) // I n i t i a l v a l u e
. shareReplay ( 1 )
. eraseToAnyPublisher ( )
} ( )
lazy var leftNavItems : AnyPublisher < [ SessionNavItem < NavItem > ] , Never > = navState
. map { [ weak self ] navState -> [ SessionNavItem < NavItem > ] in
// O n l y s h o w t h e ' E d i t ' b u t t o n i f i t ' s a c o n t a c t t h r e a d
guard self ? . threadVariant = = . contact else { return [ ] }
guard navState = = . editing else { return [ ] }
return [
SessionNavItem (
id : . cancel ,
systemItem : . cancel ,
accessibilityIdentifier : " Cancel button "
) { [ weak self ] in
self ? . setIsEditing ( false )
self ? . editedDisplayName = self ? . oldDisplayName
}
]
}
. eraseToAnyPublisher ( )
lazy var rightNavItems : AnyPublisher < [ SessionNavItem < NavItem > ] , Never > = navState
. map { [ weak self , dependencies ] navState -> [ SessionNavItem < NavItem > ] in
// O n l y s h o w t h e ' E d i t ' b u t t o n i f i t ' s a c o n t a c t t h r e a d
guard self ? . threadVariant = = . contact else { return [ ] }
switch navState {
case . editing :
return [
SessionNavItem (
id : . done ,
systemItem : . done ,
accessibilityIdentifier : " Done "
) { [ weak self ] in
self ? . setIsEditing ( false )
guard
self ? . threadVariant = = . contact ,
let threadId : String = self ? . threadId ,
let editedDisplayName : String = self ? . editedDisplayName
else { return }
let updatedNickname : String = editedDisplayName
. trimmingCharacters ( in : . whitespacesAndNewlines )
self ? . oldDisplayName = ( updatedNickname . isEmpty ? nil : editedDisplayName )
dependencies [ singleton : . storage ] . writeAsync { db in
try Profile
. filter ( id : threadId )
. updateAllAndConfig (
db ,
Profile . Columns . nickname
. set ( to : ( updatedNickname . isEmpty ? nil : editedDisplayName ) ) ,
calledFromConfig : nil ,
using : dependencies
)
}
}
]
case . standard :
return [
SessionNavItem (
id : . edit ,
systemItem : . edit ,
accessibilityIdentifier : " Edit button " ,
accessibilityLabel : " Edit user nickname "
) { [ weak self ] in self ? . setIsEditing ( true ) }
]
}
}
. eraseToAnyPublisher ( )
// MARK: - C o n t e n t
private struct State : Equatable {
@ -234,9 +137,14 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
threadViewModel . currentUserIsClosedGroupAdmin = = true
)
let editIcon : UIImage ? = UIImage ( systemName : " pencil " )
let canEditDisplayName : Bool = (
threadViewModel . threadIsNoteToSelf != true && (
threadViewModel . threadVariant = = . contact ||
currentUserIsClosedGroupAdmin
)
)
return [
SectionModel (
let conversationInfoSection : SectionModel = SectionModel (
model : . conversationInfo ,
elements : [
SessionCell . Info (
@ -247,9 +155,27 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
threadVariant : threadViewModel . threadVariant ,
displayPictureFilename : threadViewModel . displayPictureFilename ,
profile : threadViewModel . profile ,
profileIcon : . none ,
profileIcon : {
guard
threadViewModel . threadVariant = = . group &&
currentUserIsClosedGroupAdmin &&
dependencies [ feature : . updatedGroupsAllowDisplayPicture ]
else { return . none }
// I f w e a l r e a d y h a v e a d i s p l a y p i c t u r e t h e n t h e m a i n p r o f i l e g e t s t h e i c o n
return ( threadViewModel . displayPictureFilename != nil ? . rightPlus : . none )
} ( ) ,
additionalProfile : threadViewModel . additionalProfile ,
additionalProfileIcon : . none ,
additionalProfileIcon : {
guard
threadViewModel . threadVariant = = . group &&
currentUserIsClosedGroupAdmin &&
dependencies [ feature : . updatedGroupsAllowDisplayPicture ]
else { return . none }
// N o d i s p l a y p i c t u r e m e a n s t h e d u a l - p r o f i l e s o t h e a d d i t i o n a l P r o f i l e g e t s t h e i c o n
return . rightPlus
} ( ) ,
accessibility : nil
) ,
styling : SessionCell . StyleInfo (
@ -257,39 +183,46 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
customPadding : SessionCell . Padding ( bottom : Values . smallSpacing ) ,
backgroundStyle : . noBackground
) ,
onTap : { [ weak self ] in self ? . viewProfilePicture ( threadViewModel : threadViewModel ) }
onTap : { [ weak self ] in
switch ( threadViewModel . threadVariant , threadViewModel . displayPictureFilename , currentUserIsClosedGroupAdmin ) {
case ( . contact , _ , _ ) : self ? . viewDisplayPicture ( threadViewModel : threadViewModel )
case ( . group , _ , true ) :
self ? . updateGroupDisplayPicture ( currentFileName : threadViewModel . displayPictureFilename )
case ( _ , . some , _ ) : self ? . viewDisplayPicture ( threadViewModel : threadViewModel )
default : break
}
}
) ,
SessionCell . Info (
id : . nickname ,
leadingAccessory : ( threadViewModel . threadVariant != . contact ? nil :
id : . displayN ame,
leadingAccessory : ( ! canEditDisplayName ? nil :
. icon (
editIcon ? . withRenderingMode ( . alwaysTemplate ) ,
size : . medium ,
customTint : . textSecondary
size : . mediumAspectFill ,
customTint : . textSecondary ,
shouldFill : true
)
) ,
title : SessionCell . TextInfo (
threadViewModel . displayName ,
font : . titleLarge ,
alignment : . center ,
editingPlaceholder : " nicknameEnter " . localized ( ) ,
interaction : ( threadViewModel . threadVariant = = . contact ? . editable : . none )
alignment : . center
) ,
styling : SessionCell . StyleInfo (
alignment : . centerHugging ,
customPadding : SessionCell . Padding (
top : Values . smallSpacing ,
leading : ( threadViewModel . threadVariant != . contact ?
nil :
leading : ( ! canEditDisplayName ? nil :
- ( ( IconSize . medium . size + ( Values . smallSpacing * 2 ) ) / 2 )
) ,
bottom : {
guard threadViewModel . threadVariant != . contact else { return Values . smallSpacing }
guard threadViewModel . threadDescription = = nil else { return Values . smallSpacing }
return nil
} ( ) ,
interItem : 0
return Values . largeSpacing
} ( )
) ,
backgroundStyle : . noBackground
) ,
@ -298,8 +231,27 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
label : threadViewModel . displayName
) ,
onTap : { [ weak self ] in
self ? . textChanged ( self ? . oldDisplayName , for : . nickname )
self ? . setIsEditing ( true )
switch ( threadViewModel . threadVariant , currentUserIsClosedGroupAdmin ) {
case ( . contact , _ ) :
self ? . updateNickname (
current : threadViewModel . profile ? . nickname ,
displayName : (
// / * * N o t e : * * W e w a n t t o u s e t h e ` p r o f i l e ` d i r e c t l y r a t h e r t h a n ` t h r e a d V i e w M o d e l . d i s p l a y N a m e `
// / a s t h e l a t t e r w o u l d u s e t h e ` n i c k n a m e ` h e r e w h i c h i s i n c o r r e c t
threadViewModel . profile ? . displayName ( ignoringNickname : true ) ? ?
Profile . truncated ( id : threadViewModel . threadId , truncating : . middle )
)
)
case ( . group , true ) , ( . legacyGroup , true ) :
self ? . updateGroupNameAndDescription (
currentName : threadViewModel . displayName ,
currentDescription : threadViewModel . threadDescription ,
isUpdatedGroup : ( threadViewModel . threadVariant = = . group )
)
case ( . community , _ ) , ( . legacyGroup , false ) , ( . group , false ) : break
}
}
) ,
@ -349,8 +301,8 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
)
)
] . compactMap { $0 }
) ,
SectionModel (
)
let standardActionsSection : SectionModel = SectionModel (
model : . content ,
elements : [
( threadViewModel . threadVariant = = . legacyGroup || threadViewModel . threadVariant = = . group ? nil :
@ -725,7 +677,7 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
confirmStyle : . danger ,
cancelStyle : . alert_text
) ,
onTap : { [ weak self , dependencies ] in
onTap : { [ weak self ] in
let isBlocked : Bool = ( threadViewModel . threadIsBlocked = = true )
self ? . updateBlockedState (
@ -739,28 +691,56 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
)
] . compactMap { $0 }
)
]
return [
conversationInfoSection ,
standardActionsSection
] . compactMap { $0 }
}
// MARK: - F u n c t i o n s
private func viewProfilePicture ( threadViewModel : SessionThreadViewModel ) {
private func viewDisplayPicture ( threadViewModel : SessionThreadViewModel ) {
let displayPictureData : Data
let ownerId : DisplayPictureManager . OwnerId = {
switch threadViewModel . threadVariant {
case . contact : . user ( threadViewModel . threadId )
case . group , . legacyGroup : . group ( threadViewModel . threadId )
case . community : . community ( threadViewModel . threadId )
}
} ( )
switch threadViewModel . threadVariant {
case . legacyGroup : return // N o d i s p l a y p i c t u r e s f o r l e g a c y g r o u p s
case . contact :
guard
threadViewModel . threadVariant = = . contact ,
let profile : Profile = threadViewModel . profile ,
let profileData : Data = DisplayPictureManager . displayPicture ( owner : . user ( profile ) , using : dependencies )
let imageData : Data = DisplayPictureManager . displayPicture ( owner : . user ( profile ) , using : dependencies )
else { return }
displayPictureData = imageData
default :
guard
threadViewModel . displayPictureFilename != nil ,
let imageData : Data = dependencies [ singleton : . storage ] . read ( { [ dependencies ] db in
DisplayPictureManager . displayPicture ( db , id : ownerId , using : dependencies )
} )
else { return }
let format : ImageFormat = profileData . guessedImageFormat
displayPictureData = imageData
}
let format : ImageFormat = displayPictureData . guessedImageFormat
let navController : UINavigationController = StyledNavigationController (
rootViewController : ProfilePictureVC (
image : ( format = = . gif || format = = . webp ?
nil :
UIImage ( data : profileData )
UIImage ( data : dis playPictu reData)
) ,
animatedImage : ( format != . gif && format != . webp ?
nil :
YYImage ( data : profil eData)
YYImage ( data : dis playPictu reData)
) ,
title : threadViewModel . displayName
)
@ -973,6 +953,368 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
)
}
private func updateNickname ( current : String ? , displayName : String ) {
// / S e t ` u p d a t e d N a m e ` t o ` c u r r e n t ` s o w e c a n d i s a b l e t h e " s a v e " b u t t o n w h e n t h e r e a r e n o c h a n g e s a n d d o n ' t n e e d t o w o r r y
// / a b o u t r e t r i e v i n g t h e m i n t h e c o n f i r m a t i o n c l o s u r e
self . updatedName = current
self . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " nicknameSet " . localized ( ) ,
body : . input (
explanation : " nicknameDescription "
. put ( key : " name " , value : displayName )
. localizedFormatted ( baseFont : ConfirmationModal . explanationFont ) ,
info : ConfirmationModal . Info . Body . InputInfo (
placeholder : " nicknameEnter " . localized ( ) ,
initialValue : current
) ,
onChange : { [ weak self ] updatedName in self ? . updatedName = updatedName }
) ,
confirmTitle : " save " . localized ( ) ,
confirmEnabled : . afterChange { [ weak self ] _ in
self ? . updatedName != current &&
self ? . updatedName ? . trimmingCharacters ( in : . whitespacesAndNewlines ) . isEmpty = = false
} ,
cancelTitle : " RESET " ,
cancelStyle : . danger ,
cancelEnabled : . bool ( current ? . trimmingCharacters ( in : . whitespacesAndNewlines ) . isEmpty = = false ) ,
hasCloseButton : true ,
dismissOnConfirm : false ,
onConfirm : { [ weak self , dependencies , threadId ] modal in
guard
let finalNickname : String = ( self ? . updatedName ? ? " " )
. trimmingCharacters ( in : . whitespacesAndNewlines )
. nullIfEmpty
else { return }
// / C h e c k i f t h e d a t a v i o l a t e s t h e s i z e c o n s t r a i n t s
guard ! Profile . isTooLong ( profileName : finalNickname ) else {
self ? . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " theError " . localized ( ) ,
body : . text ( " displayNameErrorDescriptionShorter " . localized ( ) ) ,
cancelTitle : " okay " . localized ( ) ,
cancelStyle : . alert_text ,
dismissType : . single
)
) ,
transitionType : . present
)
return
}
// / U p d a t e t h e n i c k n a m e
dependencies [ singleton : . storage ] . writeAsync { db in
try Profile
. filter ( id : threadId )
. updateAllAndConfig (
db ,
Profile . Columns . nickname . set ( to : finalNickname ) ,
calledFromConfig : nil ,
using : dependencies
)
}
modal . dismiss ( animated : true )
} ,
onCancel : { [ dependencies , threadId ] modal in
// / R e m o v e t h e n i c k n a m e
dependencies [ singleton : . storage ] . writeAsync { db in
try Profile
. filter ( id : threadId )
. updateAllAndConfig (
db ,
Profile . Columns . nickname . set ( to : nil ) ,
calledFromConfig : nil ,
using : dependencies
)
}
modal . dismiss ( animated : true )
}
)
) ,
transitionType : . present
)
}
private func updateGroupNameAndDescription (
currentName : String ,
currentDescription : String ? ,
isUpdatedGroup : Bool
) {
// / S e t t h e ` u p d a t e d N a m e ` a n d ` u p d a t e d D e s c r i p t i o n ` v a l u e s t o t h e c u r r e n t v a l u e s s o w e c a n d i s a b l e t h e " s a v e " b u t t o n w h e n t h e r e a r e
// / n o c h a n g e s a n d d o n ' t n e e d t o w o r r y a b o u t r e t r i e v i n g t h e m i n t h e c o n f i r m a t i o n c l o s u r e
self . updatedName = currentName
self . updatedDescription = currentDescription
self . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " groupInformationSet " . localized ( ) ,
body : { [ weak self , dependencies ] in
guard isUpdatedGroup && dependencies [ feature : . updatedGroupsAllowDescriptionEditing ] else {
return . input (
explanation : NSAttributedString ( string : " EDIT_LEGACY_GROUP_INFO_MESSAGE " ) , // . l o c a l i z e d ( ) ) ,
info : ConfirmationModal . Info . Body . InputInfo (
placeholder : " groupNameEnter " . localized ( ) ,
initialValue : currentName
) ,
onChange : { updatedName in self ? . updatedName = updatedName }
)
}
return . dualInput (
explanation : NSAttributedString ( string : " EDIT_GROUP_INFO_MESSAGE " ) , // . l o c a l i z e d ( ) ) ,
firstInfo : ConfirmationModal . Info . Body . InputInfo (
placeholder : " groupNameEnter " . localized ( ) ,
initialValue : currentName
) ,
secondInfo : ConfirmationModal . Info . Body . InputInfo (
placeholder : " groupDescriptionEnter " . localized ( ) ,
initialValue : currentDescription
) ,
onChange : { updatedName , updatedDescription in
self ? . updatedName = updatedName
self ? . updatedDescription = updatedDescription
}
)
} ( ) ,
confirmTitle : " save " . localized ( ) ,
confirmEnabled : . afterChange { [ weak self ] _ in
self ? . updatedName ? . trimmingCharacters ( in : . whitespacesAndNewlines ) . isEmpty = = false && (
self ? . updatedName != currentName ||
self ? . updatedDescription != currentDescription
)
} ,
cancelStyle : . danger ,
dismissOnConfirm : false ,
onConfirm : { [ weak self , dependencies , threadId ] modal in
guard
let finalName : String = ( self ? . updatedName ? ? " " )
. trimmingCharacters ( in : . whitespacesAndNewlines )
. nullIfEmpty
else { return }
let finalDescription : String ? = self ? . updatedDescription
. map { $0 . trimmingCharacters ( in : . whitespacesAndNewlines ) }
// / C h e c k i f t h e d a t a v i o l a t e s a n y o f t h e s i z e c o n s t r a i n t s
let maybeErrorString : String ? = {
guard ! Profile . isTooLong ( profileName : finalName ) else { return " groupNameEnterShorter " . localized ( ) }
return nil // N o e r r o r h a s o c c u r r e d
} ( )
if let errorString : String = maybeErrorString {
self ? . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " theError " . localized ( ) ,
body : . text ( errorString ) ,
cancelTitle : " okay " . localized ( ) ,
cancelStyle : . alert_text ,
dismissType : . single
)
) ,
transitionType : . present
)
return
}
// / U p d a t e t h e g r o u p a p p r o p r i a t e l y
MessageSender
. updateGroup (
groupSessionId : threadId ,
name : finalName ,
groupDescription : finalDescription ,
using : dependencies
)
. sinkUntilComplete (
receiveCompletion : { [ weak self ] result in
switch result {
case . finished : 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
)
}
private func updateGroupDisplayPicture ( currentFileName : String ? ) {
guard dependencies [ feature : . updatedGroupsAllowDisplayPicture ] else { return }
let existingImageData : Data ? = dependencies [ singleton : . storage ] . read { [ threadId , dependencies ] db in
DisplayPictureManager . displayPicture ( db , id : . group ( threadId ) , using : dependencies )
}
self . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " groupSetDisplayPicture " . localized ( ) ,
body : . image (
placeholderData : UIImage ( named : " profile_placeholder " ) ? . pngData ( ) ,
valueData : existingImageData ,
icon : . rightPlus ,
style : . circular ,
accessibility : Accessibility (
identifier : " Image picker " ,
label : " Image picker "
) ,
onClick : { [ weak self ] onDisplayPictureSelected in
self ? . onDisplayPictureSelected = onDisplayPictureSelected
self ? . showPhotoLibraryForAvatar ( )
}
) ,
confirmTitle : " save " . localized ( ) ,
confirmEnabled : . afterChange { info in
switch info . body {
case . image ( _ , let valueData , _ , _ , _ , _ ) : return ( valueData != nil )
default : return false
}
} ,
cancelTitle : " remove " . localized ( ) ,
cancelEnabled : . bool ( existingImageData != nil ) ,
hasCloseButton : true ,
dismissOnConfirm : false ,
onConfirm : { [ weak self ] modal in
switch modal . info . body {
case . image ( _ , . some ( let valueData ) , _ , _ , _ , _ ) :
self ? . updateGroupDisplayPicture (
displayPictureUpdate : . groupUploadImageData ( valueData ) ,
onComplete : { [ weak modal ] in modal ? . close ( ) }
)
default : modal . close ( )
}
} ,
onCancel : { [ weak self ] modal in
self ? . updateGroupDisplayPicture (
displayPictureUpdate : . groupRemove ,
onComplete : { [ weak modal ] in modal ? . close ( ) }
)
}
)
) ,
transitionType : . present
)
}
private func showPhotoLibraryForAvatar ( ) {
Permissions . requestLibraryPermissionIfNeeded ( isSavingMedia : false , using : dependencies ) { [ weak self ] in
DispatchQueue . main . async {
let picker : UIImagePickerController = UIImagePickerController ( )
picker . sourceType = . photoLibrary
picker . mediaTypes = [ " public.image " ] // s t r i n g l i n t : d i s a b l e
picker . delegate = self ? . imagePickerHandler
self ? . transitionToScreen ( picker , transitionType : . present )
}
}
}
private func updateGroupDisplayPicture (
displayPictureUpdate : DisplayPictureManager . Update ,
onComplete : @ escaping ( ) -> ( )
) {
switch displayPictureUpdate {
case . none : onComplete ( )
default : break
}
func performChanges ( _ viewController : ModalActivityIndicatorViewController , _ displayPictureUpdate : DisplayPictureManager . Update ) {
let existingFileName : String ? = dependencies [ singleton : . storage ] . read { [ threadId ] db in
try ? ClosedGroup
. filter ( id : threadId )
. select ( . displayPictureFilename )
. asRequest ( of : String . self )
. fetchOne ( db )
}
MessageSender
. updateGroup (
groupSessionId : threadId ,
displayPictureUpdate : displayPictureUpdate ,
using : dependencies
)
. sinkUntilComplete (
receiveCompletion : { [ dependencies ] result in
// R e m o v e a n y c a c h e d a v a t a r i m a g e v a l u e
if let existingFileName : String = existingFileName {
dependencies . mutate ( cache : . displayPicture ) { $0 . imageData [ existingFileName ] = nil }
}
DispatchQueue . main . async {
viewController . dismiss ( completion : {
onComplete ( )
} )
}
}
)
}
let viewController = ModalActivityIndicatorViewController ( canCancel : false ) { [ weak self , dependencies ] viewController in
switch displayPictureUpdate {
case . none , . currentUserRemove , . currentUserUploadImageData , . currentUserUpdateTo ,
. contactRemove , . contactUpdateTo :
viewController . dismiss ( animated : true ) // S h o u l d n ' t g e t c a l l e d
case . groupRemove , . groupUpdateTo : performChanges ( viewController , displayPictureUpdate )
case . groupUploadImageData ( let data ) :
DisplayPictureManager . prepareAndUploadDisplayPicture (
queue : DispatchQueue . global ( qos : . background ) ,
imageData : data ,
success : { url , fileName , key in
performChanges ( viewController , . groupUpdateTo ( url : url , key : key , fileName : fileName ) )
} ,
failure : { error in
DispatchQueue . main . async {
viewController . dismiss {
let message : String = {
switch ( displayPictureUpdate , error ) {
case ( . groupRemove , _ ) : return " profileDisplayPictureRemoveError " . localized ( )
case ( _ , . uploadMaxFileSizeExceeded ) :
return " profileDisplayPictureSizeError " . localized ( )
default : return " errorConnection " . localized ( )
}
} ( )
self ? . transitionToScreen (
ConfirmationModal (
info : ConfirmationModal . Info (
title : " deleteAfterLegacyGroupsGroupUpdateErrorTitle " . localized ( ) ,
body : . text ( message ) ,
cancelTitle : " okay " . localized ( ) ,
cancelStyle : . alert_text ,
dismissType : . single
)
) ,
transitionType : . present
)
}
}
} ,
using : dependencies
)
}
}
self . transitionToScreen ( viewController , transitionType : . present )
}
private func updateBlockedState (
from oldBlockedState : Bool ,
isBlocked : Bool ,