diff --git a/deltachat-ios.xcodeproj/project.pbxproj b/deltachat-ios.xcodeproj/project.pbxproj index d5c4667e1..4dfbba265 100644 --- a/deltachat-ios.xcodeproj/project.pbxproj +++ b/deltachat-ios.xcodeproj/project.pbxproj @@ -209,6 +209,7 @@ B2C42570265C325C00B95377 /* MultilineLabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C4256F265C325C00B95377 /* MultilineLabelCell.swift */; }; B2D0E4952B93A4B200791949 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2D0E4942B93A4B200791949 /* NotificationService.swift */; }; B2D0E4992B93A4B200791949 /* DcNotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = B2D0E4922B93A4B200791949 /* DcNotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + B2D255972BEC2C9B00CCD7BC /* InstantOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2D255962BEC2C9B00CCD7BC /* InstantOnboardingViewController.swift */; }; B2D4B63B29C38D1900B47DA8 /* ChatsAndMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2D4B63A29C38D1900B47DA8 /* ChatsAndMediaViewController.swift */; }; B2F899E129F96A67003797D5 /* AllMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2F899E029F96A67003797D5 /* AllMediaViewController.swift */; }; D80F62792B59D1CC00877059 /* DefaultReactions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80F62782B59D1CC00877059 /* DefaultReactions.swift */; }; @@ -580,6 +581,7 @@ B2D0E4922B93A4B200791949 /* DcNotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = DcNotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; B2D0E4942B93A4B200791949 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; B2D0E4962B93A4B200791949 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B2D255962BEC2C9B00CCD7BC /* InstantOnboardingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstantOnboardingViewController.swift; sourceTree = ""; }; B2D4B63A29C38D1900B47DA8 /* ChatsAndMediaViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatsAndMediaViewController.swift; sourceTree = ""; }; B2D729E927C57B9000A4E0BE /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; B2D729EA27C57B9000A4E0BE /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; @@ -901,6 +903,7 @@ isa = PBXGroup; children = ( AE76E5ED242BF2EA003CF461 /* WelcomeViewController.swift */, + B2D255962BEC2C9B00CCD7BC /* InstantOnboardingViewController.swift */, AEE56D752253431E007DC082 /* AccountSetupController.swift */, 30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */, AE18F293228C602A0007B1BE /* SecuritySettingsController.swift */, @@ -1498,6 +1501,7 @@ 3059620E234614E700C80F33 /* DcContact+Extension.swift in Sources */, 30DED713292EE4280040835D /* LogViewController.swift in Sources */, AED423D7249F580700B6B2BB /* BlockedContactsViewController.swift in Sources */, + B2D255972BEC2C9B00CCD7BC /* InstantOnboardingViewController.swift in Sources */, 303492B32577E40700A523D0 /* DocumentPreview.swift in Sources */, 3008CB7424F9436C00E6A617 /* AudioPlayerView.swift in Sources */, 3080A01D277DDB8A00E74565 /* InputItem.swift in Sources */, diff --git a/deltachat-ios/Controller/AccountSetup/InstantOnboardingViewController.swift b/deltachat-ios/Controller/AccountSetup/InstantOnboardingViewController.swift new file mode 100644 index 000000000..b454f4a6b --- /dev/null +++ b/deltachat-ios/Controller/AccountSetup/InstantOnboardingViewController.swift @@ -0,0 +1,131 @@ +import UIKit +import DcCore + +class InstantOnboardingViewController: UITableViewController, MediaPickerDelegate { + + private struct SectionConfigs { + let headerTitle: String? + let footerTitle: String? + let cells: [UITableViewCell] + } + + private let dcContext: DcContext + private let dcAccounts: DcAccounts + + private lazy var mediaPicker: MediaPicker? = { + let mediaPicker = MediaPicker(dcContext: dcContext, navigationController: navigationController) + mediaPicker.delegate = self + return mediaPicker + }() + + private lazy var avatarSelectionCell: AvatarSelectionCell = { + return AvatarSelectionCell(image: dcContext.getSelfAvatarImage()) + }() + + private lazy var nameCell: TextFieldCell = { + let cell = TextFieldCell(description: String.localized("pref_your_name"), placeholder: String.localized("pref_your_name")) + cell.setText(text: dcContext.displayname) + cell.textFieldDelegate = self + cell.textField.returnKeyType = .default + return cell + }() + + private lazy var sections: [SectionConfigs] = { + let nameSection = SectionConfigs( + headerTitle: nil, + footerTitle: String.localized("set_name_and_avatar_explain"), + cells: [nameCell, avatarSelectionCell] + ) + return [nameSection] + }() + + init(dcAccounts: DcAccounts) { + self.dcAccounts = dcAccounts + self.dcContext = dcAccounts.getSelected() + super.init(style: .grouped) + hidesBottomBarWhenPushed = true + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + title = String.localized("pref_profile_info_headline") + avatarSelectionCell.onAvatarTapped = { [weak self] in + self?.onAvatarTapped() + } + tableView.rowHeight = UITableView.automaticDimension + } + + override func viewWillDisappear(_ animated: Bool) { + dcContext.displayname = nameCell.getText() + } + + // MARK: - Table view data source + override func numberOfSections(in tableView: UITableView) -> Int { + return sections.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return sections[section].cells.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + return sections[indexPath.section].cells[indexPath.row] + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return sections[section].headerTitle + } + + override func tableView(_: UITableView, titleForFooterInSection section: Int) -> String? { + return sections[section].footerTitle + } + + // MARK: - actions + private func galleryButtonPressed(_ action: UIAlertAction) { + mediaPicker?.showPhotoGallery() + } + + private func cameraButtonPressed(_ action: UIAlertAction) { + mediaPicker?.showCamera(allowCropping: true, supportedMediaTypes: .photo) + } + + private func deleteProfileIconPressed(_ action: UIAlertAction) { + dcContext.selfavatar = nil + updateAvatarCell() + } + + private func onAvatarTapped() { + let alert = UIAlertController(title: String.localized("pref_profile_photo"), message: nil, preferredStyle: .safeActionSheet) + alert.addAction(PhotoPickerAlertAction(title: String.localized("camera"), style: .default, handler: cameraButtonPressed(_:))) + alert.addAction(PhotoPickerAlertAction(title: String.localized("gallery"), style: .default, handler: galleryButtonPressed(_:))) + if dcContext.getSelfAvatarImage() != nil { + alert.addAction(UIAlertAction(title: String.localized("delete"), style: .destructive, handler: deleteProfileIconPressed(_:))) + } + alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil)) + + self.present(alert, animated: true, completion: nil) + } + + func onImageSelected(image: UIImage) { + AvatarHelper.saveSelfAvatarImage(dcContext: dcContext, image: image) + updateAvatarCell() + } + + private func updateAvatarCell() { + self.avatarSelectionCell.setAvatar(image: dcContext.getSelfAvatarImage()) + } + +} + + +extension InstantOnboardingViewController: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } +} diff --git a/deltachat-ios/Controller/AccountSetup/WelcomeViewController.swift b/deltachat-ios/Controller/AccountSetup/WelcomeViewController.swift index ac6219547..f794ef98f 100644 --- a/deltachat-ios/Controller/AccountSetup/WelcomeViewController.swift +++ b/deltachat-ios/Controller/AccountSetup/WelcomeViewController.swift @@ -20,6 +20,8 @@ class WelcomeViewController: UIViewController, ProgressAlertHandler { let view = WelcomeContentView() view.onSignUp = { [weak self] in guard let self else { return } + let controller = InstantOnboardingViewController(dcAccounts: dcAccounts) + navigationController?.pushViewController(controller, animated: true) } view.onLogIn = { [weak self] in guard let self else { return }