-
Notifications
You must be signed in to change notification settings - Fork 1
Feature/create chat room vc #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 29 commits
d200ce7
f151900
f17d77a
eaca76e
ae1492c
fc1e953
9cdb665
30e3bbf
a9015c4
6cd9b4a
53e9783
64d01bd
8cf5f94
74c38ab
7183396
68eec80
8fc7cf1
1a5c774
97e2440
b3b7879
a6f3baa
433dc02
daf5d66
9113277
64f6246
b356c21
6a23ac3
ed5a521
a52e499
d9a5050
8f53c57
8972caa
6023a3d
9c71c2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // | ||
| // CreateChatRoomViewBuilder.swift | ||
| // chat-iOS | ||
| // | ||
| // Created by jun on 2020/07/18. | ||
| // | ||
|
|
||
| import UIKit | ||
|
|
||
| struct CreateChatRoomViewBuilder { | ||
| static func create() -> UIViewController { | ||
| guard let createChatRoomViewController = CreateChatRoomViewController.loadFromStoryboard() as? CreateChatRoomViewController else { | ||
| fatalError("fatal: Failed to initialize the CreateChatRoomViewController") | ||
| } | ||
| let model = CreateChatRoomModel() | ||
| let presenter = CreateChatRoomViewPresenter(model: model) | ||
| createChatRoomViewController.inject(with: presenter) | ||
| return createChatRoomViewController | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,239 @@ | ||
| // | ||
| // File.swift | ||
| // chat-iOS | ||
| // | ||
| // Created by jun on 2020/07/18. | ||
| // | ||
|
|
||
| import UIKit | ||
|
|
||
| final class CreateChatRoomViewController: UIViewController { | ||
| private var presenter: CreateChatRoomViewPresenterProtocol! | ||
|
|
||
| @IBOutlet weak var userNameSearchBar: UISearchBar! | ||
| @IBOutlet weak var serchUserTableview: UITableView! | ||
| @IBOutlet weak var selectedUserCollectionView: UICollectionView! | ||
|
|
||
| @IBOutlet weak var selectedUserCollectionViewBottomsConstraints: NSLayoutConstraint! | ||
|
|
||
| var activityIndicator = UIActivityIndicatorView() | ||
|
|
||
| private let searchedUsersCellID = "SearchUserTableviewCell" | ||
| private let selectedUsersCellID = "SelectedUserCollectionViewCell" | ||
|
|
||
| override func viewDidLoad() { | ||
| super.viewDidLoad() | ||
|
|
||
| self.setupNavigationItem() | ||
| self.setupUserSearchBar() | ||
| self.setupSerchUserTableview() | ||
| self.setupSelectedUserCollectionView() | ||
| self.setupActivityIndicator() | ||
| self.setupNotificationCenter() | ||
| } | ||
|
|
||
| private func setupNavigationItem() { | ||
| self.navigationItem.title = "Choose friends" | ||
| let stopItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(tapStopCreateRoomButton)) | ||
| self.navigationItem.leftBarButtonItem = stopItem | ||
| let saveItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(tapCreateRoomButton)) | ||
| self.navigationItem.rightBarButtonItem = saveItem | ||
| } | ||
|
|
||
| private func setupUserSearchBar() { | ||
| self.userNameSearchBar.placeholder = "Search by name" | ||
| self.userNameSearchBar.delegate = self | ||
| } | ||
|
|
||
| private func setupSerchUserTableview() { | ||
| self.serchUserTableview.rowHeight = 75 | ||
| self.serchUserTableview.delegate = self | ||
| self.serchUserTableview.dataSource = self | ||
| self.serchUserTableview.tableFooterView = UIView() | ||
| } | ||
|
|
||
| private func setupSelectedUserCollectionView() { | ||
| self.selectedUserCollectionView.isHidden = true | ||
| self.selectedUserCollectionView.collectionViewLayout.invalidateLayout() | ||
| self.selectedUserCollectionView.delegate = self | ||
| self.selectedUserCollectionView.dataSource = self | ||
| } | ||
|
|
||
| private func setupActivityIndicator() { | ||
| self.activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50) | ||
| self.activityIndicator.center = self.view.center | ||
| self.activityIndicator.hidesWhenStopped = true | ||
| self.view.addSubview(self.activityIndicator) | ||
| } | ||
|
|
||
| func setupNotificationCenter() { | ||
| NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShowNotification(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) | ||
| NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHideNotification(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil) | ||
| } | ||
|
|
||
| /// キーボードが登場した時の処理 | ||
| @objc func keyboardWillShowNotification(notification: NSNotification) { | ||
| guard let userInfo = notification.userInfo else { return } | ||
| guard let keyboard = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } | ||
|
|
||
| self.selectedUserCollectionViewBottomsConstraints.constant = -keyboard.cgRectValue.height + self.view.safeAreaInsets.bottom | ||
| UIView.animate(withDuration: 1.0, animations: { self.view.layoutIfNeeded() }) | ||
| } | ||
|
|
||
| /// キーボードが隠れた時の処理 | ||
| @objc func keyboardWillHideNotification(notification: NSNotification) { | ||
| guard notification.userInfo != nil else { return } | ||
|
|
||
| self.selectedUserCollectionViewBottomsConstraints.constant = 0 | ||
| UIView.animate(withDuration: 1.0, animations: { self.view.layoutIfNeeded() }) | ||
| } | ||
|
|
||
| @objc func tapStopCreateRoomButton() { | ||
| self.presenter.didTapStopCreateRoomButton() | ||
| } | ||
|
|
||
| @objc func tapCreateRoomButton() { | ||
| self.presenter.didTapCreateRoomutton() | ||
| } | ||
|
|
||
| /// `selectedUserCollectionView`にあるバツボタンタップされたときに呼ばれる関数。 | ||
| /// - Parameter button: ボタンの`tag`でどのindexを消すかがわかる | ||
| @objc func tapSelectedUserCollectionViewCellDeleteUserButton(_ button: UIButton) { | ||
| self.presenter.didTapSelectedUserCollectionViewCellDeleteUserButton(index: button.tag) | ||
| } | ||
|
|
||
| func inject(with presenter: CreateChatRoomViewPresenterProtocol) { | ||
| self.presenter = presenter | ||
| self.presenter.view = self | ||
| } | ||
| } | ||
|
|
||
| extension CreateChatRoomViewController: CreateChatRoomViewPresenterOutput { | ||
| func reloadSerchUserTableview() { | ||
| DispatchQueue.main.async { self.serchUserTableview.reloadData() } | ||
| } | ||
|
|
||
| func reloadSelectedUserCollectionView() { | ||
| DispatchQueue.main.async { self.selectedUserCollectionView.reloadData() } | ||
| guard self.selectedUserCollectionView.isHidden else { return } | ||
|
|
||
| self.selectedUserCollectionView.alpha = 0 | ||
| self.selectedUserCollectionView.isHidden = false | ||
| UIView.animate( withDuration: 0.15, animations: { self.selectedUserCollectionView.alpha = 1 }, completion: nil) | ||
| } | ||
|
|
||
| func hiddenSelectedUsersCollectionView() { | ||
| UIView.animate( | ||
| withDuration: 0.25, | ||
| animations: { self.selectedUserCollectionView.alpha = 0 }, | ||
| completion: { _ in | ||
| self.selectedUserCollectionView.isHidden = true | ||
| self.selectedUserCollectionView.alpha = 1 | ||
| } | ||
| ) | ||
| } | ||
|
|
||
| func dismissCreateChatRoomVC() { | ||
| DispatchQueue.main.async { self.dismiss(animated: true, completion: nil) } | ||
| } | ||
|
|
||
| func clearSearchUserTableView() { | ||
| DispatchQueue.main.async { self.serchUserTableview.reloadData() } | ||
| } | ||
|
|
||
| func startActivityIndicator() { | ||
| DispatchQueue.main.async { self.activityIndicator.startAnimating() } | ||
| } | ||
|
|
||
| func stopActivityIndicator() { | ||
| DispatchQueue.main.async { self.activityIndicator.stopAnimating() } | ||
| } | ||
| } | ||
|
|
||
| extension CreateChatRoomViewController: UITableViewDelegate, UITableViewDataSource { | ||
| func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | ||
| return self.presenter.numberOfSearchedUsers | ||
| } | ||
|
|
||
| func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | ||
| guard let cell = self.serchUserTableview.dequeueReusableCell(withIdentifier: self.searchedUsersCellID, for: indexPath) | ||
| as? SearchUserTableviewCell else { return UITableViewCell() } | ||
|
|
||
| let selectedUsersArray: [User] = self.presenter.getSelectedUsersArray() | ||
| let searchedUsersArray: [User] = self.presenter.getSearchedUsersArray() | ||
| let isSelected = !selectedUsersArray.filter({ $0.id == searchedUsersArray[indexPath.item].id ?? ""}).isEmpty | ||
| cell.configure(with: searchedUsersArray[indexPath.item], isSelected: isSelected) | ||
|
|
||
| //TODO:Firestoreから取得した後で表示し直すこと | ||
| if #available(iOS 13.0, *) { | ||
| cell.profileImageView.image = UIImage(systemName: "bolt.circle.fill") | ||
| } else { | ||
| // Fallback on earlier versions | ||
| } | ||
|
|
||
| return cell | ||
| } | ||
|
|
||
| func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | ||
| let searchedUsersArray: [User] = self.presenter.getSearchedUsersArray() | ||
| self.serchUserTableview.deselectRow(at: indexPath, animated: true) | ||
| self.presenter.didSelectedSerchUserTableview(selectedUser: searchedUsersArray[indexPath.item]) | ||
| } | ||
|
|
||
| } | ||
|
|
||
| extension CreateChatRoomViewController: UICollectionViewDelegate, UICollectionViewDataSource { | ||
| func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { | ||
| return self.presenter.numberOfSelectedUsers | ||
| } | ||
|
|
||
| func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { | ||
| let cell = collectionView.dequeueReusableCell(withReuseIdentifier: selectedUsersCellID, for: indexPath) as! SelectedUserCollectionViewCell | ||
| let selectedUsersArray: [User] = self.presenter.getSelectedUsersArray() | ||
|
|
||
| cell.userNameLabel.text = selectedUsersArray[indexPath.item].displayName | ||
|
|
||
| cell.deleteUserButton.tag = indexPath.item | ||
| cell.deleteUserButton.addTarget(self, action: #selector(tapSelectedUserCollectionViewCellDeleteUserButton(_:)), for: .touchUpInside) | ||
|
|
||
| //TODO:Firestoreから取得した後で表示し直すこと | ||
| if #available(iOS 13.0, *) { | ||
| cell.profileImageView.image = UIImage(systemName: "bolt.circle.fill") | ||
| } else { | ||
| // Fallback on earlier versions | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tagを使わずにdelegateかクロージャの方が良いかなと思うのですがどうでしょうか?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これは結局
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. タグを使わない方が良い(例えばsectionがいっこだけじゃない時にややこしくなる)と思っていて こんな感じでindexPath.itemを渡した方が良いかなと思いました
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど。理解しました!了解です |
||
|
|
||
| return cell | ||
| } | ||
|
|
||
| func numberOfSections(in collectionView: UICollectionView) -> Int { | ||
| return 1 | ||
| } | ||
|
|
||
| func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { | ||
| return CGSize(width: 85, height: 90) | ||
| } | ||
|
|
||
| func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { | ||
| return UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10) | ||
| } | ||
| } | ||
|
|
||
| extension CreateChatRoomViewController: UISearchBarDelegate { | ||
| func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { | ||
| guard scrollView == self.serchUserTableview else { return } | ||
| guard self.userNameSearchBar.isFirstResponder else { return } | ||
| self.userNameSearchBar.resignFirstResponder() | ||
| } | ||
|
|
||
| /// 検索ボタンがタップされたときに呼ばれる関数 | ||
| /// - Parameter searchBar: サーチバー | ||
| func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { | ||
| guard let searchBarText = searchBar.text else { return } | ||
| guard !searchBarText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return } | ||
| guard self.userNameSearchBar.isFirstResponder else { return } | ||
| self.userNameSearchBar.resignFirstResponder() | ||
|
|
||
| self.presenter.didSearchBarSearchButtonClicked(searchText: searchBarText) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| // | ||
| // CreateChatRoomViewModel.swift | ||
| // chat-iOS | ||
| // | ||
| // Created by jun on 2020/07/18. | ||
| // | ||
|
|
||
| import Firebase | ||
|
|
||
| protocol CreateChatRoomModelProtocol { | ||
| var presenter: CreateChatRoomModelOutput! { get set } | ||
| var selectedUsersArray: [User] { get set } | ||
| var searchedUsersArray: [User] { get set } | ||
|
|
||
| func isContaintsUser(user: User) -> Bool | ||
| func searchUser(searchText: String) | ||
| func removeSelectedUserFromSelectedUserArray(user: User) | ||
| func appendUserToSelectedUserArray(user: User) | ||
| func removeSelectedUsersArray(index: Int) -> [User] | ||
| func createChatRoom() | ||
| } | ||
|
|
||
| protocol CreateChatRoomModelOutput: class { | ||
| func successRemoveSelectedUser() | ||
| func successAppendUser() | ||
|
|
||
| func successCreateChatRoom() | ||
|
|
||
| func successSearchUser() | ||
| } | ||
|
|
||
| final class CreateChatRoomModel: CreateChatRoomModelProtocol { | ||
| weak var presenter: CreateChatRoomModelOutput! | ||
| private var firestore: Firestore! | ||
| var selectedUsersArray: [User] = Array() | ||
| var searchedUsersArray: [User] = Array() | ||
|
|
||
| init() { | ||
| self.firestore = Firestore.firestore() | ||
| let settings = FirestoreSettings() | ||
| self.firestore.settings = settings | ||
| } | ||
|
|
||
| /// ユーザをFirestoreから検索する関数 | ||
| /// - Parameter searchText: 検索するユーザ名 | ||
| func searchUser(searchText: String) { | ||
| self.firestore.collection("message/v1/users").whereField("displayName", isEqualTo: searchText).getDocuments { [weak self] (documentSnapshot, error) in | ||
| if let error = error { | ||
| print("Error: \(error.localizedDescription)") | ||
| return | ||
| } | ||
|
|
||
| guard let documents = documentSnapshot?.documents else { | ||
| print("The document doesn't exist.") | ||
| return | ||
| } | ||
|
|
||
| let searchedUsers = documents.compactMap { queryDocumentSnapshot -> User? in | ||
| return try? queryDocumentSnapshot.data(as: User.self) | ||
| } | ||
|
|
||
| self?.searchedUsersArray = searchedUsers | ||
| self?.presenter.successSearchUser() | ||
| } | ||
| } | ||
|
|
||
| /// ユーザが既に選択されているかを返す | ||
| /// - Parameter user: 検索するユーザ | ||
| /// - Returns: 検索結果 | ||
| func isContaintsUser(user: User) -> Bool { | ||
| if self.selectedUsersArray.filter({ $0.id == user.id ?? "" }).isEmpty { return false } | ||
| return true | ||
| } | ||
|
|
||
| func removeSelectedUserFromSelectedUserArray(user: User) { | ||
| self.selectedUsersArray = self.selectedUsersArray.filter({ $0.id != user.id }) | ||
|
|
||
| self.presenter.successRemoveSelectedUser() | ||
| } | ||
|
|
||
| func appendUserToSelectedUserArray(user: User) { | ||
| self.selectedUsersArray.append(user) | ||
| self.presenter.successAppendUser() | ||
| } | ||
|
|
||
| /// `selectedUsersArray`からあるインデックスを削除する | ||
| /// - Parameter index: 削除する配列番号 | ||
| /// - Returns: 削除した後の`selectedUsersArray` | ||
| func removeSelectedUsersArray(index: Int) -> [User] { | ||
| self.selectedUsersArray.remove(at: index) | ||
| return self.selectedUsersArray | ||
| } | ||
|
|
||
| //TODO:- Firesotreに保存する | ||
| func createChatRoom() { | ||
| self.presenter.successCreateChatRoom() | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.