123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- //
- // SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
- // SPDX-License-Identifier: GPL-3.0-or-later
- //
- import Foundation
- import UIKit
- enum RoomAvatarInfoSection: Int {
- case kRoomNameSection = 0
- case kRoomDescriptionSection
- }
- @objcMembers class RoomAvatarInfoTableViewController: UITableViewController,
- UINavigationControllerDelegate,
- UIImagePickerControllerDelegate,
- UITextFieldDelegate,
- AvatarEditViewDelegate,
- EmojiAvatarPickerViewControllerDelegate,
- RoomDescriptionTableViewCellDelegate,
- TOCropViewControllerDelegate {
- var room: NCRoom
- var imagePicker: UIImagePickerController?
- var headerView: AvatarEditView
- var rightBarButton = UIBarButtonItem()
- var modifyingView = UIActivityIndicatorView()
- var descriptionHeaderView = HeaderWithButton()
- var currentDescription = ""
- init(room: NCRoom) {
- self.room = room
- self.headerView = AvatarEditView()
- super.init(nibName: "RoomAvatarInfoTableViewController", bundle: nil)
- self.headerView.delegate = self
- self.headerView.scopeButton.isHidden = true
- self.headerView.nameLabel.isHidden = true
- self.descriptionHeaderView.label.text = NSLocalizedString("Description", comment: "").uppercased()
- self.descriptionHeaderView.button.setTitle(NSLocalizedString("Save", comment: "Save conversation description"), for: .normal)
- self.descriptionHeaderView.button.addTarget(self, action: #selector(setButtonPressed), for: .touchUpInside)
- self.descriptionHeaderView.button.isHidden = true
- self.currentDescription = self.room.roomDescription
- self.updateHeaderView()
- }
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- override func viewDidLoad() {
- super.viewDidLoad()
- self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: NCAppBranding.themeTextColor()]
- self.navigationController?.navigationBar.tintColor = NCAppBranding.themeTextColor()
- self.navigationController?.navigationBar.barTintColor = NCAppBranding.themeColor()
- self.navigationController?.navigationBar.isTranslucent = false
- self.navigationItem.title = NSLocalizedString("Conversation details", comment: "")
- let appearance = UINavigationBarAppearance()
- appearance.configureWithOpaqueBackground()
- appearance.titleTextAttributes = [.foregroundColor: NCAppBranding.themeTextColor()]
- appearance.backgroundColor = NCAppBranding.themeColor()
- self.navigationItem.standardAppearance = appearance
- self.navigationItem.compactAppearance = appearance
- self.navigationItem.scrollEdgeAppearance = appearance
- self.tableView.register(UINib(nibName: kTextInputTableViewCellNibName, bundle: nil), forCellReuseIdentifier: kTextInputCellIdentifier)
- self.tableView.register(UINib(nibName: RoomDescriptionTableViewCell.nibName, bundle: nil), forCellReuseIdentifier: RoomDescriptionTableViewCell.identifier)
- self.tableView.tableHeaderView = self.headerView
- self.modifyingView.color = NCAppBranding.themeTextColor()
- }
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
- }
- func updateHeaderView() {
- self.headerView.avatarImageView.setAvatar(for: self.room)
- self.headerView.editView.isHidden = !NCDatabaseManager.sharedInstance().serverHasTalkCapability(kCapabilityConversationAvatars, forAccountId: self.room.accountId)
- self.headerView.trashButton.isHidden = !self.room.isCustomAvatar
- // Need to have an explicit size here for the header view
- let size = self.headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
- self.headerView.frame = CGRect(origin: .zero, size: size)
- }
- func getAvatarInfoSections() -> [Int] {
- var sections = [Int]()
- // Room name section
- sections.append(RoomAvatarInfoSection.kRoomNameSection.rawValue)
- // Room description section
- if NCDatabaseManager.sharedInstance().serverHasTalkCapability(kCapabilityRoomDescription, forAccountId: self.room.accountId) {
- sections.append(RoomAvatarInfoSection.kRoomDescriptionSection.rawValue)
- }
- return sections
- }
- override func numberOfSections(in tableView: UITableView) -> Int {
- return self.getAvatarInfoSections().count
- }
- override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
- if section == RoomAvatarInfoSection.kRoomDescriptionSection.rawValue {
- return descriptionHeaderView
- }
- return nil
- }
- override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
- return 40
- }
- override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return 1
- }
- override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let textInputCell = tableView.dequeueReusableCell(withIdentifier: kTextInputCellIdentifier) as? TextInputTableViewCell ??
- TextInputTableViewCell(style: .default, reuseIdentifier: kTextInputCellIdentifier)
- if indexPath.section == RoomAvatarInfoSection.kRoomNameSection.rawValue {
- textInputCell.textField.delegate = self
- textInputCell.textField.text = self.room.displayName
- textInputCell.textField.autocapitalizationType = .sentences
- textInputCell.selectionStyle = .none
- return textInputCell
- } else if indexPath.section == RoomAvatarInfoSection.kRoomDescriptionSection.rawValue {
- let descriptionCell = tableView.dequeueReusableCell(withIdentifier: RoomDescriptionTableViewCell.identifier) as? RoomDescriptionTableViewCell ??
- RoomDescriptionTableViewCell(style: .default, reuseIdentifier: RoomDescriptionTableViewCell.identifier)
- descriptionCell.textView?.text = self.room.roomDescription
- descriptionCell.textView?.isEditable = true
- descriptionCell.delegate = self
- descriptionCell.characterLimit = 500
- descriptionCell.selectionStyle = .none
- return descriptionCell
- }
- return textInputCell
- }
- override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
- if section == RoomAvatarInfoSection.kRoomNameSection.rawValue {
- return NSLocalizedString("Name", comment: "")
- } else if section == RoomAvatarInfoSection.kRoomDescriptionSection.rawValue {
- return NSLocalizedString("Description", comment: "")
- }
- return nil
- }
- func updateRoomAndRemoveModifyingView() {
- NCRoomsManager.sharedInstance().updateRoom(self.room.token) { _, _ in
- guard let room = NCDatabaseManager.sharedInstance().room(withToken: self.room.token, forAccountId: self.room.accountId) else { return }
- self.room = room
- self.currentDescription = self.room.roomDescription
- self.updateHeaderView()
- self.tableView.reloadData()
- self.removeModifyingView()
- }
- }
- func setButtonPressed() {
- self.showModifyingView()
- self.descriptionHeaderView.button.isHidden = true
- let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
- NCAPIController.sharedInstance().setRoomDescription(currentDescription, forRoom: room.token, forAccount: activeAccount) { error in
- if error != nil {
- NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("An error occurred while setting description", comment: ""), withMessage: nil)
- }
- self.updateRoomAndRemoveModifyingView()
- }
- }
- func showModifyingView() {
- modifyingView.startAnimating()
- self.headerView.changeButtonState(to: false)
- self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: modifyingView)
- self.tableView.isUserInteractionEnabled = false
- }
- func removeModifyingView() {
- modifyingView.stopAnimating()
- self.headerView.changeButtonState(to: true)
- self.tableView.isUserInteractionEnabled = true
- }
- // MARK: - Present camera/photo library
- func checkAndPresentCamera() {
- // https://stackoverflow.com/a/20464727/2512312
- let mediaType: String = AVMediaType.video.rawValue
- let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType(mediaType))
- if authStatus == AVAuthorizationStatus.authorized {
- self.presentCamera()
- return
- } else if authStatus == AVAuthorizationStatus.notDetermined {
- AVCaptureDevice.requestAccess(for: AVMediaType(mediaType)) { granted in
- if granted {
- self.presentCamera()
- }
- }
- return
- }
- let alertTitle = NSLocalizedString("Could not access camera", comment: "")
- let alertMessage = NSLocalizedString("Camera access is not allowed. Check your settings.", comment: "")
- NCUserInterfaceController.sharedInstance().presentAlert(withTitle: alertTitle, withMessage: alertMessage)
- }
- func presentCamera() {
- DispatchQueue.main.async {
- self.imagePicker = UIImagePickerController()
- if let imagePicker = self.imagePicker {
- imagePicker.sourceType = .camera
- imagePicker.delegate = self
- self.present(imagePicker, animated: true)
- }
- }
- }
- func presentPhotoLibrary() {
- DispatchQueue.main.async {
- self.imagePicker = UIImagePickerController()
- if let imagePicker = self.imagePicker {
- imagePicker.sourceType = .photoLibrary
- imagePicker.delegate = self
- self.present(imagePicker, animated: true)
- }
- }
- }
- func presentEmojiAvatarPicker() {
- DispatchQueue.main.async {
- let emojiAvatarPickerVC = EmojiAvatarPickerViewController()
- emojiAvatarPickerVC.delegate = self
- let emojiAvatarPickerNC = UINavigationController(rootViewController: emojiAvatarPickerVC)
- self.present(emojiAvatarPickerNC, animated: true, completion: nil)
- }
- }
- // MARK: - UIImagePickerController Delegate
- func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
- let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String
- if mediaType == "public.image" {
- let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
- self.dismiss(animated: true) {
- if let image = image {
- let cropViewController = TOCropViewController(croppingStyle: TOCropViewCroppingStyle.circular, image: image)
- cropViewController.delegate = self
- self.present(cropViewController, animated: true)
- }
- }
- }
- }
- func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
- self.dismiss(animated: true, completion: nil)
- }
- // MARK: - RoomDescriptionTableViewCellDelegate
- func roomDescriptionCellTextViewDidChange(_ cell: RoomDescriptionTableViewCell) {
- DispatchQueue.main.async {
- self.tableView?.beginUpdates()
- self.tableView?.endUpdates()
- self.currentDescription = cell.textView?.text ?? ""
- self.descriptionHeaderView.button.isHidden = self.currentDescription == self.room.roomDescription
- }
- }
- func roomDescriptionCellDidExceedLimit(_ cell: RoomDescriptionTableViewCell) {
- NotificationPresenter.shared().present(
- text: NSLocalizedString("Description cannot be longer than 500 characters", comment: ""),
- dismissAfterDelay: 3.0,
- includedStyle: .warning)
- }
- // MARK: - TOCROPViewControllerDelegate
- func cropViewController(_ cropViewController: TOCropViewController, didCropTo image: UIImage, with cropRect: CGRect, angle: Int) {
- // Fixes weird iOS 13 bug: https://github.com/TimOliver/TOCropViewController/issues/365
- cropViewController.transitioningDelegate = nil
- cropViewController.dismiss(animated: true) {
- // Need to dismiss cropViewController first before showing the activityIndicator
- self.showModifyingView()
- NCAPIController.sharedInstance().setAvatarFor(self.room, with: image) { error in
- if error != nil {
- let errorDialog = UIAlertController(title: NSLocalizedString("An error occurred while setting the avatar", comment: ""), message: nil, preferredStyle: .alert)
- let okAction = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)
- errorDialog.addAction(okAction)
- self.present(errorDialog, animated: true, completion: nil)
- }
- self.updateRoomAndRemoveModifyingView()
- }
- }
- }
- func cropViewController(_ cropViewController: TOCropViewController, didFinishCancelled cancelled: Bool) {
- // Fixes weird iOS 13 bug: https://github.com/TimOliver/TOCropViewController/issues/365
- cropViewController.transitioningDelegate = nil
- cropViewController.dismiss(animated: true)
- }
- // MARK: - AvaterEditViewDelegate
- func avatarEditViewPresentCamera(_ controller: AvatarEditView?) {
- self.checkAndPresentCamera()
- }
- func avatarEditViewPresentPhotoLibrary(_ controller: AvatarEditView?) {
- self.presentPhotoLibrary()
- }
- func avatarEditViewPresentEmojiAvatarPicker(_ controller: AvatarEditView?) {
- self.presentEmojiAvatarPicker()
- }
- func avatarEditViewRemoveAvatar(_ controller: AvatarEditView?) {
- self.showModifyingView()
- NCAPIController.sharedInstance().removeAvatar(for: room) { error in
- if error != nil {
- NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("An error occurred while removing the avatar", comment: ""), withMessage: nil)
- }
- self.updateRoomAndRemoveModifyingView()
- }
- }
- // MARK: - EmojiAvatarPickerViewControllerDelegate
- func didSelectEmoji(emoji: NSString, color: NSString, image: UIImage) {
- self.showModifyingView()
- NCAPIController.sharedInstance().setEmojiAvatarFor(room, withEmoji: emoji as String, andColor: color as String) { error in
- if error != nil {
- let errorDialog = UIAlertController(title: NSLocalizedString("An error occurred while setting the avatar", comment: ""), message: nil, preferredStyle: .alert)
- let okAction = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)
- errorDialog.addAction(okAction)
- self.present(errorDialog, animated: true, completion: nil)
- }
- self.updateRoomAndRemoveModifyingView()
- }
- }
- // MARK: - UITextField delegate
- func textFieldShouldReturn(_ textField: UITextField) -> Bool {
- textField.resignFirstResponder()
- let newRoomValue = textField.text!.trimmingCharacters(in: CharacterSet.whitespaces)
- if newRoomValue == self.room.name {
- return true
- }
- if newRoomValue.isEmpty {
- let alertTitle = NSLocalizedString("Could not set conversation name", comment: "")
- let alertMessage = NSLocalizedString("Conversation name cannot be empty", comment: "")
- NCUserInterfaceController.sharedInstance().presentAlert(withTitle: alertTitle, withMessage: alertMessage)
- self.tableView.reloadData()
- return true
- }
- self.showModifyingView()
- let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
- NCAPIController.sharedInstance().renameRoom(self.room.token, forAccount: activeAccount, withName: newRoomValue) { error in
- if error != nil {
- let alertTitle = NSLocalizedString("Could not rename the conversation", comment: "")
- NCUserInterfaceController.sharedInstance().presentAlert(withTitle: alertTitle, withMessage: nil)
- }
- self.updateRoomAndRemoveModifyingView()
- }
- return true
- }
- }
|