git.iko.soy/dashared/GDproject
on master help sign in
Added ability to write and send message to chats and groups. Full UI is waiting for me. Can give admins. Chat info is completed.
4acfa6eDarya Rednikina
GDproject/Base.lproj/Main.storyboard−44
@@ -610,50 +610,6 @@
610 </objects>
610 </objects>
611 <point key="canvasLocation" x="3614" y="938"/>
611 <point key="canvasLocation" x="3614" y="938"/>
612 </scene>
612 </scene>
613− <!--Dialog View Controller-->
614− <scene sceneID="9ac-1R-s4m">
615− <objects>
616− <tableViewController storyboardIdentifier="DialogViewController" id="AVr-rR-mka" customClass="DialogViewController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
617− <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="NlQ-jM-tQP">
618− <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
619− <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
620− <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
621− <prototypes>
622− <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="cell3" textLabel="8Ad-wM-No8" detailTextLabel="QHM-V8-CWk" style="IBUITableViewCellStyleSubtitle" id="bgd-9d-SmR">
623− <rect key="frame" x="0.0" y="28" width="375" height="44"/>
624− <autoresizingMask key="autoresizingMask"/>
625− <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="bgd-9d-SmR" id="9W4-bO-4MN">
626− <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
627− <autoresizingMask key="autoresizingMask"/>
628− <subviews>
629− <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8Ad-wM-No8">
630− <rect key="frame" x="16" y="5" width="33.5" height="20.5"/>
631− <autoresizingMask key="autoresizingMask"/>
632− <fontDescription key="fontDescription" type="system" pointSize="17"/>
633− <nil key="textColor"/>
634− <nil key="highlightedColor"/>
635− </label>
636− <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="hfhhfhh" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QHM-V8-CWk">
637− <rect key="frame" x="16" y="25.5" width="44.5" height="14.5"/>
638− <autoresizingMask key="autoresizingMask"/>
639− <fontDescription key="fontDescription" type="system" pointSize="12"/>
640− <nil key="textColor"/>
641− <nil key="highlightedColor"/>
642− </label>
643− </subviews>
644− </tableViewCellContentView>
645− </tableViewCell>
646− </prototypes>
647− <connections>
648− <outlet property="dataSource" destination="AVr-rR-mka" id="quz-3V-jDT"/>
649− <outlet property="delegate" destination="AVr-rR-mka" id="R4y-E9-LVr"/>
650− </connections>
651− </tableView>
652− </tableViewController>
653− <placeholder placeholderIdentifier="IBFirstResponder" id="Tzh-O8-O7T" userLabel="First Responder" sceneMemberID="firstResponder"/>
654− </objects>
655− <point key="canvasLocation" x="4336" y="937"/>
656− </scene>
657 <!--Chat Info View Controller-->
613 <!--Chat Info View Controller-->
658 <scene sceneID="1br-Xg-fR9">
614 <scene sceneID="1br-Xg-fR9">
659 <objects>
615 <objects>
GDproject/Controller/Coordinators/MessagesCoordinator.swift+2−4
@@ -40,15 +40,13 @@ class MessagesCoordinator: BaseCoordinator {
40 vc?.navigationController?.pushViewController(newVC, animated: true)
40 vc?.navigationController?.pushViewController(newVC, animated: true)
41 }
41 }
42
42
43− vc.onDialogDisplay = { [weak vc, unowned self] in
43+ vc.onDialogDisplay = { [weak vc] in
44− let newVC = self.storyboard.instantiateViewController(withIdentifier: dialogVC) as! DialogViewController
44+ let newVC = DialogViewController()
45 newVC.dialog = $0.dialog
45 newVC.dialog = $0.dialog
46 newVC.users = $0.users
46 newVC.users = $0.users
47 vc?.navigationController?.pushViewController(newVC, animated: true)
47 vc?.navigationController?.pushViewController(newVC, animated: true)
48 }
48 }
49
49
50−
51−
52 navigationController?.viewControllers = [vc]
50 navigationController?.viewControllers = [vc]
53 }
51 }
54}
52}
GDproject/Controller/Messages/ChatInfoViewController.swift+43−1
@@ -119,7 +119,14 @@ class ChatInfoViewController: UITableViewController {
119 }
119 }
120
120
121 private func name(for user: Model.Users?) -> String {
121 private func name(for user: Model.Users?) -> String {
122+ if let user = user, let perm = groupChat?.users[user.id]?.isAdmin {
123+ if perm {
124+ return "🤴🏻 \(user.fullName())"
125+ }
122− return "👤 \(user!.fullName())"
126+ return "👤 \(user.fullName())"
127+ }
128+
129+ return "left"
123 }
130 }
124
131
125 override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
132 override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
@@ -219,6 +226,41 @@ class ChatInfoViewController: UITableViewController {
219 return true
226 return true
220 }
227 }
221
228
229+ override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
230+ {
231+ let editButton = UITableViewRowAction(style: .normal, title: "Promote") { [unowned self] (rowAction, indexPath) in
232+
233+ self.tableView.beginUpdates()
234+ self.groupChat?.users[self.usersArray[indexPath.row-1]]?.isAdmin = true
235+ self.tableView.reloadRows(at: [indexPath], with: .none)
236+ self.tableView.endUpdates()
237+ }
238+
239+ editButton.backgroundColor = #colorLiteral(red: 0.476841867, green: 0.5048075914, blue: 1, alpha: 1)
240+
241+ let restrictButton = UITableViewRowAction(style: .normal, title: "Restrict") { [unowned self] (rowAction, indexPath) in
242+
243+ self.tableView.beginUpdates()
244+ self.groupChat?.users[self.usersArray[indexPath.row-1]]?.isAdmin = false
245+ self.tableView.reloadRows(at: [indexPath], with: .none)
246+ self.tableView.endUpdates()
247+ }
248+
249+ restrictButton.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
250+
251+ let deleteButton = UITableViewRowAction(style: .normal, title: "Delete") { [unowned self] (action, indexPath) in
252+
253+ self.tableView.beginUpdates()
254+ self.groupChat?.users[self.usersArray[indexPath.row-1]] = nil
255+ self.tableView.deleteRows(at: [indexPath], with: .none)
256+ self.tableView.endUpdates()
257+ }
258+
259+ deleteButton.backgroundColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1)
260+
261+ return [editButton, restrictButton, deleteButton]
262+ }
263+
222 /// update chatInfo
264 /// update chatInfo
223 override func viewWillDisappear(_ animated: Bool) {
265 override func viewWillDisappear(_ animated: Bool) {
224
266
GDproject/Controller/Messages/Dialog/DialogViewController.swift+109−5
@@ -7,12 +7,16 @@
7//
7//
8
8
9import UIKit
9import UIKit
10+import TinyConstraints
10
11
11protocol UpdatableGroup:class {
12protocol UpdatableGroup:class {
12 func updateGroup(with group: Model.Group)
13 func updateGroup(with group: Model.Group)
13}
14}
14
15
15−class DialogViewController: UITableViewController, UpdatableGroup {
16+class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegate, UITableViewDataSource
17+{
18+
19+ var tableView: UITableView = UITableView()
16
20
17 func updateGroup(with group: Model.Group){
21 func updateGroup(with group: Model.Group){
18 self.groupChat?.group = group
22 self.groupChat?.group = group
@@ -49,6 +53,12 @@ class DialogViewController: UITableViewController, UpdatableGroup {
49
53
50 override func viewDidLoad() {
54 override func viewDidLoad() {
51 super.viewDidLoad()
55 super.viewDidLoad()
56+
57+ setConstraints()
58+ magicForKeyboardChanges()
59+
60+ self.tableView.delegate = self
61+ self.tableView.dataSource = self
52
62
53 tabBarController?.tabBar.isHidden = true
63 tabBarController?.tabBar.isHidden = true
54 navigationItem.largeTitleDisplayMode = .never
64 navigationItem.largeTitleDisplayMode = .never
@@ -65,6 +75,97 @@ class DialogViewController: UITableViewController, UpdatableGroup {
65 }
75 }
66 }
76 }
67
77
78+ var messageSendView: UIView = {
79+ let view = UIView()
80+ view.backgroundColor = .red
81+ return view
82+ }()
83+
84+ var messageTextView: UITextView = {
85+ let textView = UITextView()
86+ textView.isEditable = true
87+ textView.isScrollEnabled = true
88+ textView.backgroundColor = .green
89+ return textView
90+ }()
91+
92+ var sendButton: UIButton = {
93+ let button = UIButton()
94+ button.setTitle("Send", for: .normal)
95+ button.setTitleColor(.blue, for: .normal)
96+ button.addTarget(self, action: #selector(sendMessage), for: .touchUpInside)
97+ return button
98+ }()
99+
100+ @objc func sendMessage()
101+ {
102+ var destination: Model.MessageDestination?
103+
104+ if let group = groupChat{
105+ destination = Model.MessageDestination.groupChatDestination(group.group.id)
106+ } else if let user = userChat {
107+ destination = Model.MessageDestination.userChatDestination(user.user)
108+ }
109+
110+ if let destination = destination
111+ {
112+ Model.sendMessage(message: Model.SendMessage(body: Model.Attachments(markdown: messageTextView.text), destination: destination)) { [unowned self] in
113+ self.getMessages(for: self.dialog!)
114+ }
115+ }
116+ }
117+
118+ var bottomConstraint: NSLayoutConstraint!
119+
120+ func setConstraints(){
121+ self.view.addSubview(tableView)
122+ self.view.addSubview(messageSendView)
123+
124+ messageSendView.addSubview(messageTextView)
125+ messageSendView.addSubview(sendButton)
126+
127+ messageSendView.edgesToSuperview(excluding: [.top,.bottom])
128+
129+ bottomConstraint = NSLayoutConstraint(item: messageSendView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
130+ view.addConstraint(bottomConstraint)
131+
132+ messageSendView.height(40)
133+
134+ sendButton.edgesToSuperview(excluding: .left)
135+ sendButton.width(60)
136+
137+ messageTextView.edgesToSuperview(excluding: .right)
138+ messageTextView.rightToLeft(of: sendButton)
139+
140+ tableView.edgesToSuperview(excluding: .bottom)
141+ tableView.bottomToTop(of: messageSendView)
142+
143+ self.view.layoutSubviews()
144+ }
145+
146+ func magicForKeyboardChanges()
147+ {
148+ NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillShowNotification, object: nil)
149+
150+ NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillHideNotification, object: nil)
151+ }
152+
153+ @objc func handleKeyboardNotifications(notification: NSNotification){
154+ if let userInfo = notification.userInfo{
155+ // UIKeyboardFrameEndUserInfoKey
156+
157+ let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
158+
159+ bottomConstraint.constant = notification.name == UIResponder.keyboardWillShowNotification ? -(keyBoardFrame.height) : 0
160+
161+ UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: {
162+ self.view.layoutIfNeeded()
163+ }) { (completed) in
164+
165+ }
166+ }
167+ }
168+
68 func setTitleForChat(userChat: Model.UserChat){
169 func setTitleForChat(userChat: Model.UserChat){
69 navigationItem.title = "🌌 \(users![userChat.user]!.fullName())"
170 navigationItem.title = "🌌 \(users![userChat.user]!.fullName())"
70 }
171 }
@@ -75,7 +176,7 @@ class DialogViewController: UITableViewController, UpdatableGroup {
75 }
176 }
76
177
77 @objc func moveToInfoVC(){
178 @objc func moveToInfoVC(){
78− let vc = storyboard?.instantiateViewController(withIdentifier: chatInfoViewController) as! ChatInfoViewController
179+ let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: chatInfoViewController) as! ChatInfoViewController
79
180
80 vc.delegate = self
181 vc.delegate = self
81 vc.users = users!
182 vc.users = users!
@@ -97,11 +198,11 @@ class DialogViewController: UITableViewController, UpdatableGroup {
97
198
98 // MARK: - Table view data source
199 // MARK: - Table view data source
99
200
100− override func numberOfSections(in tableView: UITableView) -> Int {
201+ func numberOfSections(in tableView: UITableView) -> Int {
101 return 1
202 return 1
102 }
203 }
103
204
104− override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
205+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
105 return cellData.count
206 return cellData.count
106 }
207 }
107
208
@@ -128,7 +229,7 @@ class DialogViewController: UITableViewController, UpdatableGroup {
128 }
229 }
129 }
230 }
130
231
131− override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
232+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
132 {
233 {
133 let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! DialogCell
234 let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! DialogCell
134
235
@@ -143,3 +244,6 @@ class DialogViewController: UITableViewController, UpdatableGroup {
143 return cell
244 return cell
144 }
245 }
145}
246}
247+
248+
249+
GDproject/Controller/Messages/MessagesViewController.swift+2−1
@@ -67,11 +67,12 @@ class MessagesViewController: UITableViewController {
67 let cell = tableView.dequeueReusableCell(withIdentifier: "MessagesCell", for: indexPath)
67 let cell = tableView.dequeueReusableCell(withIdentifier: "MessagesCell", for: indexPath)
68
68
69 switch currentActiveDialogs[indexPath.row].self {
69 switch currentActiveDialogs[indexPath.row].self {
70+
70 case .groupChat(let group):
71 case .groupChat(let group):
71 cell.textLabel?.text = group.group.name
72 cell.textLabel?.text = group.group.name
72 cell.detailTextLabel?.text = group.lastMessage.body.markdown
73 cell.detailTextLabel?.text = group.lastMessage.body.markdown
73 case .userChat(let userChat):
74 case .userChat(let userChat):
74− cell.textLabel?.text = "\(users[userChat.user]!)"
75+ cell.textLabel?.text = "👤 \(users[userChat.user]!.fullName())"
75 cell.detailTextLabel?.text = userChat.lastMessage.body.markdown
76 cell.detailTextLabel?.text = userChat.lastMessage.body.markdown
76 }
77 }
77
78
GDproject/Controller/Profile/ProfileViewController.swift+1−1
@@ -90,7 +90,7 @@ class ProfileViewController: UIViewController
90 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
90 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
91
91
92 tableView.register(BasicInfoCell.self, forCellReuseIdentifier: basicInfoCellId)
92 tableView.register(BasicInfoCell.self, forCellReuseIdentifier: basicInfoCellId)
93−
93+
94 tableView.register(InfoCell.self, forCellReuseIdentifier: infoCellId)
94 tableView.register(InfoCell.self, forCellReuseIdentifier: infoCellId)
95
95
96 posts.viewController = self
96 posts.viewController = self
GDproject/Simple model/Model.swift+52−1
@@ -642,7 +642,7 @@ class Model{
642 var author: Int
642 var author: Int
643 var id: Int
643 var id: Int
644
644
645− private enum MessageCodingKeys: CodingKey{
645+ enum MessageCodingKeys: CodingKey{
646 case user
646 case user
647 case group
647 case group
648 case body
648 case body
@@ -793,4 +793,55 @@ class Model{
793
793
794 // completion()
794 // completion()
795 }
795 }
796+
797+ struct SendMessage: Codable {
798+ var body: Attachments
799+ var destination: MessageDestination
800+
801+ init(from decoder: Decoder) throws
802+ {
803+ let container = try decoder.container(keyedBy: LastMessage.MessageCodingKeys.self)
804+ body = try container.decode(Attachments.self, forKey: .body)
805+
806+ if container.contains(.user){
807+ destination = MessageDestination.userChatDestination(try Int(from: container.superDecoder(forKey: .user)))
808+ } else if container.contains(.group){
809+ destination = MessageDestination.groupChatDestination(try Int(from: container.superDecoder(forKey: .group)))
810+ } else {
811+ throw DecodingError.keyNotFound(LastMessage.MessageCodingKeys.group, DecodingError.Context(codingPath: container.codingPath, debugDescription: "hz gr1"))
812+ }
813+ }
814+
815+ func encode(to encoder: Encoder) throws {
816+ var container = encoder.container(keyedBy: LastMessage.MessageCodingKeys.self)
817+ try container.encode(body, forKey: .body)
818+
819+ switch destination {
820+ case .userChatDestination(let uId):
821+ try uId.encode(to: container.superEncoder(forKey: .user))
822+ case .groupChatDestination(let gId):
823+ try gId.encode(to: container.superEncoder(forKey: .group))
824+ }
825+ }
826+
827+ init(body: Attachments, destination: MessageDestination)
828+ {
829+ self.body = body
830+ self.destination = destination
831+ }
832+ }
833+
834+ static func sendMessage(message: SendMessage, completion: @escaping (()->())){
835+
836+ var request = URLRequest(url: messagesSendURL)
837+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
838+ request.httpMethod = "POST"
839+ request.httpBody = try? JSONEncoder().encode(message)
840+
841+ AF.request(request).response { (response) in
842+ isValidTocken?(response.response?.statusCode ?? 498)
843+
844+ completion()
845+ }
846+ }
796}
847}