Merge pull request #851 from mpretty-cyro/feature/updated-profile-picture-modal
Updated the profile picture management UI & functionality
This commit is contained in:
commit
6be759d39c
|
@ -485,7 +485,7 @@ final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegat
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: title,
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -305,7 +305,7 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate
|
|||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: title,
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
|
||||
|
@ -350,7 +350,7 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "GROUP_CREATION_ERROR_TITLE".localized(),
|
||||
explanation: "GROUP_CREATION_ERROR_MESSAGE".localized(),
|
||||
body: .text("GROUP_CREATION_ERROR_MESSAGE".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -68,7 +68,7 @@ extension ConversationVC:
|
|||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "modal_call_permission_request_title".localized(),
|
||||
explanation: "modal_call_permission_request_explanation".localized(),
|
||||
body: .text("modal_call_permission_request_explanation".localized()),
|
||||
confirmTitle: "vc_settings_title".localized(),
|
||||
confirmAccessibilityLabel: "Settings",
|
||||
cancelAccessibilityLabel: "Cancel",
|
||||
|
@ -132,11 +132,13 @@ extension ConversationVC:
|
|||
format: "modal_blocked_title".localized(),
|
||||
self.viewModel.threadData.displayName
|
||||
),
|
||||
attributedExplanation: NSAttributedString(string: message)
|
||||
.adding(
|
||||
attributes: [ .font: UIFont.boldSystemFont(ofSize: Values.smallFontSize) ],
|
||||
range: (message as NSString).range(of: self.viewModel.threadData.displayName)
|
||||
),
|
||||
body: .attributedText(
|
||||
NSAttributedString(string: message)
|
||||
.adding(
|
||||
attributes: [ .font: UIFont.boldSystemFont(ofSize: Values.smallFontSize) ],
|
||||
range: (message as NSString).range(of: self.viewModel.threadData.displayName)
|
||||
)
|
||||
),
|
||||
confirmTitle: "modal_blocked_button_title".localized(),
|
||||
confirmAccessibilityLabel: "Confirm block",
|
||||
cancelAccessibilityLabel: "Cancel block",
|
||||
|
@ -205,7 +207,7 @@ extension ConversationVC:
|
|||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "GIPHY_PERMISSION_TITLE".localized(),
|
||||
explanation: "GIPHY_PERMISSION_MESSAGE".localized(),
|
||||
body: .text("GIPHY_PERMISSION_MESSAGE".localized()),
|
||||
confirmTitle: "continue_2".localized()
|
||||
) { [weak self] _ in
|
||||
Storage.shared.writeAsync(
|
||||
|
@ -295,7 +297,7 @@ extension ConversationVC:
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "Session",
|
||||
explanation: "An error occurred.",
|
||||
body: .text("An error occurred."),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
@ -312,7 +314,7 @@ extension ConversationVC:
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "ATTACHMENT_PICKER_DOCUMENTS_PICKED_DIRECTORY_FAILED_ALERT_TITLE".localized(),
|
||||
explanation: "ATTACHMENT_PICKER_DOCUMENTS_PICKED_DIRECTORY_FAILED_ALERT_BODY".localized(),
|
||||
body: .text("ATTACHMENT_PICKER_DOCUMENTS_PICKED_DIRECTORY_FAILED_ALERT_BODY".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
@ -410,7 +412,7 @@ extension ConversationVC:
|
|||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "modal_send_seed_title".localized(),
|
||||
explanation: "modal_send_seed_explanation".localized(),
|
||||
body: .text("modal_send_seed_explanation".localized()),
|
||||
confirmTitle: "modal_send_seed_send_button_title".localized(),
|
||||
confirmStyle: .danger,
|
||||
cancelStyle: .alert_text,
|
||||
|
@ -540,7 +542,7 @@ extension ConversationVC:
|
|||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "modal_send_seed_title".localized(),
|
||||
explanation: "modal_send_seed_explanation".localized(),
|
||||
body: .text("modal_send_seed_explanation".localized()),
|
||||
confirmTitle: "modal_send_seed_send_button_title".localized(),
|
||||
confirmStyle: .danger,
|
||||
cancelStyle: .alert_text,
|
||||
|
@ -646,7 +648,7 @@ extension ConversationVC:
|
|||
let linkPreviewModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "modal_link_previews_title".localized(),
|
||||
explanation: "modal_link_previews_explanation".localized(),
|
||||
body: .text("modal_link_previews_explanation".localized()),
|
||||
confirmTitle: "modal_link_previews_button_title".localized()
|
||||
) { [weak self] _ in
|
||||
Storage.shared.writeAsync { db in
|
||||
|
@ -890,11 +892,13 @@ extension ConversationVC:
|
|||
format: "modal_download_attachment_title".localized(),
|
||||
cellViewModel.authorName
|
||||
),
|
||||
attributedExplanation: NSAttributedString(string: message)
|
||||
.adding(
|
||||
attributes: [ .font: UIFont.boldSystemFont(ofSize: Values.smallFontSize) ],
|
||||
range: (message as NSString).range(of: cellViewModel.authorName)
|
||||
),
|
||||
body: .attributedText(
|
||||
NSAttributedString(string: message)
|
||||
.adding(
|
||||
attributes: [ .font: UIFont.boldSystemFont(ofSize: Values.smallFontSize) ],
|
||||
range: (message as NSString).range(of: cellViewModel.authorName)
|
||||
)
|
||||
),
|
||||
confirmTitle: "modal_download_button_title".localized(),
|
||||
confirmAccessibilityLabel: "Download media",
|
||||
cancelAccessibilityLabel: "Don't download media",
|
||||
|
@ -1541,11 +1545,13 @@ extension ConversationVC:
|
|||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "Join \(finalName)?",
|
||||
attributedExplanation: NSMutableAttributedString(string: message)
|
||||
.adding(
|
||||
attributes: [ .font: UIFont.boldSystemFont(ofSize: Values.smallFontSize) ],
|
||||
range: (message as NSString).range(of: finalName)
|
||||
),
|
||||
body: .attributedText(
|
||||
NSMutableAttributedString(string: message)
|
||||
.adding(
|
||||
attributes: [ .font: UIFont.boldSystemFont(ofSize: Values.smallFontSize) ],
|
||||
range: (message as NSString).range(of: finalName)
|
||||
)
|
||||
),
|
||||
confirmTitle: "JOIN_COMMUNITY_BUTTON_TITLE".localized(),
|
||||
onConfirm: { modal in
|
||||
guard let presentingViewController: UIViewController = modal.presentingViewController else {
|
||||
|
@ -1582,7 +1588,7 @@ extension ConversationVC:
|
|||
let errorModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "COMMUNITY_ERROR_GENERIC".localized(),
|
||||
explanation: error.localizedDescription,
|
||||
body: .text(error.localizedDescription),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
@ -2048,7 +2054,7 @@ extension ConversationVC:
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "Session",
|
||||
explanation: "This will ban the selected user from this room. It won't ban them from other rooms.",
|
||||
body: .text("This will ban the selected user from this room. It won't ban them from other rooms."),
|
||||
confirmTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text,
|
||||
onConfirm: { [weak self] _ in
|
||||
|
@ -2072,7 +2078,7 @@ extension ConversationVC:
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: CommonStrings.errorAlertTitle,
|
||||
explanation: "context_menu_ban_user_error_alert_message".localized(),
|
||||
body: .text("context_menu_ban_user_error_alert_message".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
@ -2097,7 +2103,7 @@ extension ConversationVC:
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "Session",
|
||||
explanation: "This will ban the selected user from this room and delete all messages sent by them. It won't ban them from other rooms or delete the messages they sent there.",
|
||||
body: .text("This will ban the selected user from this room and delete all messages sent by them. It won't ban them from other rooms or delete the messages they sent there."),
|
||||
confirmTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text,
|
||||
onConfirm: { [weak self] _ in
|
||||
|
@ -2121,7 +2127,7 @@ extension ConversationVC:
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: CommonStrings.errorAlertTitle,
|
||||
explanation: "context_menu_ban_user_error_alert_message".localized(),
|
||||
body: .text("context_menu_ban_user_error_alert_message".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
@ -2231,7 +2237,7 @@ extension ConversationVC:
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "VOICE_MESSAGE_TOO_SHORT_ALERT_TITLE".localized(),
|
||||
explanation: "VOICE_MESSAGE_TOO_SHORT_ALERT_MESSAGE".localized(),
|
||||
body: .text("VOICE_MESSAGE_TOO_SHORT_ALERT_MESSAGE".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
@ -2302,7 +2308,7 @@ extension ConversationVC:
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "ATTACHMENT_ERROR_ALERT_TITLE".localized(),
|
||||
explanation: (attachment.localizedErrorDescription ?? SignalAttachment.missingDataErrorMessage),
|
||||
body: .text(attachment.localizedErrorDescription ?? SignalAttachment.missingDataErrorMessage),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text,
|
||||
afterClosed: onDismiss
|
||||
|
|
|
@ -1304,7 +1304,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: CommonStrings.errorAlertTitle,
|
||||
explanation: "INVALID_AUDIO_FILE_ALERT_ERROR_MESSAGE".localized(),
|
||||
body: .text("INVALID_AUDIO_FILE_ALERT_ERROR_MESSAGE".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
import GRDB
|
||||
import YYImage
|
||||
import DifferenceKit
|
||||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
|
@ -395,7 +396,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
|
|||
accessibilityLabel: "Leave group",
|
||||
confirmationInfo: ConfirmationModal.Info(
|
||||
title: "leave_group_confirmation_alert_title".localized(),
|
||||
attributedExplanation: {
|
||||
body: .attributedText({
|
||||
if currentUserIsClosedGroupAdmin {
|
||||
return NSAttributedString(string: "admin_group_leave_warning".localized())
|
||||
}
|
||||
|
@ -412,7 +413,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
|
|||
range: (mutableAttributedString.string as NSString).range(of: threadViewModel.displayName)
|
||||
)
|
||||
return mutableAttributedString
|
||||
}(),
|
||||
}()),
|
||||
confirmTitle: "LEAVE_BUTTON_TITLE".localized(),
|
||||
confirmStyle: .danger,
|
||||
cancelStyle: .alert_text
|
||||
|
@ -548,9 +549,8 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
|
|||
threadViewModel.displayName
|
||||
)
|
||||
}(),
|
||||
explanation: (threadViewModel.threadIsBlocked == true ?
|
||||
nil :
|
||||
"BLOCK_USER_BEHAVIOR_EXPLANATION".localized()
|
||||
body: (threadViewModel.threadIsBlocked == true ? .none :
|
||||
.text("BLOCK_USER_BEHAVIOR_EXPLANATION".localized())
|
||||
),
|
||||
confirmTitle: (threadViewModel.threadIsBlocked == true ?
|
||||
"BLOCK_LIST_UNBLOCK_BUTTON".localized() :
|
||||
|
@ -688,13 +688,12 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
|
|||
displayName
|
||||
)
|
||||
),
|
||||
explanation: (oldBlockedState == false ?
|
||||
body: (oldBlockedState == true ? .none : .text(
|
||||
String(
|
||||
format: "BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT".localized(),
|
||||
displayName
|
||||
) :
|
||||
nil
|
||||
),
|
||||
)
|
||||
)),
|
||||
accessibilityLabel: oldBlockedState == false ? "User blocked" : "Confirm unblock",
|
||||
accessibilityId: "Test_name",
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
|
|
|
@ -752,7 +752,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, SeedRemi
|
|||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "delete_conversation_confirmation_alert_title".localized(),
|
||||
attributedExplanation: confirmationModalExplanation,
|
||||
body: .attributedText(confirmationModalExplanation),
|
||||
confirmTitle: "TXT_DELETE_TITLE".localized(),
|
||||
confirmStyle: .danger,
|
||||
cancelStyle: .alert_text,
|
||||
|
@ -837,7 +837,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, SeedRemi
|
|||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: confirmationModalTitle,
|
||||
attributedExplanation: confirmationModalExplanation,
|
||||
body: .attributedText(confirmationModalExplanation),
|
||||
confirmTitle: "LEAVE_BUTTON_TITLE".localized(),
|
||||
confirmStyle: .danger,
|
||||
cancelStyle: .alert_text,
|
||||
|
|
|
@ -214,7 +214,7 @@ final class NewDMVC: BaseVC, UIPageViewControllerDataSource, UIPageViewControlle
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "ALERT_ERROR_TITLE".localized(),
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -389,7 +389,7 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "GIF_PICKER_FAILURE_ALERT_TITLE".localized(),
|
||||
explanation: error.localizedDescription,
|
||||
body: .text(error.localizedDescription),
|
||||
confirmTitle: CommonStrings.retryButton,
|
||||
cancelTitle: CommonStrings.dismissButton,
|
||||
cancelStyle: .alert_text,
|
||||
|
@ -458,7 +458,7 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: CommonStrings.errorAlertTitle,
|
||||
explanation: "GIF_PICKER_VIEW_MISSING_QUERY".localized(),
|
||||
body: .text("GIF_PICKER_VIEW_MISSING_QUERY".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -315,7 +315,7 @@ class PhotoCaptureViewController: OWSViewController {
|
|||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: CommonStrings.errorAlertTitle,
|
||||
explanation: error.localizedDescription,
|
||||
body: .text(error.localizedDescription),
|
||||
cancelTitle: CommonStrings.dismissButton,
|
||||
cancelStyle: .alert_text,
|
||||
afterClosed: { [weak self] in self?.dismiss(animated: true) }
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "profile_placeholder.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "profile_placeholder@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "profile_placeholder@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Session/Meta/Images.xcassets/profile_placeholder.imageset/profile_placeholder.png
vendored
Normal file
BIN
Session/Meta/Images.xcassets/profile_placeholder.imageset/profile_placeholder.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
Session/Meta/Images.xcassets/profile_placeholder.imageset/profile_placeholder@2x.png
vendored
Normal file
BIN
Session/Meta/Images.xcassets/profile_placeholder.imageset/profile_placeholder@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
BIN
Session/Meta/Images.xcassets/profile_placeholder.imageset/profile_placeholder@3x.png
vendored
Normal file
BIN
Session/Meta/Images.xcassets/profile_placeholder.imageset/profile_placeholder@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -623,3 +623,6 @@
|
|||
"group_unable_to_leave" = "Unable to leave the Group, please try again";
|
||||
"delete_conversation_confirmation_alert_message" = "Are you sure you want to delete your conversation with %@?";
|
||||
"delete_conversation_confirmation_alert_title" = "Delete Conversation";
|
||||
"update_profile_modal_title" = "Set Display Picture";
|
||||
"update_profile_modal_upload" = "Upload";
|
||||
"update_profile_modal_remove" = "Remove";
|
||||
|
|
|
@ -158,7 +158,7 @@ final class DisplayNameVC: BaseVC {
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: title,
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -143,7 +143,7 @@ final class LinkDeviceVC: BaseVC, UIPageViewControllerDataSource, UIPageViewCont
|
|||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "invalid_recovery_phrase".localized(),
|
||||
explanation: "INVALID_RECOVERY_PHRASE_MESSAGE".localized(),
|
||||
body: .text("INVALID_RECOVERY_PHRASE_MESSAGE".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text,
|
||||
afterClosed: { [weak self] in
|
||||
|
@ -321,7 +321,7 @@ private final class RecoveryPhraseVC: UIViewController {
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: title,
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -186,7 +186,7 @@ final class RestoreVC: BaseVC {
|
|||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: title,
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -211,7 +211,7 @@ final class JoinOpenGroupVC: BaseVC, UIPageViewControllerDataSource, UIPageViewC
|
|||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: title,
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -9,8 +9,8 @@ import SignalUtilitiesKit
|
|||
final class NukeDataModal: Modal {
|
||||
// MARK: - Initialization
|
||||
|
||||
override init(targetView: UIView? = nil, afterClosed: (() -> ())? = nil) {
|
||||
super.init(targetView: targetView, afterClosed: afterClosed)
|
||||
override init(targetView: UIView? = nil, dismissType: DismissType = .recursive, afterClosed: (() -> ())? = nil) {
|
||||
super.init(targetView: targetView, dismissType: dismissType, afterClosed: afterClosed)
|
||||
|
||||
self.modalPresentationStyle = .overFullScreen
|
||||
self.modalTransitionStyle = .crossDissolve
|
||||
|
@ -135,7 +135,7 @@ final class NukeDataModal: Modal {
|
|||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "modal_clear_all_data_title".localized(),
|
||||
explanation: "modal_clear_all_data_explanation_2".localized(),
|
||||
body: .text("modal_clear_all_data_explanation_2".localized()),
|
||||
confirmTitle: "modal_clear_all_data_confirm".localized(),
|
||||
confirmStyle: .danger,
|
||||
cancelStyle: .alert_text,
|
||||
|
@ -184,7 +184,7 @@ final class NukeDataModal: Modal {
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "ALERT_ERROR_TITLE".localized(),
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
@ -199,7 +199,7 @@ final class NukeDataModal: Modal {
|
|||
targetView: self?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "ALERT_ERROR_TITLE".localized(),
|
||||
explanation: error.localizedDescription,
|
||||
body: .text(error.localizedDescription),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -209,8 +209,8 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
|
|||
accessibilityLabel: "Allow voice and video calls",
|
||||
confirmationInfo: ConfirmationModal.Info(
|
||||
title: "PRIVACY_CALLS_WARNING_TITLE".localized(),
|
||||
explanation: "PRIVACY_CALLS_WARNING_DESCRIPTION".localized(),
|
||||
stateToShow: .whenDisabled,
|
||||
body: .text("PRIVACY_CALLS_WARNING_DESCRIPTION".localized()),
|
||||
showCondition: .disabled,
|
||||
confirmTitle: "continue_2".localized(),
|
||||
confirmAccessibilityLabel: "Enable",
|
||||
confirmStyle: .textPrimary,
|
||||
|
|
|
@ -131,7 +131,7 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "invalid_session_id".localized(),
|
||||
explanation: "INVALID_SESSION_ID_MESSAGE".localized(),
|
||||
body: .text("INVALID_SESSION_ID_MESSAGE".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
|
@ -16,8 +16,8 @@ final class SeedModal: Modal {
|
|||
|
||||
// MARK: - Initialization
|
||||
|
||||
override init(targetView: UIView? = nil, afterClosed: (() -> ())? = nil) {
|
||||
super.init(targetView: targetView, afterClosed: afterClosed)
|
||||
override init(targetView: UIView? = nil, dismissType: DismissType = .recursive, afterClosed: (() -> ())? = nil) {
|
||||
super.init(targetView: targetView, dismissType: dismissType, afterClosed: afterClosed)
|
||||
|
||||
self.modalPresentationStyle = .overFullScreen
|
||||
self.modalTransitionStyle = .crossDissolve
|
||||
|
|
|
@ -50,6 +50,10 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
|
|||
private lazy var imagePickerHandler: ImagePickerHandler = ImagePickerHandler(viewModel: self)
|
||||
fileprivate var oldDisplayName: String
|
||||
private var editedDisplayName: String?
|
||||
private var editProfilePictureModal: ConfirmationModal?
|
||||
private var editProfilePictureModalInfo: ConfirmationModal.Info?
|
||||
private var editedProfilePicture: UIImage?
|
||||
private var editedProfilePictureFileName: String?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
|
@ -102,11 +106,13 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
|
|||
}
|
||||
|
||||
override var rightNavItems: AnyPublisher<[NavItem]?, Never> {
|
||||
navState
|
||||
.map { [weak self] navState -> [NavItem] in
|
||||
switch navState {
|
||||
case .standard:
|
||||
return [
|
||||
let userSessionId: String = self.userSessionId
|
||||
|
||||
return navState
|
||||
.map { [weak self] navState -> [NavItem] in
|
||||
switch navState {
|
||||
case .standard:
|
||||
return [
|
||||
NavItem(
|
||||
id: .qrCode,
|
||||
image: UIImage(named: "QRCode")?
|
||||
|
@ -117,10 +123,10 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
|
|||
self?.transitionToScreen(QRCodeVC())
|
||||
}
|
||||
)
|
||||
]
|
||||
]
|
||||
|
||||
case .editing:
|
||||
return [
|
||||
case .editing:
|
||||
return [
|
||||
NavItem(
|
||||
id: .done,
|
||||
systemItem: .done,
|
||||
|
@ -161,7 +167,7 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
|
|||
self?.updateProfile(
|
||||
name: updatedNickname,
|
||||
profilePicture: nil,
|
||||
profilePictureFilePath: nil,
|
||||
profilePictureFilePath: ProfileManager.profileAvatarFilepath(id: userSessionId),
|
||||
isUpdatingDisplayName: true,
|
||||
isUpdatingProfilePicture: false
|
||||
)
|
||||
|
@ -389,23 +395,90 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
|
|||
}
|
||||
|
||||
private func updateProfilePicture() {
|
||||
let actionSheet: UIAlertController = UIAlertController(
|
||||
title: "Update Profile Picture",
|
||||
message: nil,
|
||||
preferredStyle: .actionSheet
|
||||
)
|
||||
let action = UIAlertAction(
|
||||
title: "MEDIA_FROM_LIBRARY_BUTTON".localized(),
|
||||
style: .default,
|
||||
handler: { [weak self] _ in
|
||||
self?.showPhotoLibraryForAvatar()
|
||||
let existingDisplayName: String = self.oldDisplayName
|
||||
let existingImage: UIImage? = ProfileManager
|
||||
.profileAvatar(id: self.userSessionId)
|
||||
.map { UIImage(data: $0) }
|
||||
let editProfilePictureModalInfo: ConfirmationModal.Info = ConfirmationModal.Info(
|
||||
title: "update_profile_modal_title".localized(),
|
||||
body: .image(
|
||||
placeholder: UIImage(named: "profile_placeholder"),
|
||||
value: existingImage,
|
||||
style: .circular,
|
||||
onClick: { [weak self] in self?.showPhotoLibraryForAvatar() }
|
||||
),
|
||||
confirmTitle: "update_profile_modal_upload".localized(),
|
||||
confirmEnabled: false,
|
||||
cancelTitle: "update_profile_modal_remove".localized(),
|
||||
cancelEnabled: (existingImage != nil),
|
||||
hasCloseButton: true,
|
||||
dismissOnConfirm: false,
|
||||
onConfirm: { [weak self] modal in
|
||||
self?.updateProfile(
|
||||
name: existingDisplayName,
|
||||
profilePicture: self?.editedProfilePicture,
|
||||
profilePictureFilePath: self?.editedProfilePictureFileName,
|
||||
isUpdatingDisplayName: false,
|
||||
isUpdatingProfilePicture: true,
|
||||
onComplete: { [weak modal] in modal?.close() }
|
||||
)
|
||||
},
|
||||
onCancel: { [weak self] modal in
|
||||
self?.updateProfile(
|
||||
name: existingDisplayName,
|
||||
profilePicture: nil,
|
||||
profilePictureFilePath: nil,
|
||||
isUpdatingDisplayName: false,
|
||||
isUpdatingProfilePicture: true,
|
||||
onComplete: { [weak modal] in modal?.close() }
|
||||
)
|
||||
},
|
||||
afterClosed: { [weak self] in
|
||||
self?.editedProfilePicture = nil
|
||||
self?.editedProfilePictureFileName = nil
|
||||
self?.editProfilePictureModal = nil
|
||||
self?.editProfilePictureModalInfo = nil
|
||||
}
|
||||
)
|
||||
action.accessibilityLabel = "Photo library"
|
||||
actionSheet.addAction(action)
|
||||
actionSheet.addAction(UIAlertAction(title: "cancel".localized(), style: .cancel, handler: nil))
|
||||
let modal: ConfirmationModal = ConfirmationModal(info: editProfilePictureModalInfo)
|
||||
|
||||
self.editProfilePictureModalInfo = editProfilePictureModalInfo
|
||||
self.editProfilePictureModal = modal
|
||||
self.transitionToScreen(modal, transitionType: .present)
|
||||
}
|
||||
|
||||
fileprivate func updatedProfilePictureSelected(image: UIImage?, filePath: String?) {
|
||||
guard let info: ConfirmationModal.Info = self.editProfilePictureModalInfo else { return }
|
||||
|
||||
self.transitionToScreen(actionSheet, transitionType: .present)
|
||||
self.editedProfilePicture = image
|
||||
self.editedProfilePictureFileName = filePath
|
||||
|
||||
if let image: UIImage = image {
|
||||
self.editProfilePictureModal?.updateContent(
|
||||
with: info.with(
|
||||
body: .image(
|
||||
placeholder: UIImage(named: "profile_placeholder"),
|
||||
value: image,
|
||||
style: .circular,
|
||||
onClick: { [weak self] in self?.showPhotoLibraryForAvatar() }
|
||||
),
|
||||
confirmEnabled: true
|
||||
)
|
||||
)
|
||||
}
|
||||
else if let filePath: String = filePath {
|
||||
self.editProfilePictureModal?.updateContent(
|
||||
with: info.with(
|
||||
body: .image(
|
||||
placeholder: UIImage(named: "profile_placeholder"),
|
||||
value: UIImage(contentsOfFile: filePath),
|
||||
style: .circular,
|
||||
onClick: { [weak self] in self?.showPhotoLibraryForAvatar() }
|
||||
),
|
||||
confirmEnabled: true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func showPhotoLibraryForAvatar() {
|
||||
|
@ -421,24 +494,20 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate func updateProfile(
|
||||
private func updateProfile(
|
||||
name: String,
|
||||
profilePicture: UIImage?,
|
||||
profilePictureFilePath: String?,
|
||||
isUpdatingDisplayName: Bool,
|
||||
isUpdatingProfilePicture: Bool
|
||||
isUpdatingProfilePicture: Bool,
|
||||
onComplete: (() -> ())? = nil
|
||||
) {
|
||||
let imageFilePath: String? = (
|
||||
profilePictureFilePath ??
|
||||
ProfileManager.profileAvatarFilepath(id: self.userSessionId)
|
||||
)
|
||||
|
||||
let viewController = ModalActivityIndicatorViewController(canCancel: false) { [weak self] modalActivityIndicator in
|
||||
ProfileManager.updateLocal(
|
||||
queue: DispatchQueue.global(qos: .default),
|
||||
profileName: name,
|
||||
image: profilePicture,
|
||||
imageFilePath: imageFilePath,
|
||||
imageFilePath: profilePictureFilePath,
|
||||
success: { db, updatedProfile in
|
||||
if isUpdatingDisplayName {
|
||||
UserDefaults.standard[.lastDisplayNameUpdate] = Date()
|
||||
|
@ -453,7 +522,9 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
|
|||
// Wait for the database transaction to complete before updating the UI
|
||||
db.afterNextTransaction { _ in
|
||||
DispatchQueue.main.async {
|
||||
modalActivityIndicator.dismiss(completion: {})
|
||||
modalActivityIndicator.dismiss(completion: {
|
||||
onComplete?()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -469,12 +540,13 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
|
|||
"Maximum File Size Exceeded" :
|
||||
"Couldn't Update Profile"
|
||||
),
|
||||
explanation: (isMaxFileSizeExceeded ?
|
||||
body: .text(isMaxFileSizeExceeded ?
|
||||
"Please select a smaller photo and try again" :
|
||||
"Please check your internet connection and try again"
|
||||
),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
cancelStyle: .alert_text,
|
||||
dismissType: .single
|
||||
)
|
||||
),
|
||||
transitionType: .present
|
||||
|
@ -497,8 +569,6 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
|
|||
DispatchQueue.main.async {
|
||||
button.isUserInteractionEnabled = false
|
||||
|
||||
|
||||
|
||||
UIView.transition(
|
||||
with: button,
|
||||
duration: 0.25,
|
||||
|
@ -560,7 +630,6 @@ class ImagePickerHandler: NSObject, UIImagePickerControllerDelegate & UINavigati
|
|||
picker.presentingViewController?.dismiss(animated: true)
|
||||
return
|
||||
}
|
||||
let name: String = self.viewModel.oldDisplayName
|
||||
|
||||
picker.presentingViewController?.dismiss(animated: true) { [weak self] in
|
||||
// Check if the user selected an animated image (if so then don't crop, just
|
||||
|
@ -574,12 +643,9 @@ class ImagePickerHandler: NSObject, UIImagePickerControllerDelegate & UINavigati
|
|||
let viewController: CropScaleImageViewController = CropScaleImageViewController(
|
||||
srcImage: rawAvatar,
|
||||
successCompletion: { resultImage in
|
||||
self?.viewModel.updateProfile(
|
||||
name: name,
|
||||
profilePicture: resultImage,
|
||||
profilePictureFilePath: nil,
|
||||
isUpdatingDisplayName: false,
|
||||
isUpdatingProfilePicture: true
|
||||
self?.viewModel.updatedProfilePictureSelected(
|
||||
image: resultImage,
|
||||
filePath: nil
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -587,12 +653,9 @@ class ImagePickerHandler: NSObject, UIImagePickerControllerDelegate & UINavigati
|
|||
return
|
||||
}
|
||||
|
||||
self?.viewModel.updateProfile(
|
||||
name: name,
|
||||
profilePicture: nil,
|
||||
profilePictureFilePath: imageUrl.path,
|
||||
isUpdatingDisplayName: false,
|
||||
isUpdatingProfilePicture: true
|
||||
self?.viewModel.updatedProfilePictureSelected(
|
||||
image: nil,
|
||||
filePath: imageUrl.path
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
import SignalUtilitiesKit
|
||||
|
@ -271,7 +272,7 @@ class ScreenLockUI {
|
|||
targetView: screenBlockingWindow.rootViewController?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "SCREEN_LOCK_UNLOCK_FAILED".localized(),
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text,
|
||||
afterClosed: { [weak self] in self?.ensureUI() } // After the alert, update the UI
|
||||
|
|
|
@ -339,13 +339,15 @@ class SessionTableViewController<NavItemId: Equatable, Section: SessionTableSect
|
|||
self?.navigationController?.pushViewController(viewController, animated: true)
|
||||
|
||||
case .present:
|
||||
let presenter: UIViewController? = (self?.presentedViewController ?? self)
|
||||
|
||||
if UIDevice.current.isIPad {
|
||||
viewController.popoverPresentationController?.permittedArrowDirections = []
|
||||
viewController.popoverPresentationController?.sourceView = self?.view
|
||||
viewController.popoverPresentationController?.sourceRect = (self?.view.bounds ?? UIScreen.main.bounds)
|
||||
viewController.popoverPresentationController?.sourceView = presenter?.view
|
||||
viewController.popoverPresentationController?.sourceRect = (presenter?.view.bounds ?? UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
self?.present(viewController, animated: true)
|
||||
presenter?.present(viewController, animated: true)
|
||||
}
|
||||
}
|
||||
.store(in: &disposables)
|
||||
|
@ -523,7 +525,7 @@ class SessionTableViewController<NavItemId: Equatable, Section: SessionTableSect
|
|||
|
||||
guard
|
||||
let confirmationInfo: ConfirmationModal.Info = info.confirmationInfo,
|
||||
confirmationInfo.stateToShow.shouldShow(for: info.currentBoolValue)
|
||||
confirmationInfo.showCondition.shouldShow(for: info.currentBoolValue)
|
||||
else {
|
||||
performAction()
|
||||
return
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
import UIKit
|
||||
import Photos
|
||||
import PhotosUI
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
import SessionMessagingKit
|
||||
|
||||
public enum Permissions {
|
||||
@discardableResult public static func requestCameraPermissionIfNeeded(
|
||||
|
@ -21,9 +23,11 @@ public enum Permissions {
|
|||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "Session",
|
||||
explanation: String(
|
||||
format: "modal_permission_explanation".localized(),
|
||||
"modal_permission_camera".localized()
|
||||
body: .text(
|
||||
String(
|
||||
format: "modal_permission_explanation".localized(),
|
||||
"modal_permission_camera".localized()
|
||||
)
|
||||
),
|
||||
confirmTitle: "modal_permission_settings_title".localized(),
|
||||
dismissOnConfirm: false
|
||||
|
@ -59,9 +63,11 @@ public enum Permissions {
|
|||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "Session",
|
||||
explanation: String(
|
||||
format: "modal_permission_explanation".localized(),
|
||||
"modal_permission_microphone".localized()
|
||||
body: .text(
|
||||
String(
|
||||
format: "modal_permission_explanation".localized(),
|
||||
"modal_permission_microphone".localized()
|
||||
)
|
||||
),
|
||||
confirmTitle: "modal_permission_settings_title".localized(),
|
||||
dismissOnConfirm: false,
|
||||
|
@ -128,9 +134,11 @@ public enum Permissions {
|
|||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "Session",
|
||||
explanation: String(
|
||||
format: "modal_permission_explanation".localized(),
|
||||
"modal_permission_library".localized()
|
||||
body: .text(
|
||||
String(
|
||||
format: "modal_permission_explanation".localized(),
|
||||
"modal_permission_library".localized()
|
||||
)
|
||||
),
|
||||
confirmTitle: "modal_permission_settings_title".localized(),
|
||||
dismissOnConfirm: false
|
||||
|
|
|
@ -163,7 +163,7 @@ final class SAEScreenLockViewController: ScreenLockViewController {
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "SCREEN_LOCK_UNLOCK_FAILED".localized(),
|
||||
explanation: message,
|
||||
body: .text(message),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text,
|
||||
afterClosed: { [weak self] in self?.ensureUI() } // After the alert, update the UI
|
||||
|
|
|
@ -226,7 +226,7 @@ final class ShareVC: UINavigationController, ShareViewDelegate {
|
|||
targetView: self.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: "Session",
|
||||
explanation: error.localizedDescription,
|
||||
body: .text(error.localizedDescription),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text,
|
||||
afterClosed: { [weak self] in self?.extensionContext?.cancelRequest(withError: error) }
|
||||
|
|
|
@ -1,135 +1,16 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
// FIXME: Refactor as part of the Groups Rebuild
|
||||
public class ConfirmationModal: Modal {
|
||||
public struct Info: Equatable, Hashable {
|
||||
public enum State {
|
||||
case whenEnabled
|
||||
case whenDisabled
|
||||
case always
|
||||
|
||||
public func shouldShow(for value: Bool) -> Bool {
|
||||
switch self {
|
||||
case .whenEnabled: return (value == true)
|
||||
case .whenDisabled: return (value == false)
|
||||
case .always: return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let title: String
|
||||
let explanation: String?
|
||||
let attributedExplanation: NSAttributedString?
|
||||
let accessibilityLabel: String?
|
||||
let accessibilityIdentifier: String?
|
||||
public let stateToShow: State
|
||||
let confirmTitle: String?
|
||||
let confirmAccessibilityLabel: String?
|
||||
let confirmStyle: ThemeValue
|
||||
let cancelTitle: String
|
||||
let cancelAccessibilityLabel: String?
|
||||
let cancelStyle: ThemeValue
|
||||
let dismissOnConfirm: Bool
|
||||
let onConfirm: ((UIViewController) -> ())?
|
||||
let afterClosed: (() -> ())?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
public init(
|
||||
title: String,
|
||||
explanation: String? = nil,
|
||||
attributedExplanation: NSAttributedString? = nil,
|
||||
accessibilityLabel: String? = nil,
|
||||
accessibilityId: String? = nil,
|
||||
stateToShow: State = .always,
|
||||
confirmTitle: String? = nil,
|
||||
confirmAccessibilityLabel: String? = nil,
|
||||
confirmStyle: ThemeValue = .alert_text,
|
||||
cancelTitle: String = "TXT_CANCEL_TITLE".localized(),
|
||||
cancelAccessibilityLabel: String? = nil,
|
||||
cancelStyle: ThemeValue = .danger,
|
||||
dismissOnConfirm: Bool = true,
|
||||
onConfirm: ((UIViewController) -> ())? = nil,
|
||||
afterClosed: (() -> ())? = nil
|
||||
) {
|
||||
self.title = title
|
||||
self.explanation = explanation
|
||||
self.attributedExplanation = attributedExplanation
|
||||
self.accessibilityLabel = accessibilityLabel
|
||||
self.accessibilityIdentifier = accessibilityId
|
||||
self.stateToShow = stateToShow
|
||||
self.confirmTitle = confirmTitle
|
||||
self.confirmAccessibilityLabel = confirmAccessibilityLabel
|
||||
self.confirmStyle = confirmStyle
|
||||
self.cancelTitle = cancelTitle
|
||||
self.cancelAccessibilityLabel = cancelAccessibilityLabel
|
||||
self.cancelStyle = cancelStyle
|
||||
self.dismissOnConfirm = dismissOnConfirm
|
||||
self.onConfirm = onConfirm
|
||||
self.afterClosed = afterClosed
|
||||
}
|
||||
|
||||
// MARK: - Mutation
|
||||
|
||||
public func with(
|
||||
onConfirm: ((UIViewController) -> ())? = nil,
|
||||
afterClosed: (() -> ())? = nil
|
||||
) -> Info {
|
||||
return Info(
|
||||
title: self.title,
|
||||
explanation: self.explanation,
|
||||
attributedExplanation: self.attributedExplanation,
|
||||
accessibilityLabel: self.accessibilityLabel,
|
||||
stateToShow: self.stateToShow,
|
||||
confirmTitle: self.confirmTitle,
|
||||
confirmAccessibilityLabel: self.confirmAccessibilityLabel,
|
||||
confirmStyle: self.confirmStyle,
|
||||
cancelTitle: self.cancelTitle,
|
||||
cancelAccessibilityLabel: self.cancelAccessibilityLabel,
|
||||
cancelStyle: self.cancelStyle,
|
||||
dismissOnConfirm: self.dismissOnConfirm,
|
||||
onConfirm: (onConfirm ?? self.onConfirm),
|
||||
afterClosed: (afterClosed ?? self.afterClosed)
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Confirmance
|
||||
|
||||
public static func == (lhs: ConfirmationModal.Info, rhs: ConfirmationModal.Info) -> Bool {
|
||||
return (
|
||||
lhs.title == rhs.title &&
|
||||
lhs.explanation == rhs.explanation &&
|
||||
lhs.attributedExplanation == rhs.attributedExplanation &&
|
||||
lhs.accessibilityLabel == rhs.accessibilityLabel &&
|
||||
lhs.stateToShow == rhs.stateToShow &&
|
||||
lhs.confirmTitle == rhs.confirmTitle &&
|
||||
lhs.confirmAccessibilityLabel == rhs.confirmAccessibilityLabel &&
|
||||
lhs.confirmStyle == rhs.confirmStyle &&
|
||||
lhs.cancelTitle == rhs.cancelTitle &&
|
||||
lhs.cancelAccessibilityLabel == rhs.cancelAccessibilityLabel &&
|
||||
lhs.cancelStyle == rhs.cancelStyle &&
|
||||
lhs.dismissOnConfirm == rhs.dismissOnConfirm
|
||||
)
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
title.hash(into: &hasher)
|
||||
explanation.hash(into: &hasher)
|
||||
attributedExplanation.hash(into: &hasher)
|
||||
accessibilityLabel.hash(into: &hasher)
|
||||
stateToShow.hash(into: &hasher)
|
||||
confirmTitle.hash(into: &hasher)
|
||||
confirmAccessibilityLabel.hash(into: &hasher)
|
||||
confirmStyle.hash(into: &hasher)
|
||||
cancelTitle.hash(into: &hasher)
|
||||
cancelAccessibilityLabel.hash(into: &hasher)
|
||||
cancelStyle.hash(into: &hasher)
|
||||
dismissOnConfirm.hash(into: &hasher)
|
||||
}
|
||||
}
|
||||
private static let imageSize: CGFloat = 80
|
||||
private static let closeSize: CGFloat = 24
|
||||
|
||||
private let internalOnConfirm: (UIViewController) -> ()
|
||||
private var internalOnConfirm: ((ConfirmationModal) -> ())? = nil
|
||||
private var internalOnCancel: ((ConfirmationModal) -> ())? = nil
|
||||
private var internalOnBodyTap: (() -> ())? = nil
|
||||
|
||||
// MARK: - Components
|
||||
|
||||
|
@ -151,6 +32,24 @@ public class ConfirmationModal: Modal {
|
|||
result.textAlignment = .center
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
result.numberOfLines = 0
|
||||
result.isHidden = true
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var imageViewContainer: UIView = {
|
||||
let result: UIView = UIView()
|
||||
result.isHidden = true
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var imageView: UIImageView = {
|
||||
let result: UIImageView = UIImageView()
|
||||
result.clipsToBounds = true
|
||||
result.contentMode = .scaleAspectFill
|
||||
result.set(.width, to: ConfirmationModal.imageSize)
|
||||
result.set(.height, to: ConfirmationModal.imageSize)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -174,7 +73,7 @@ public class ConfirmationModal: Modal {
|
|||
}()
|
||||
|
||||
private lazy var contentStackView: UIStackView = {
|
||||
let result = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel ])
|
||||
let result = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, imageViewContainer ])
|
||||
result.axis = .vertical
|
||||
result.spacing = Values.smallSpacing
|
||||
result.isLayoutMarginsRelativeArrangement = true
|
||||
|
@ -185,13 +84,41 @@ public class ConfirmationModal: Modal {
|
|||
right: Values.largeSpacing
|
||||
)
|
||||
|
||||
let gestureRecogniser: UITapGestureRecognizer = UITapGestureRecognizer(
|
||||
target: self,
|
||||
action: #selector(bodyTapped)
|
||||
)
|
||||
result.addGestureRecognizer(gestureRecogniser)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var mainStackView: UIStackView = {
|
||||
let result = UIStackView(arrangedSubviews: [ contentStackView, buttonStackView ])
|
||||
result.axis = .vertical
|
||||
result.spacing = Values.largeSpacing - Values.smallFontSize / 2
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var closeButton: UIButton = {
|
||||
let result: UIButton = UIButton()
|
||||
result.setImage(
|
||||
UIImage(named: "X")?
|
||||
.withRenderingMode(.alwaysTemplate),
|
||||
for: .normal
|
||||
)
|
||||
result.imageView?.contentMode = .scaleAspectFit
|
||||
result.themeTintColor = .textPrimary
|
||||
result.contentEdgeInsets = UIEdgeInsets(
|
||||
top: 6,
|
||||
left: 6,
|
||||
bottom: 6,
|
||||
right: 6
|
||||
)
|
||||
result.set(.width, to: ConfirmationModal.closeSize)
|
||||
result.set(.height, to: ConfirmationModal.closeSize)
|
||||
result.addTarget(self, action: #selector(close), for: .touchUpInside)
|
||||
result.isHidden = true
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -199,50 +126,11 @@ public class ConfirmationModal: Modal {
|
|||
// MARK: - Lifecycle
|
||||
|
||||
public init(targetView: UIView? = nil, info: Info) {
|
||||
self.internalOnConfirm = { viewController in
|
||||
if info.dismissOnConfirm {
|
||||
viewController.dismiss(animated: true)
|
||||
}
|
||||
|
||||
info.onConfirm?(viewController)
|
||||
}
|
||||
|
||||
super.init(targetView: targetView, afterClosed: info.afterClosed)
|
||||
super.init(targetView: targetView, dismissType: info.dismissType, afterClosed: info.afterClosed)
|
||||
|
||||
self.modalPresentationStyle = .overFullScreen
|
||||
self.modalTransitionStyle = .crossDissolve
|
||||
|
||||
// Set the content based on the provided info
|
||||
titleLabel.text = info.title
|
||||
|
||||
// Note: We should only set the appropriate explanation/attributedExplanation value (as
|
||||
// setting both when one is null can result in the other being removed)
|
||||
if let explanation: String = info.explanation {
|
||||
explanationLabel.text = explanation
|
||||
}
|
||||
|
||||
if let attributedExplanation: NSAttributedString = info.attributedExplanation {
|
||||
explanationLabel.attributedText = attributedExplanation
|
||||
}
|
||||
|
||||
explanationLabel.isHidden = (
|
||||
info.explanation == nil &&
|
||||
info.attributedExplanation == nil
|
||||
)
|
||||
confirmButton.accessibilityLabel = info.confirmAccessibilityLabel
|
||||
confirmButton.accessibilityIdentifier = info.confirmAccessibilityLabel
|
||||
confirmButton.isAccessibilityElement = true
|
||||
confirmButton.setTitle(info.confirmTitle, for: .normal)
|
||||
confirmButton.setThemeTitleColor(info.confirmStyle, for: .normal)
|
||||
confirmButton.isHidden = (info.confirmTitle == nil)
|
||||
cancelButton.accessibilityLabel = info.cancelAccessibilityLabel
|
||||
cancelButton.accessibilityIdentifier = info.cancelAccessibilityLabel
|
||||
cancelButton.isAccessibilityElement = true
|
||||
cancelButton.setTitle(info.cancelTitle, for: .normal)
|
||||
cancelButton.setThemeTitleColor(info.cancelStyle, for: .normal)
|
||||
|
||||
self.contentView.accessibilityLabel = info.accessibilityLabel
|
||||
self.contentView.accessibilityIdentifier = info.accessibilityIdentifier
|
||||
self.updateContent(with: info)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
@ -251,13 +139,321 @@ public class ConfirmationModal: Modal {
|
|||
|
||||
public override func populateContentView() {
|
||||
contentView.addSubview(mainStackView)
|
||||
contentView.addSubview(closeButton)
|
||||
|
||||
imageViewContainer.addSubview(imageView)
|
||||
imageView.center(.horizontal, in: imageViewContainer)
|
||||
imageView.pin(.top, to: .top, of: imageViewContainer, withInset: 15)
|
||||
imageView.pin(.bottom, to: .bottom, of: imageViewContainer, withInset: -15)
|
||||
|
||||
mainStackView.pin(to: contentView)
|
||||
closeButton.pin(.top, to: .top, of: contentView, withInset: 8)
|
||||
closeButton.pin(.right, to: .right, of: contentView, withInset: -8)
|
||||
}
|
||||
|
||||
// MARK: - Content
|
||||
|
||||
public func updateContent(with info: Info) {
|
||||
internalOnBodyTap = nil
|
||||
internalOnConfirm = { modal in
|
||||
if info.dismissOnConfirm {
|
||||
modal.close()
|
||||
}
|
||||
|
||||
info.onConfirm?(modal)
|
||||
}
|
||||
internalOnCancel = { modal in
|
||||
guard info.onCancel != nil else { return modal.close() }
|
||||
|
||||
info.onCancel?(modal)
|
||||
}
|
||||
|
||||
// Set the content based on the provided info
|
||||
titleLabel.text = info.title
|
||||
|
||||
switch info.body {
|
||||
case .none:
|
||||
mainStackView.spacing = Values.smallSpacing
|
||||
|
||||
case .text(let text):
|
||||
mainStackView.spacing = Values.smallSpacing
|
||||
explanationLabel.text = text
|
||||
explanationLabel.isHidden = false
|
||||
|
||||
case .attributedText(let attributedText):
|
||||
mainStackView.spacing = Values.smallSpacing
|
||||
explanationLabel.attributedText = attributedText
|
||||
explanationLabel.isHidden = false
|
||||
|
||||
case .image(let placeholder, let value, let style, let onClick):
|
||||
mainStackView.spacing = 0
|
||||
imageView.image = (value ?? placeholder)
|
||||
imageView.layer.cornerRadius = (style == .circular ?
|
||||
(ConfirmationModal.imageSize / 2) :
|
||||
0
|
||||
)
|
||||
imageViewContainer.isHidden = false
|
||||
internalOnBodyTap = onClick
|
||||
}
|
||||
|
||||
confirmButton.accessibilityLabel = info.confirmAccessibilityLabel
|
||||
confirmButton.accessibilityIdentifier = info.confirmAccessibilityLabel
|
||||
confirmButton.isAccessibilityElement = true
|
||||
confirmButton.setTitle(info.confirmTitle, for: .normal)
|
||||
confirmButton.setThemeTitleColor(info.confirmStyle, for: .normal)
|
||||
confirmButton.setThemeTitleColor(.disabled, for: .disabled)
|
||||
confirmButton.isHidden = (info.confirmTitle == nil)
|
||||
confirmButton.isEnabled = info.confirmEnabled
|
||||
cancelButton.accessibilityLabel = info.cancelAccessibilityLabel
|
||||
cancelButton.accessibilityIdentifier = info.cancelAccessibilityLabel
|
||||
cancelButton.isAccessibilityElement = true
|
||||
cancelButton.setTitle(info.cancelTitle, for: .normal)
|
||||
cancelButton.setThemeTitleColor(info.cancelStyle, for: .normal)
|
||||
cancelButton.setThemeTitleColor(.disabled, for: .disabled)
|
||||
cancelButton.isEnabled = info.cancelEnabled
|
||||
closeButton.isHidden = !info.hasCloseButton
|
||||
|
||||
contentView.accessibilityLabel = info.accessibilityLabel
|
||||
contentView.accessibilityIdentifier = info.accessibilityIdentifier
|
||||
}
|
||||
|
||||
// MARK: - Interaction
|
||||
|
||||
@objc private func bodyTapped() {
|
||||
internalOnBodyTap?()
|
||||
}
|
||||
|
||||
@objc private func confirmationPressed() {
|
||||
internalOnConfirm(self)
|
||||
internalOnConfirm?(self)
|
||||
}
|
||||
|
||||
override public func cancel() {
|
||||
internalOnCancel?(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Types
|
||||
|
||||
public extension ConfirmationModal {
|
||||
struct Info: Equatable, Hashable {
|
||||
let title: String
|
||||
let body: Body
|
||||
let accessibilityLabel: String?
|
||||
let accessibilityIdentifier: String?
|
||||
public let showCondition: ShowCondition
|
||||
let confirmTitle: String?
|
||||
let confirmAccessibilityLabel: String?
|
||||
let confirmStyle: ThemeValue
|
||||
let confirmEnabled: Bool
|
||||
let cancelTitle: String
|
||||
let cancelAccessibilityLabel: String?
|
||||
let cancelStyle: ThemeValue
|
||||
let cancelEnabled: Bool
|
||||
let hasCloseButton: Bool
|
||||
let dismissOnConfirm: Bool
|
||||
let dismissType: Modal.DismissType
|
||||
let onConfirm: ((ConfirmationModal) -> ())?
|
||||
let onCancel: ((ConfirmationModal) -> ())?
|
||||
let afterClosed: (() -> ())?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
public init(
|
||||
title: String,
|
||||
body: Body = .none,
|
||||
accessibilityLabel: String? = nil,
|
||||
accessibilityId: String? = nil,
|
||||
showCondition: ShowCondition = .none,
|
||||
confirmTitle: String? = nil,
|
||||
confirmAccessibilityLabel: String? = nil,
|
||||
confirmStyle: ThemeValue = .alert_text,
|
||||
confirmEnabled: Bool = true,
|
||||
cancelTitle: String = "TXT_CANCEL_TITLE".localized(),
|
||||
cancelAccessibilityLabel: String? = nil,
|
||||
cancelStyle: ThemeValue = .danger,
|
||||
cancelEnabled: Bool = true,
|
||||
hasCloseButton: Bool = false,
|
||||
dismissOnConfirm: Bool = true,
|
||||
dismissType: Modal.DismissType = .recursive,
|
||||
onConfirm: ((ConfirmationModal) -> ())? = nil,
|
||||
onCancel: ((ConfirmationModal) -> ())? = nil,
|
||||
afterClosed: (() -> ())? = nil
|
||||
) {
|
||||
self.title = title
|
||||
self.body = body
|
||||
self.accessibilityLabel = accessibilityLabel
|
||||
self.accessibilityIdentifier = accessibilityId
|
||||
self.showCondition = showCondition
|
||||
self.confirmTitle = confirmTitle
|
||||
self.confirmAccessibilityLabel = confirmAccessibilityLabel
|
||||
self.confirmStyle = confirmStyle
|
||||
self.confirmEnabled = confirmEnabled
|
||||
self.cancelTitle = cancelTitle
|
||||
self.cancelAccessibilityLabel = cancelAccessibilityLabel
|
||||
self.cancelStyle = cancelStyle
|
||||
self.cancelEnabled = cancelEnabled
|
||||
self.hasCloseButton = hasCloseButton
|
||||
self.dismissOnConfirm = dismissOnConfirm
|
||||
self.dismissType = dismissType
|
||||
self.onConfirm = onConfirm
|
||||
self.onCancel = onCancel
|
||||
self.afterClosed = afterClosed
|
||||
}
|
||||
|
||||
// MARK: - Mutation
|
||||
|
||||
public func with(
|
||||
body: Body? = nil,
|
||||
confirmEnabled: Bool? = nil,
|
||||
cancelEnabled: Bool? = nil,
|
||||
onConfirm: ((ConfirmationModal) -> ())? = nil,
|
||||
onCancel: ((ConfirmationModal) -> ())? = nil,
|
||||
afterClosed: (() -> ())? = nil
|
||||
) -> Info {
|
||||
return Info(
|
||||
title: self.title,
|
||||
body: (body ?? self.body),
|
||||
accessibilityLabel: self.accessibilityLabel,
|
||||
showCondition: self.showCondition,
|
||||
confirmTitle: self.confirmTitle,
|
||||
confirmAccessibilityLabel: self.confirmAccessibilityLabel,
|
||||
confirmStyle: self.confirmStyle,
|
||||
confirmEnabled: (confirmEnabled ?? self.confirmEnabled),
|
||||
cancelTitle: self.cancelTitle,
|
||||
cancelAccessibilityLabel: self.cancelAccessibilityLabel,
|
||||
cancelStyle: self.cancelStyle,
|
||||
cancelEnabled: (cancelEnabled ?? self.cancelEnabled),
|
||||
hasCloseButton: self.hasCloseButton,
|
||||
dismissOnConfirm: self.dismissOnConfirm,
|
||||
dismissType: self.dismissType,
|
||||
onConfirm: (onConfirm ?? self.onConfirm),
|
||||
onCancel: (onCancel ?? self.onCancel),
|
||||
afterClosed: (afterClosed ?? self.afterClosed)
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Confirmance
|
||||
|
||||
public static func == (lhs: ConfirmationModal.Info, rhs: ConfirmationModal.Info) -> Bool {
|
||||
return (
|
||||
lhs.title == rhs.title &&
|
||||
lhs.body == rhs.body &&
|
||||
lhs.accessibilityLabel == rhs.accessibilityLabel &&
|
||||
lhs.showCondition == rhs.showCondition &&
|
||||
lhs.confirmTitle == rhs.confirmTitle &&
|
||||
lhs.confirmAccessibilityLabel == rhs.confirmAccessibilityLabel &&
|
||||
lhs.confirmStyle == rhs.confirmStyle &&
|
||||
lhs.confirmEnabled == rhs.confirmEnabled &&
|
||||
lhs.cancelTitle == rhs.cancelTitle &&
|
||||
lhs.cancelAccessibilityLabel == rhs.cancelAccessibilityLabel &&
|
||||
lhs.cancelStyle == rhs.cancelStyle &&
|
||||
lhs.cancelEnabled == rhs.cancelEnabled &&
|
||||
lhs.hasCloseButton == rhs.hasCloseButton &&
|
||||
lhs.dismissOnConfirm == rhs.dismissOnConfirm &&
|
||||
lhs.dismissType == rhs.dismissType
|
||||
)
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
title.hash(into: &hasher)
|
||||
body.hash(into: &hasher)
|
||||
accessibilityLabel.hash(into: &hasher)
|
||||
showCondition.hash(into: &hasher)
|
||||
confirmTitle.hash(into: &hasher)
|
||||
confirmAccessibilityLabel.hash(into: &hasher)
|
||||
confirmStyle.hash(into: &hasher)
|
||||
confirmEnabled.hash(into: &hasher)
|
||||
cancelTitle.hash(into: &hasher)
|
||||
cancelAccessibilityLabel.hash(into: &hasher)
|
||||
cancelStyle.hash(into: &hasher)
|
||||
cancelEnabled.hash(into: &hasher)
|
||||
hasCloseButton.hash(into: &hasher)
|
||||
dismissOnConfirm.hash(into: &hasher)
|
||||
dismissType.hash(into: &hasher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension ConfirmationModal.Info {
|
||||
// MARK: - ShowCondition
|
||||
|
||||
enum ShowCondition {
|
||||
case none
|
||||
case enabled
|
||||
case disabled
|
||||
|
||||
public func shouldShow(for value: Bool) -> Bool {
|
||||
switch self {
|
||||
case .none: return true
|
||||
case .enabled: return (value == true)
|
||||
case .disabled: return (value == false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Body
|
||||
|
||||
enum Body: Equatable, Hashable {
|
||||
public enum ImageStyle: Equatable, Hashable {
|
||||
case inherit
|
||||
case circular
|
||||
}
|
||||
|
||||
case none
|
||||
case text(String)
|
||||
case attributedText(NSAttributedString)
|
||||
// FIXME: Implement these
|
||||
// case input(placeholder: String, value: String?)
|
||||
// case radio(explanation: NSAttributedString?, options: [(title: String, selected: Bool)])
|
||||
case image(
|
||||
placeholder: UIImage?,
|
||||
value: UIImage?,
|
||||
style: ImageStyle,
|
||||
onClick: (() -> ())
|
||||
)
|
||||
|
||||
public static func == (lhs: ConfirmationModal.Info.Body, rhs: ConfirmationModal.Info.Body) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.none, .none): return true
|
||||
case (.text(let lhsText), .text(let rhsText)): return (lhsText == rhsText)
|
||||
case (.attributedText(let lhsText), .attributedText(let rhsText)): return (lhsText == rhsText)
|
||||
|
||||
// FIXME: Implement these
|
||||
//case (.input(let lhsPlaceholder, let lhsValue), .input(let rhsPlaceholder, let rhsValue)):
|
||||
// return (
|
||||
// lhsPlaceholder == rhsPlaceholder &&
|
||||
// lhsValue == rhsValue &&
|
||||
// )
|
||||
|
||||
// FIXME: Implement these
|
||||
//case (.radio(let lhsExplanation, let lhsOptions), .radio(let rhsExplanation, let rhsOptions)):
|
||||
// return (
|
||||
// lhsExplanation == rhsExplanation &&
|
||||
// lhsOptions.map { "\($0.0)-\($0.1)" } == rhsValue.map { "\($0.0)-\($0.1)" }
|
||||
// )
|
||||
|
||||
case (.image(let lhsPlaceholder, let lhsValue, let lhsStyle, _), .image(let rhsPlaceholder, let rhsValue, let rhsStyle, _)):
|
||||
return (
|
||||
lhsPlaceholder == rhsPlaceholder &&
|
||||
lhsValue == rhsValue &&
|
||||
lhsStyle == rhsStyle
|
||||
)
|
||||
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
switch self {
|
||||
case .none: break
|
||||
case .text(let text): text.hash(into: &hasher)
|
||||
case .attributedText(let text): text.hash(into: &hasher)
|
||||
|
||||
case .image(let placeholder, let value, let style, _):
|
||||
placeholder.hash(into: &hasher)
|
||||
value.hash(into: &hasher)
|
||||
style.hash(into: &hasher)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,12 @@ import SessionUtilitiesKit
|
|||
open class Modal: UIViewController, UIGestureRecognizerDelegate {
|
||||
private static let cornerRadius: CGFloat = 11
|
||||
|
||||
public enum DismissType: Equatable, Hashable {
|
||||
case single
|
||||
case recursive
|
||||
}
|
||||
|
||||
private let dismissType: DismissType
|
||||
private let afterClosed: (() -> ())?
|
||||
|
||||
// MARK: - Components
|
||||
|
@ -47,14 +53,19 @@ open class Modal: UIViewController, UIGestureRecognizerDelegate {
|
|||
|
||||
public lazy var cancelButton: UIButton = {
|
||||
let result: UIButton = Modal.createButton(title: "cancel".localized(), titleColor: .textPrimary)
|
||||
result.addTarget(self, action: #selector(close), for: .touchUpInside)
|
||||
result.addTarget(self, action: #selector(cancel), for: .touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
public init(targetView: UIView? = nil, afterClosed: (() -> ())? = nil) {
|
||||
public init(
|
||||
targetView: UIView? = nil,
|
||||
dismissType: DismissType = .recursive,
|
||||
afterClosed: (() -> ())? = nil
|
||||
) {
|
||||
self.dismissType = dismissType
|
||||
self.afterClosed = afterClosed
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
@ -129,13 +140,22 @@ open class Modal: UIViewController, UIGestureRecognizerDelegate {
|
|||
|
||||
// MARK: - Interaction
|
||||
|
||||
@objc func close() {
|
||||
@objc public func cancel() {
|
||||
close()
|
||||
}
|
||||
|
||||
@objc public final func close() {
|
||||
// Recursively dismiss all modals (ie. find the first modal presented by a non-modal
|
||||
// and get that to dismiss it's presented view controller)
|
||||
var targetViewController: UIViewController? = self
|
||||
|
||||
while targetViewController?.presentingViewController is Modal {
|
||||
targetViewController = targetViewController?.presentingViewController
|
||||
switch dismissType {
|
||||
case .single: break
|
||||
|
||||
case .recursive:
|
||||
while targetViewController?.presentingViewController is Modal {
|
||||
targetViewController = targetViewController?.presentingViewController
|
||||
}
|
||||
}
|
||||
|
||||
targetViewController?.presentingViewController?.dismiss(animated: true) { [weak self] in
|
||||
|
|
|
@ -21,7 +21,7 @@ public final class Values : NSObject {
|
|||
@objc public static let smallButtonHeight = isIPhone5OrSmaller ? CGFloat(24) : CGFloat(28)
|
||||
@objc public static let mediumButtonHeight = isIPhone5OrSmaller ? CGFloat(30) : CGFloat(34)
|
||||
@objc public static let largeButtonHeight = isIPhone5OrSmaller ? CGFloat(40) : CGFloat(45)
|
||||
@objc public static let alertButtonHeight: CGFloat = 50
|
||||
@objc public static let alertButtonHeight: CGFloat = 51 // 19px tall font with 16px margins
|
||||
|
||||
@objc public static let accentLineThickness = CGFloat(4)
|
||||
|
||||
|
|
|
@ -653,7 +653,7 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate {
|
|||
targetView: CurrentAppContext().frontmostViewController()?.view,
|
||||
info: ConfirmationModal.Info(
|
||||
title: CommonStrings.errorAlertTitle,
|
||||
explanation: "INVALID_AUDIO_FILE_ALERT_ERROR_MESSAGE".localized(),
|
||||
body: .text("INVALID_AUDIO_FILE_ALERT_ERROR_MESSAGE".localized()),
|
||||
cancelTitle: "BUTTON_OK".localized(),
|
||||
cancelStyle: .alert_text
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue