git.iko.soy/dashared/GDproject
on master help sign in
39 files changed+3237−710
since splitsnapshots
unifiedsplit
GDproject.xcodeproj/project.pbxproj+44−12 GDproject/Base.lproj/Main.storyboard+439−127 GDproject/Constants.swift+8 GDproject/Controller/ News and channels/AddToChannelVC.swift+41−26 GDproject/Controller/ News and channels/AnonimousChannelController.swift−18 GDproject/Controller/ News and channels/ChannelController.swift+1−1 GDproject/Controller/ News and channels/ChannelListController.swift+2−2 GDproject/Controller/ News and channels/ChannelViewController.swift+29−16 GDproject/Controller/ News and channels/FullPostController.swift+3−19 GDproject/Controller/ News and channels/NewPostViewController.swift+46−10 GDproject/Controller/ News and channels/NewsController.swift+53−14 GDproject/Controller/ News and channels/NewsVC.swift+12−11 GDproject/Controller/ News and channels/PostViewCell.swift+7−3 GDproject/Controller/ News and channels/TaggsSelectionViewController.swift+106 GDproject/Controller/ News and channels/TagsSuggestionController.swift+52 GDproject/Controller/Coordinators/ChannelsCoordinator.swift+20 GDproject/Controller/Coordinators/LogInCoordinator.swift+37−8 GDproject/Controller/Coordinators/MessagesCoordinator.swift+45−3 GDproject/Controller/Coordinators/ProfileCoordinator.swift+22−16 GDproject/Controller/Log In/CodeViewController.swift+144 GDproject/Controller/Log In/FacultyTableViewController.swift+88 GDproject/Controller/Log In/InititalsViewCell.swift+23 GDproject/Controller/Log In/LogInCoordinator1.swift−48 GDproject/Controller/Log In/LogInViewController.swift+29−4 GDproject/Controller/Log In/RegisterTableViewController.swift+198 GDproject/Controller/Messages/ChatInfoViewController.swift+270−56 GDproject/Controller/Messages/Dialog/DialogCell.swift+132 GDproject/Controller/Messages/Dialog/DialogViewController.swift+340 GDproject/Controller/Messages/DialogViewController.swift−92 GDproject/Controller/Messages/MessageViewCell.swift+38 GDproject/Controller/Messages/MessagesViewController.swift+122−54 GDproject/Controller/Messages/PeopleToWriteViewController.swift+21−62 GDproject/Controller/Profile/BasicInfoController.swift+16−11 GDproject/Controller/Profile/InfoCell.swift+2−1 GDproject/Controller/Profile/ProfileViewController.swift+139−55 GDproject/DataStorage.swift+8 GDproject/Extentions.swift+47 GDproject/Simple model/CompletionTree.swift+2−1 GDproject/Simple model/Model.swift+651−40
GDproject.xcodeproj/project.pbxproj+44−12
@@ -8,25 +8,31 @@
88
99/* Begin PBXBuildFile section */
1010 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */; };
11− 121A8972226B1EAC005EE599 /* AnonimousChannelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */; };
11+ 121A8972226B1EAC005EE599 /* TagsSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121A8971226B1EAC005EE599 /* TagsSuggestionController.swift */; };
12+ 1220FF94227C41270092C6BC /* TaggsSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */; };
13+ 123589FD22899339002F8028 /* MessageViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123589FC22899339002F8028 /* MessageViewCell.swift */; };
1214 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A4221F1B3200F6E42A /* LogInViewController.swift */; };
1315 123E37A7221F1DD700F6E42A /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A6221F1DD700F6E42A /* MainController.swift */; };
1416 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; };
17+ 125A9A8A2285C3DA00513E2A /* CodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125A9A892285C3DA00513E2A /* CodeViewController.swift */; };
18+ 125A9A8E2286068A00513E2A /* RegisterTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125A9A8D2286068A00513E2A /* RegisterTableViewController.swift */; };
19+ 125A9A90228609C500513E2A /* InititalsViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125A9A8F228609C500513E2A /* InititalsViewCell.swift */; };
20+ 125A9A922286100800513E2A /* FacultyTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125A9A912286100800513E2A /* FacultyTableViewController.swift */; };
1521 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; };
1622 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; };
1723 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; };
1824 1261BB93227B364C003898CF /* ChannelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB92227B364C003898CF /* ChannelViewController.swift */; };
1925 1261BB95227B3991003898CF /* AddToChannelVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB94227B3991003898CF /* AddToChannelVC.swift */; };
2026 1261BB9E227B793D003898CF /* ChatInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB9D227B793D003898CF /* ChatInfoViewController.swift */; };
27+ 12758257227F1401001F291F /* DialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12758256227F1401001F291F /* DialogViewController.swift */; };
2128 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1288B5CD221F1158002BE6B1 /* DataStorage.swift */; };
2229 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; };
2330 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; };
24− 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */; };
2531 129320042279B4270035C7B3 /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320032279B4270035C7B3 /* MessagesViewController.swift */; };
2632 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320062279B5690035C7B3 /* MessagesCoordinator.swift */; };
2733 129320092279C3B50035C7B3 /* PeopleToWriteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */; };
28− 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1293200A2279D02D0035C7B3 /* DialogViewController.swift */; };
2934 1293200D2279D7310035C7B3 /* CompletionTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1293200C2279D7310035C7B3 /* CompletionTree.swift */; };
35+ 1298810B227F13840032ACA3 /* DialogCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1298810A227F13840032ACA3 /* DialogCell.swift */; };
3036 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */; };
3137 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */; };
3238 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */; };
@@ -101,25 +107,31 @@
101107
102108/* Begin PBXFileReference section */
103109 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplifiedChannelsList.swift; sourceTree = "<group>"; };
104− 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnonimousChannelController.swift; sourceTree = "<group>"; };
110+ 121A8971226B1EAC005EE599 /* TagsSuggestionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsSuggestionController.swift; sourceTree = "<group>"; };
111+ 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaggsSelectionViewController.swift; sourceTree = "<group>"; };
112+ 123589FC22899339002F8028 /* MessageViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageViewCell.swift; sourceTree = "<group>"; };
105113 123E37A4221F1B3200F6E42A /* LogInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInViewController.swift; sourceTree = "<group>"; };
106114 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
107115 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = "<group>"; };
116+ 125A9A892285C3DA00513E2A /* CodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeViewController.swift; sourceTree = "<group>"; };
117+ 125A9A8D2286068A00513E2A /* RegisterTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterTableViewController.swift; sourceTree = "<group>"; };
118+ 125A9A8F228609C500513E2A /* InititalsViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InititalsViewCell.swift; sourceTree = "<group>"; };
119+ 125A9A912286100800513E2A /* FacultyTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FacultyTableViewController.swift; sourceTree = "<group>"; };
108120 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
109121 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = "<group>"; };
110122 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = "<group>"; };
111123 1261BB92227B364C003898CF /* ChannelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelViewController.swift; sourceTree = "<group>"; };
112124 1261BB94227B3991003898CF /* AddToChannelVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToChannelVC.swift; sourceTree = "<group>"; };
113125 1261BB9D227B793D003898CF /* ChatInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoViewController.swift; sourceTree = "<group>"; };
126+ 12758256227F1401001F291F /* DialogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DialogViewController.swift; sourceTree = "<group>"; };
114127 1288B5CD221F1158002BE6B1 /* DataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStorage.swift; sourceTree = "<group>"; };
115128 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = "<group>"; };
116129 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = "<group>"; };
117− 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator1.swift; sourceTree = "<group>"; };
118130 129320032279B4270035C7B3 /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = "<group>"; };
119131 129320062279B5690035C7B3 /* MessagesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesCoordinator.swift; sourceTree = "<group>"; };
120132 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PeopleToWriteViewController.swift; path = GDproject/Controller/Messages/PeopleToWriteViewController.swift; sourceTree = SOURCE_ROOT; };
121− 1293200A2279D02D0035C7B3 /* DialogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DialogViewController.swift; path = GDproject/Controller/Messages/DialogViewController.swift; sourceTree = SOURCE_ROOT; };
122133 1293200C2279D7310035C7B3 /* CompletionTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionTree.swift; sourceTree = "<group>"; };
134+ 1298810A227F13840032ACA3 /* DialogCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialogCell.swift; sourceTree = "<group>"; };
123135 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = "<group>"; };
124136 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = "<group>"; };
125137 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = "<group>"; };
@@ -212,8 +224,11 @@
212224 1291BE3722218DC4009D3F23 /* Log In */ = {
213225 isa = PBXGroup;
214226 children = (
227+ 125A9A912286100800513E2A /* FacultyTableViewController.swift */,
228+ 125A9A8F228609C500513E2A /* InititalsViewCell.swift */,
229+ 125A9A8D2286068A00513E2A /* RegisterTableViewController.swift */,
230+ 125A9A892285C3DA00513E2A /* CodeViewController.swift */,
215231 123E37A4221F1B3200F6E42A /* LogInViewController.swift */,
216− 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */,
217232 );
218233 path = "Log In";
219234 sourceTree = "<group>";
@@ -223,12 +238,22 @@
223238 children = (
224239 1261BB9D227B793D003898CF /* ChatInfoViewController.swift */,
225240 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */,
226− 1293200A2279D02D0035C7B3 /* DialogViewController.swift */,
241+ 1298810C227F138C0032ACA3 /* Dialog */,
227242 129320032279B4270035C7B3 /* MessagesViewController.swift */,
243+ 123589FC22899339002F8028 /* MessageViewCell.swift */,
228244 );
229245 path = Messages;
230246 sourceTree = "<group>";
231247 };
248+ 1298810C227F138C0032ACA3 /* Dialog */ = {
249+ isa = PBXGroup;
250+ children = (
251+ 12758256227F1401001F291F /* DialogViewController.swift */,
252+ 1298810A227F13840032ACA3 /* DialogCell.swift */,
253+ );
254+ path = Dialog;
255+ sourceTree = "<group>";
256+ };
232257 12E36D8F221424EA006FCDD7 = {
233258 isa = PBXGroup;
234259 children = (
@@ -311,9 +336,10 @@
311336 isa = PBXGroup;
312337 children = (
313338 1261BB92227B364C003898CF /* ChannelViewController.swift */,
339+ 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */,
314340 1261BB94227B3991003898CF /* AddToChannelVC.swift */,
315341 12D7D134221C42B700B35452 /* ChannelController.swift */,
316− 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */,
342+ 121A8971226B1EAC005EE599 /* TagsSuggestionController.swift */,
317343 12D7D132221C321600B35452 /* ChannelListController.swift */,
318344 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */,
319345 12E36DC922144635006FCDD7 /* NewPostViewController.swift */,
@@ -411,30 +437,36 @@
411437 files = (
412438 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */,
413439 124CC7032221C00C009DF531 /* Model.swift in Sources */,
440+ 125A9A90228609C500513E2A /* InititalsViewCell.swift in Sources */,
441+ 1220FF94227C41270092C6BC /* TaggsSelectionViewController.swift in Sources */,
414442 12F3D6B0224106F700A69214 /* TabbarController.swift in Sources */,
415443 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */,
416444 12D7D137221D78E800B35452 /* Channel.swift in Sources */,
417− 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */,
418445 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */,
419446 125BD5812217314A008A3575 /* NewsVC.swift in Sources */,
420447 12E36DB72214400A006FCDD7 /* Post.swift in Sources */,
421448 12DB7FDF221877160096878E /* InviteViewController.swift in Sources */,
422− 121A8972226B1EAC005EE599 /* AnonimousChannelController.swift in Sources */,
449+ 121A8972226B1EAC005EE599 /* TagsSuggestionController.swift in Sources */,
423450 1293200D2279D7310035C7B3 /* CompletionTree.swift in Sources */,
424− 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */,
425451 12E36DCA22144635006FCDD7 /* NewPostViewController.swift in Sources */,
426452 1261BB9E227B793D003898CF /* ChatInfoViewController.swift in Sources */,
427453 12E36D9E221424EA006FCDD7 /* NewsController.swift in Sources */,
454+ 1298810B227F13840032ACA3 /* DialogCell.swift in Sources */,
455+ 125A9A922286100800513E2A /* FacultyTableViewController.swift in Sources */,
456+ 125A9A8E2286068A00513E2A /* RegisterTableViewController.swift in Sources */,
457+ 123589FD22899339002F8028 /* MessageViewCell.swift in Sources */,
428458 12E36DCC22144725006FCDD7 /* PostViewCell.swift in Sources */,
429459 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */,
430460 125BD57F22171D73008A3575 /* Extentions.swift in Sources */,
431461 12D7D133221C321600B35452 /* ChannelListController.swift in Sources */,
462+ 125A9A8A2285C3DA00513E2A /* CodeViewController.swift in Sources */,
432463 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */,
433464 129320092279C3B50035C7B3 /* PeopleToWriteViewController.swift in Sources */,
434465 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */,
435466 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */,
436467 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */,
437468 129320042279B4270035C7B3 /* MessagesViewController.swift in Sources */,
469+ 12758257227F1401001F291F /* DialogViewController.swift in Sources */,
438470 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */,
439471 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */,
440472 12F3D6B22241097B00A69214 /* ProfileCoordinator.swift in Sources */,
GDproject/Base.lproj/Main.storyboard+439−127
@@ -90,6 +90,9 @@
9090 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yir-N6-Q6f">
9191 <rect key="frame" x="246" y="0.0" width="97" height="30"/>
9292 <state key="normal" title="New message"/>
93+ <connections>
94+ <action selector="sendMessage:" destination="R4D-Ao-uvG" eventType="touchUpInside" id="tQc-5f-SU3"/>
95+ </connections>
9396 </button>
9497 </subviews>
9598 </stackView>
@@ -228,6 +231,250 @@
228231 </objects>
229232 <point key="canvasLocation" x="2799" y="1979"/>
230233 </scene>
234+ <!--Code View Controller-->
235+ <scene sceneID="VW5-ny-vaB">
236+ <objects>
237+ <viewController storyboardIdentifier="CodeViewController" id="Lz1-1J-ywG" customClass="CodeViewController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
238+ <view key="view" contentMode="scaleToFill" id="Rud-1x-pKL">
239+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
240+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
241+ <subviews>
242+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enter the code you received via mail" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hE6-cQ-NbO">
243+ <rect key="frame" x="50" y="47" width="275.5" height="20.5"/>
244+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
245+ <nil key="textColor"/>
246+ <nil key="highlightedColor"/>
247+ </label>
248+ <stackView opaque="NO" contentMode="scaleToFill" spacing="12" translatesAutoresizingMaskIntoConstraints="NO" id="2Gj-pC-RZ2">
249+ <rect key="frame" x="37.5" y="99" width="300" height="58"/>
250+ <subviews>
251+ <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="_" textAlignment="center" adjustsFontSizeToFit="NO" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="744-t1-YNn">
252+ <rect key="frame" x="0.0" y="0.0" width="40" height="58"/>
253+ <nil key="textColor"/>
254+ <fontDescription key="fontDescription" type="system" pointSize="45"/>
255+ <textInputTraits key="textInputTraits" keyboardType="numberPad"/>
256+ </textField>
257+ <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="_" textAlignment="center" adjustsFontSizeToFit="NO" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="jcR-jp-s7a">
258+ <rect key="frame" x="52" y="0.0" width="40" height="58"/>
259+ <nil key="textColor"/>
260+ <fontDescription key="fontDescription" type="system" pointSize="45"/>
261+ <textInputTraits key="textInputTraits" keyboardType="numberPad"/>
262+ </textField>
263+ <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="_" textAlignment="center" adjustsFontSizeToFit="NO" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="YKO-0M-KD8">
264+ <rect key="frame" x="104" y="0.0" width="40" height="58"/>
265+ <nil key="textColor"/>
266+ <fontDescription key="fontDescription" type="system" pointSize="45"/>
267+ <textInputTraits key="textInputTraits" keyboardType="numberPad"/>
268+ </textField>
269+ <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="_" textAlignment="center" adjustsFontSizeToFit="NO" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="SmG-Ae-oOs">
270+ <rect key="frame" x="156" y="0.0" width="40" height="58"/>
271+ <nil key="textColor"/>
272+ <fontDescription key="fontDescription" type="system" pointSize="45"/>
273+ <textInputTraits key="textInputTraits" keyboardType="numberPad"/>
274+ </textField>
275+ <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="_" textAlignment="center" adjustsFontSizeToFit="NO" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="vaP-rO-NZW">
276+ <rect key="frame" x="208" y="0.0" width="40" height="58"/>
277+ <nil key="textColor"/>
278+ <fontDescription key="fontDescription" type="system" pointSize="45"/>
279+ <textInputTraits key="textInputTraits" keyboardType="numberPad"/>
280+ </textField>
281+ <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="_" textAlignment="center" adjustsFontSizeToFit="NO" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="qDh-32-HSn">
282+ <rect key="frame" x="260" y="0.0" width="40" height="58"/>
283+ <nil key="textColor"/>
284+ <fontDescription key="fontDescription" type="system" pointSize="45"/>
285+ <textInputTraits key="textInputTraits" keyboardType="numberPad"/>
286+ </textField>
287+ </subviews>
288+ <constraints>
289+ <constraint firstAttribute="height" constant="58" id="5tl-bI-7tm"/>
290+ <constraint firstItem="YKO-0M-KD8" firstAttribute="width" secondItem="744-t1-YNn" secondAttribute="width" id="E7r-rk-4Mb"/>
291+ <constraint firstItem="SmG-Ae-oOs" firstAttribute="width" secondItem="744-t1-YNn" secondAttribute="width" id="Hkd-L6-qkf"/>
292+ <constraint firstItem="qDh-32-HSn" firstAttribute="width" secondItem="744-t1-YNn" secondAttribute="width" id="Koj-e5-Dof"/>
293+ <constraint firstItem="vaP-rO-NZW" firstAttribute="width" secondItem="744-t1-YNn" secondAttribute="width" id="Vzl-ZY-3yI"/>
294+ <constraint firstItem="jcR-jp-s7a" firstAttribute="width" secondItem="744-t1-YNn" secondAttribute="width" id="bDe-cA-CGi"/>
295+ <constraint firstAttribute="width" constant="300" id="gWy-t0-k14"/>
296+ </constraints>
297+ </stackView>
298+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lCU-OF-giE">
299+ <rect key="frame" x="0.0" y="617" width="375" height="50"/>
300+ <subviews>
301+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="AMw-Yp-yMs">
302+ <rect key="frame" x="327" y="8" width="32" height="30"/>
303+ <state key="normal" title="Next"/>
304+ <connections>
305+ <action selector="whereToGoNext:" destination="Lz1-1J-ywG" eventType="touchUpInside" id="1OA-n8-Gky"/>
306+ </connections>
307+ </button>
308+ </subviews>
309+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
310+ <constraints>
311+ <constraint firstAttribute="bottom" secondItem="AMw-Yp-yMs" secondAttribute="bottom" constant="12" id="7qy-qR-6jm"/>
312+ <constraint firstItem="AMw-Yp-yMs" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="lCU-OF-giE" secondAttribute="leading" constant="20" symbolic="YES" id="Bqz-qr-CNW"/>
313+ <constraint firstAttribute="height" constant="50" id="Dyu-H3-S3f"/>
314+ <constraint firstAttribute="trailing" secondItem="AMw-Yp-yMs" secondAttribute="trailing" constant="16" id="N19-bz-XCw"/>
315+ <constraint firstItem="AMw-Yp-yMs" firstAttribute="top" secondItem="lCU-OF-giE" secondAttribute="top" constant="8" id="u6d-3F-1Y5"/>
316+ </constraints>
317+ </view>
318+ </subviews>
319+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
320+ <constraints>
321+ <constraint firstItem="hE6-cQ-NbO" firstAttribute="top" secondItem="xwd-51-foC" secondAttribute="top" constant="27" id="8Gx-SH-Pc0"/>
322+ <constraint firstItem="2Gj-pC-RZ2" firstAttribute="top" secondItem="hE6-cQ-NbO" secondAttribute="bottom" constant="31.5" id="DEY-u8-rFj"/>
323+ <constraint firstItem="lCU-OF-giE" firstAttribute="leading" secondItem="xwd-51-foC" secondAttribute="leading" id="HFt-0X-LMb"/>
324+ <constraint firstItem="lCU-OF-giE" firstAttribute="trailing" secondItem="xwd-51-foC" secondAttribute="trailing" id="Z71-jX-S5Q"/>
325+ <constraint firstItem="2Gj-pC-RZ2" firstAttribute="centerX" secondItem="xwd-51-foC" secondAttribute="centerX" id="aSr-QZ-LuB"/>
326+ <constraint firstItem="hE6-cQ-NbO" firstAttribute="centerX" secondItem="xwd-51-foC" secondAttribute="centerX" id="oFz-CS-SSk"/>
327+ <constraint firstItem="lCU-OF-giE" firstAttribute="bottom" secondItem="xwd-51-foC" secondAttribute="bottom" id="vMF-KP-eIa"/>
328+ </constraints>
329+ <viewLayoutGuide key="safeArea" id="xwd-51-foC"/>
330+ </view>
331+ <connections>
332+ <outlet property="bottomConstraint" destination="vMF-KP-eIa" id="a15-El-XZs"/>
333+ <outlet property="f1" destination="744-t1-YNn" id="kaQ-nt-ubz"/>
334+ <outlet property="f2" destination="jcR-jp-s7a" id="tjD-L6-nis"/>
335+ <outlet property="f3" destination="YKO-0M-KD8" id="QHg-UM-Bta"/>
336+ <outlet property="f4" destination="SmG-Ae-oOs" id="bwy-JB-My7"/>
337+ <outlet property="f5" destination="vaP-rO-NZW" id="rNh-iH-648"/>
338+ <outlet property="f6" destination="qDh-32-HSn" id="h4g-QY-abI"/>
339+ </connections>
340+ </viewController>
341+ <placeholder placeholderIdentifier="IBFirstResponder" id="y6E-3d-hPA" userLabel="First Responder" sceneMemberID="firstResponder"/>
342+ </objects>
343+ <point key="canvasLocation" x="4381.6000000000004" y="1961.4692653673164"/>
344+ </scene>
345+ <!--Register Table View Controller-->
346+ <scene sceneID="h6T-rh-iCS">
347+ <objects>
348+ <tableViewController storyboardIdentifier="RegisterTableViewController" id="gZg-8R-SiJ" customClass="RegisterTableViewController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
349+ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="yDm-gG-Zw0">
350+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
351+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
352+ <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
353+ <prototypes>
354+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="cell111" textLabel="bby-bf-qyX" style="IBUITableViewCellStyleDefault" id="Ybe-Vg-GR0">
355+ <rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
356+ <autoresizingMask key="autoresizingMask"/>
357+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Ybe-Vg-GR0" id="OZG-F0-QqJ">
358+ <rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
359+ <autoresizingMask key="autoresizingMask"/>
360+ <subviews>
361+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Add faculty" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bby-bf-qyX">
362+ <rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
363+ <autoresizingMask key="autoresizingMask"/>
364+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
365+ <nil key="textColor"/>
366+ <nil key="highlightedColor"/>
367+ </label>
368+ </subviews>
369+ </tableViewCellContentView>
370+ </tableViewCell>
371+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="facultyCell" textLabel="8Xf-Sx-eEe" detailTextLabel="Vjg-Il-F07" style="IBUITableViewCellStyleSubtitle" id="xJW-zY-MeC">
372+ <rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
373+ <autoresizingMask key="autoresizingMask"/>
374+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="xJW-zY-MeC" id="hnt-oe-CHh">
375+ <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
376+ <autoresizingMask key="autoresizingMask"/>
377+ <subviews>
378+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Add faculty" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8Xf-Sx-eEe">
379+ <rect key="frame" x="16" y="5" width="87" height="20.5"/>
380+ <autoresizingMask key="autoresizingMask"/>
381+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
382+ <nil key="textColor"/>
383+ <nil key="highlightedColor"/>
384+ </label>
385+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Vjg-Il-F07">
386+ <rect key="frame" x="16" y="25.5" width="44" height="14.5"/>
387+ <autoresizingMask key="autoresizingMask"/>
388+ <fontDescription key="fontDescription" type="system" pointSize="12"/>
389+ <nil key="textColor"/>
390+ <nil key="highlightedColor"/>
391+ </label>
392+ </subviews>
393+ </tableViewCellContentView>
394+ </tableViewCell>
395+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="nameCell" id="VPG-zq-xMh" customClass="InititalsViewCell" customModule="GDproject" customModuleProvider="target">
396+ <rect key="frame" x="0.0" y="143.5" width="375" height="44"/>
397+ <autoresizingMask key="autoresizingMask"/>
398+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VPG-zq-xMh" id="jrO-rB-x0c">
399+ <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
400+ <autoresizingMask key="autoresizingMask"/>
401+ <subviews>
402+ <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="hwQ-St-DAp">
403+ <rect key="frame" x="16" y="7" width="343" height="30"/>
404+ <constraints>
405+ <constraint firstAttribute="height" constant="30" id="QXw-Sf-Hnk"/>
406+ </constraints>
407+ <nil key="textColor"/>
408+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
409+ <textInputTraits key="textInputTraits"/>
410+ </textField>
411+ </subviews>
412+ <constraints>
413+ <constraint firstAttribute="bottom" secondItem="hwQ-St-DAp" secondAttribute="bottom" constant="6.5" id="3Kb-PR-ZPu"/>
414+ <constraint firstItem="hwQ-St-DAp" firstAttribute="top" secondItem="jrO-rB-x0c" secondAttribute="top" constant="7" id="CyQ-kQ-mce"/>
415+ <constraint firstAttribute="trailing" secondItem="hwQ-St-DAp" secondAttribute="trailing" constant="16" id="HKq-mF-8h9"/>
416+ <constraint firstItem="hwQ-St-DAp" firstAttribute="leading" secondItem="jrO-rB-x0c" secondAttribute="leading" constant="16" id="NoW-FW-Q6L"/>
417+ </constraints>
418+ </tableViewCellContentView>
419+ <connections>
420+ <outlet property="initialsTextField" destination="hwQ-St-DAp" id="VU3-m6-FnI"/>
421+ </connections>
422+ </tableViewCell>
423+ </prototypes>
424+ <connections>
425+ <outlet property="dataSource" destination="gZg-8R-SiJ" id="OVm-5H-nyP"/>
426+ <outlet property="delegate" destination="gZg-8R-SiJ" id="khe-nG-0w4"/>
427+ </connections>
428+ </tableView>
429+ </tableViewController>
430+ <placeholder placeholderIdentifier="IBFirstResponder" id="0cS-ao-ZWH" userLabel="First Responder" sceneMemberID="firstResponder"/>
431+ </objects>
432+ <point key="canvasLocation" x="5925.6000000000004" y="2041.529235382309"/>
433+ </scene>
434+ <!--Faculty Table View Controller-->
435+ <scene sceneID="WcK-wN-L0e">
436+ <objects>
437+ <tableViewController storyboardIdentifier="FacultyTableViewController" id="wiz-jF-9HF" customClass="FacultyTableViewController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
438+ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="Iny-a7-zjv">
439+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
440+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
441+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
442+ <prototypes>
443+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="F" textLabel="mSQ-7c-C8A" detailTextLabel="YuF-0p-znr" style="IBUITableViewCellStyleSubtitle" id="Otu-BR-9o9">
444+ <rect key="frame" x="0.0" y="28" width="375" height="44"/>
445+ <autoresizingMask key="autoresizingMask"/>
446+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Otu-BR-9o9" id="2Vm-f8-txH">
447+ <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
448+ <autoresizingMask key="autoresizingMask"/>
449+ <subviews>
450+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="mSQ-7c-C8A">
451+ <rect key="frame" x="16" y="5" width="33.5" height="20.5"/>
452+ <autoresizingMask key="autoresizingMask"/>
453+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
454+ <nil key="textColor"/>
455+ <nil key="highlightedColor"/>
456+ </label>
457+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="YuF-0p-znr">
458+ <rect key="frame" x="16" y="25.5" width="44" height="14.5"/>
459+ <autoresizingMask key="autoresizingMask"/>
460+ <fontDescription key="fontDescription" type="system" pointSize="12"/>
461+ <nil key="textColor"/>
462+ <nil key="highlightedColor"/>
463+ </label>
464+ </subviews>
465+ </tableViewCellContentView>
466+ </tableViewCell>
467+ </prototypes>
468+ <connections>
469+ <outlet property="dataSource" destination="wiz-jF-9HF" id="db0-qR-UJy"/>
470+ <outlet property="delegate" destination="wiz-jF-9HF" id="i3e-Uc-EfR"/>
471+ </connections>
472+ </tableView>
473+ </tableViewController>
474+ <placeholder placeholderIdentifier="IBFirstResponder" id="Kkr-Cr-ZiV" userLabel="First Responder" sceneMemberID="firstResponder"/>
475+ </objects>
476+ <point key="canvasLocation" x="6804" y="2044"/>
477+ </scene>
231478 <!--Simplified Channels List-->
232479 <scene sceneID="gAa-jy-vpG">
233480 <objects>
@@ -379,33 +626,35 @@
379626 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
380627 <subviews>
381628 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aRT-Du-PVE">
382− <rect key="frame" x="0.0" y="55" width="375" height="612"/>
629+ <rect key="frame" x="0.0" y="62" width="375" height="605"/>
383630 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
384631 </view>
385− <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="368-bf-QU6">
386− <rect key="frame" x="0.0" y="20" width="375" height="35"/>
387− <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
632+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bHv-9b-9uA">
633+ <rect key="frame" x="16" y="32" width="343" height="30"/>
388634 <constraints>
389− <constraint firstAttribute="height" constant="35" id="5ER-su-0Yf"/>
635+ <constraint firstAttribute="height" constant="30" id="QPs-3f-AfA"/>
390636 </constraints>
391− </view>
637+ <state key="normal" title="Edit hashtags"/>
638+ <connections>
639+ <action selector="chooseTags:" destination="uED-wX-77j" eventType="touchUpInside" id="jvf-w2-K2f"/>
640+ </connections>
641+ </button>
392642 </subviews>
393643 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
394644 <constraints>
395645 <constraint firstItem="wxD-Ek-mgv" firstAttribute="trailing" secondItem="aRT-Du-PVE" secondAttribute="trailing" id="2ee-e8-G7A"/>
396− <constraint firstItem="368-bf-QU6" firstAttribute="leading" secondItem="wxD-Ek-mgv" secondAttribute="leading" id="9Rl-wh-kZf"/>
646+ <constraint firstItem="aRT-Du-PVE" firstAttribute="top" secondItem="bHv-9b-9uA" secondAttribute="bottom" id="6As-MU-bEh"/>
647+ <constraint firstItem="bHv-9b-9uA" firstAttribute="leading" secondItem="wxD-Ek-mgv" secondAttribute="leading" constant="16" id="OIV-hF-TBs"/>
648+ <constraint firstItem="bHv-9b-9uA" firstAttribute="top" secondItem="wxD-Ek-mgv" secondAttribute="top" constant="12" id="REt-Jv-IfH"/>
397649 <constraint firstItem="aRT-Du-PVE" firstAttribute="leading" secondItem="wxD-Ek-mgv" secondAttribute="leading" id="UBy-Ch-zSL"/>
398− <constraint firstItem="aRT-Du-PVE" firstAttribute="top" secondItem="368-bf-QU6" secondAttribute="bottom" id="WaE-kh-6w3"/>
399− <constraint firstItem="368-bf-QU6" firstAttribute="top" secondItem="wxD-Ek-mgv" secondAttribute="top" id="aI9-JQ-7dq"/>
400− <constraint firstItem="368-bf-QU6" firstAttribute="trailing" secondItem="wxD-Ek-mgv" secondAttribute="trailing" id="haz-0r-Cud"/>
650+ <constraint firstItem="wxD-Ek-mgv" firstAttribute="trailing" secondItem="bHv-9b-9uA" secondAttribute="trailing" constant="16" id="Ufp-mQ-Xeo"/>
401651 <constraint firstItem="wxD-Ek-mgv" firstAttribute="bottom" secondItem="aRT-Du-PVE" secondAttribute="bottom" id="x3f-eb-ACt"/>
402652 </constraints>
403653 <viewLayoutGuide key="safeArea" id="wxD-Ek-mgv"/>
404654 </view>
405655 <connections>
406656 <outlet property="bottomTextViewConstraint" destination="x3f-eb-ACt" id="ktT-im-InH"/>
407− <outlet property="view1" destination="aRT-Du-PVE" id="m3d-kW-vqW"/>
408− <outlet property="viewForTags" destination="368-bf-QU6" id="M5s-RZ-Fq7"/>
657+ <outlet property="view1" destination="aRT-Du-PVE" id="DXD-HR-NcE"/>
409658 </connections>
410659 </viewController>
411660 <placeholder placeholderIdentifier="IBFirstResponder" id="3fd-7N-vPg" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -430,6 +679,96 @@
430679 </objects>
431680 <point key="canvasLocation" x="3444" y="129.98500749625188"/>
432681 </scene>
682+ <!--Taggs Selection View Controller-->
683+ <scene sceneID="74G-Lc-cqT">
684+ <objects>
685+ <viewController storyboardIdentifier="TaggsSelectionViewController" id="rQl-ri-9O8" customClass="TaggsSelectionViewController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
686+ <view key="view" contentMode="scaleToFill" id="k3T-8B-NVf">
687+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
688+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
689+ <subviews>
690+ <containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yeR-J3-ysT">
691+ <rect key="frame" x="16" y="131" width="343" height="142"/>
692+ <constraints>
693+ <constraint firstAttribute="height" constant="142" id="1By-sC-rMT"/>
694+ </constraints>
695+ </containerView>
696+ <containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lEu-rZ-H6U">
697+ <rect key="frame" x="16" y="306" width="343" height="184"/>
698+ <constraints>
699+ <constraint firstAttribute="height" constant="184" id="F0W-3R-hCj"/>
700+ </constraints>
701+ </containerView>
702+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add tags:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uXC-Rm-Hek">
703+ <rect key="frame" x="16" y="28" width="73" height="21"/>
704+ <constraints>
705+ <constraint firstAttribute="height" constant="21" id="KZW-ld-K8d"/>
706+ </constraints>
707+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
708+ <color key="textColor" red="0.43529411759999997" green="0.4431372549" blue="0.47450980390000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
709+ <nil key="highlightedColor"/>
710+ </label>
711+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Suggested tags:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hvS-Ix-A23">
712+ <rect key="frame" x="16" y="106" width="126" height="21"/>
713+ <constraints>
714+ <constraint firstAttribute="height" constant="21" id="ZJb-Aw-bLx"/>
715+ </constraints>
716+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
717+ <color key="textColor" red="0.43529411759999997" green="0.4431372549" blue="0.47450980390000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
718+ <nil key="highlightedColor"/>
719+ </label>
720+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Chosen tags:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lxp-KC-Iua">
721+ <rect key="frame" x="16" y="281" width="101" height="21"/>
722+ <constraints>
723+ <constraint firstAttribute="height" constant="21" id="aT7-BK-i42"/>
724+ </constraints>
725+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
726+ <color key="textColor" red="0.43529411759999997" green="0.4431372549" blue="0.47450980390000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
727+ <nil key="highlightedColor"/>
728+ </label>
729+ <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="BgX-NT-7NG" customClass="TKTextField" customModule="TaggerKit">
730+ <rect key="frame" x="16" y="57" width="343" height="30"/>
731+ <constraints>
732+ <constraint firstAttribute="height" constant="30" id="dBr-rd-Cg8"/>
733+ </constraints>
734+ <nil key="textColor"/>
735+ <fontDescription key="fontDescription" type="system" pointSize="14"/>
736+ <textInputTraits key="textInputTraits"/>
737+ </textField>
738+ </subviews>
739+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
740+ <constraints>
741+ <constraint firstItem="hvS-Ix-A23" firstAttribute="top" secondItem="BgX-NT-7NG" secondAttribute="bottom" constant="19" id="17U-ae-Gxa"/>
742+ <constraint firstItem="Oc2-oW-bST" firstAttribute="trailing" secondItem="yeR-J3-ysT" secondAttribute="trailing" constant="16" id="9gG-QD-LG4"/>
743+ <constraint firstItem="lxp-KC-Iua" firstAttribute="top" secondItem="yeR-J3-ysT" secondAttribute="bottom" constant="8" id="C3R-dM-Fzj"/>
744+ <constraint firstItem="uXC-Rm-Hek" firstAttribute="leading" secondItem="Oc2-oW-bST" secondAttribute="leading" constant="16" id="E6w-kw-7Uo"/>
745+ <constraint firstItem="Oc2-oW-bST" firstAttribute="trailing" secondItem="hvS-Ix-A23" secondAttribute="trailing" constant="233" id="GLM-Fj-UeZ"/>
746+ <constraint firstItem="Oc2-oW-bST" firstAttribute="trailing" secondItem="lxp-KC-Iua" secondAttribute="trailing" constant="258" id="Pbc-Ul-Bqf"/>
747+ <constraint firstItem="Oc2-oW-bST" firstAttribute="trailing" secondItem="uXC-Rm-Hek" secondAttribute="trailing" constant="286" id="Uc1-pS-uKg"/>
748+ <constraint firstItem="lEu-rZ-H6U" firstAttribute="top" secondItem="lxp-KC-Iua" secondAttribute="bottom" constant="4" id="aew-vr-F7L"/>
749+ <constraint firstItem="uXC-Rm-Hek" firstAttribute="top" secondItem="Oc2-oW-bST" secondAttribute="top" constant="8" id="dGW-ps-wNH"/>
750+ <constraint firstItem="lxp-KC-Iua" firstAttribute="leading" secondItem="Oc2-oW-bST" secondAttribute="leading" constant="16" id="gmr-z4-RMQ"/>
751+ <constraint firstItem="BgX-NT-7NG" firstAttribute="top" secondItem="uXC-Rm-Hek" secondAttribute="bottom" constant="8" id="jqp-Lv-waU"/>
752+ <constraint firstItem="yeR-J3-ysT" firstAttribute="top" secondItem="hvS-Ix-A23" secondAttribute="bottom" constant="4" id="nZ3-oH-0AF"/>
753+ <constraint firstItem="Oc2-oW-bST" firstAttribute="trailing" secondItem="BgX-NT-7NG" secondAttribute="trailing" constant="16" id="pPk-Ih-GDO"/>
754+ <constraint firstItem="yeR-J3-ysT" firstAttribute="leading" secondItem="Oc2-oW-bST" secondAttribute="leading" constant="16" id="qBn-xg-Ioc"/>
755+ <constraint firstItem="lEu-rZ-H6U" firstAttribute="leading" secondItem="Oc2-oW-bST" secondAttribute="leading" constant="16" id="qCJ-rc-qBq"/>
756+ <constraint firstItem="Oc2-oW-bST" firstAttribute="trailing" secondItem="lEu-rZ-H6U" secondAttribute="trailing" constant="16" id="vaR-Zr-5nI"/>
757+ <constraint firstItem="hvS-Ix-A23" firstAttribute="leading" secondItem="Oc2-oW-bST" secondAttribute="leading" constant="16" id="vcb-xj-bM8"/>
758+ <constraint firstItem="BgX-NT-7NG" firstAttribute="leading" secondItem="Oc2-oW-bST" secondAttribute="leading" constant="16" id="xOL-J4-JIV"/>
759+ </constraints>
760+ <viewLayoutGuide key="safeArea" id="Oc2-oW-bST"/>
761+ </view>
762+ <connections>
763+ <outlet property="addTagsTextField" destination="BgX-NT-7NG" id="3vt-U9-NYI"/>
764+ <outlet property="searchContainerView" destination="yeR-J3-ysT" id="SzT-fM-A6H"/>
765+ <outlet property="testContainer" destination="lEu-rZ-H6U" id="DTe-9O-VAi"/>
766+ </connections>
767+ </viewController>
768+ <placeholder placeholderIdentifier="IBFirstResponder" id="eoH-1e-A6A" userLabel="First Responder" sceneMemberID="firstResponder"/>
769+ </objects>
770+ <point key="canvasLocation" x="4293.6000000000004" y="132.68365817091455"/>
771+ </scene>
433772 <!--Messages View Controller-->
434773 <scene sceneID="WSW-fr-Vf8">
435774 <objects>
@@ -439,29 +778,53 @@
439778 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
440779 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
441780 <prototypes>
442− <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="MessagesCell" textLabel="9br-gb-mg8" detailTextLabel="Jxd-We-VyC" style="IBUITableViewCellStyleSubtitle" id="67C-fT-WEV">
443− <rect key="frame" x="0.0" y="28" width="375" height="44"/>
781+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="MessageViewCell" rowHeight="78" id="67C-fT-WEV" customClass="MessageViewCell" customModule="GDproject" customModuleProvider="target">
782+ <rect key="frame" x="0.0" y="28" width="375" height="78"/>
444783 <autoresizingMask key="autoresizingMask"/>
445784 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="67C-fT-WEV" id="O3b-Xv-UNk">
446− <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
785+ <rect key="frame" x="0.0" y="0.0" width="375" height="77.5"/>
447786 <autoresizingMask key="autoresizingMask"/>
448787 <subviews>
449− <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="9br-gb-mg8">
450− <rect key="frame" x="16" y="5" width="33.5" height="20.5"/>
451− <autoresizingMask key="autoresizingMask"/>
452− <fontDescription key="fontDescription" type="system" pointSize="17"/>
788+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xAY-IF-RpX">
789+ <rect key="frame" x="229" y="11" width="130" height="15"/>
790+ <constraints>
791+ <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="130" id="f99-ZF-f8h"/>
792+ </constraints>
793+ <fontDescription key="fontDescription" type="system" pointSize="12"/>
794+ <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
795+ <nil key="highlightedColor"/>
796+ </label>
797+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FG3-gU-0r1">
798+ <rect key="frame" x="16" y="11" width="46" height="22"/>
799+ <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="18"/>
453800 <nil key="textColor"/>
454801 <nil key="highlightedColor"/>
455802 </label>
456− <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Jxd-We-VyC">
457− <rect key="frame" x="16" y="25.5" width="33" height="14.5"/>
458− <autoresizingMask key="autoresizingMask"/>
459− <fontDescription key="fontDescription" type="system" pointSize="12"/>
803+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nTy-rK-XTr">
804+ <rect key="frame" x="16" y="49" width="343" height="18"/>
805+ <fontDescription key="fontDescription" type="system" pointSize="15"/>
460806 <nil key="textColor"/>
461807 <nil key="highlightedColor"/>
462808 </label>
463809 </subviews>
810+ <constraints>
811+ <constraint firstItem="nTy-rK-XTr" firstAttribute="top" secondItem="xAY-IF-RpX" secondAttribute="bottom" constant="23" id="09Y-aK-nD9"/>
812+ <constraint firstItem="FG3-gU-0r1" firstAttribute="top" secondItem="O3b-Xv-UNk" secondAttribute="topMargin" id="4Hp-d5-9An"/>
813+ <constraint firstItem="xAY-IF-RpX" firstAttribute="top" secondItem="O3b-Xv-UNk" secondAttribute="topMargin" id="G4f-oJ-7ud"/>
814+ <constraint firstItem="nTy-rK-XTr" firstAttribute="trailing" secondItem="O3b-Xv-UNk" secondAttribute="trailingMargin" id="MaE-VM-KhL"/>
815+ <constraint firstItem="nTy-rK-XTr" firstAttribute="leading" secondItem="O3b-Xv-UNk" secondAttribute="leadingMargin" id="OIX-rY-Leo"/>
816+ <constraint firstItem="nTy-rK-XTr" firstAttribute="top" secondItem="FG3-gU-0r1" secondAttribute="bottom" constant="16" id="Qdj-s3-t60"/>
817+ <constraint firstItem="nTy-rK-XTr" firstAttribute="bottom" secondItem="O3b-Xv-UNk" secondAttribute="bottomMargin" id="Rv3-Mz-Aed"/>
818+ <constraint firstItem="FG3-gU-0r1" firstAttribute="leading" secondItem="O3b-Xv-UNk" secondAttribute="leadingMargin" id="iN1-KG-dYn"/>
819+ <constraint firstItem="xAY-IF-RpX" firstAttribute="trailing" secondItem="O3b-Xv-UNk" secondAttribute="trailingMargin" id="opV-4x-VCk"/>
820+ <constraint firstItem="xAY-IF-RpX" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="FG3-gU-0r1" secondAttribute="trailing" constant="4" id="x9r-sM-t4x"/>
821+ </constraints>
464822 </tableViewCellContentView>
823+ <connections>
824+ <outlet property="dateLabel" destination="xAY-IF-RpX" id="fcz-re-BXk"/>
825+ <outlet property="dialogName" destination="FG3-gU-0r1" id="gd7-ND-wCi"/>
826+ <outlet property="lastMessagePreview" destination="nTy-rK-XTr" id="Cmo-IJ-2r0"/>
827+ </connections>
465828 </tableViewCell>
466829 </prototypes>
467830 <connections>
@@ -472,18 +835,18 @@
472835 </tableViewController>
473836 <placeholder placeholderIdentifier="IBFirstResponder" id="fu3-z4-EY7" userLabel="First Responder" sceneMemberID="firstResponder"/>
474837 </objects>
475− <point key="canvasLocation" x="2798" y="932"/>
838+ <point key="canvasLocation" x="2681" y="918"/>
476839 </scene>
477840 <!--People To Write View Controller-->
478841 <scene sceneID="6Td-bq-Ebf">
479842 <objects>
480843 <tableViewController storyboardIdentifier="PeopleToWriteViewController" id="rUc-Tv-hWE" customClass="PeopleToWriteViewController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
481− <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="PDb-NB-Ey6">
844+ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelectionDuringEditing="YES" allowsMultipleSelection="YES" allowsMultipleSelectionDuringEditing="YES" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="PDb-NB-Ey6">
482845 <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
483846 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
484847 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
485848 <prototypes>
486− <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="peopleToWriteCell" textLabel="e4H-yo-W57" detailTextLabel="QSb-Bd-4gu" style="IBUITableViewCellStyleValue1" id="5ue-Sf-Blf">
849+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="peopleToWriteCell" textLabel="e4H-yo-W57" style="IBUITableViewCellStyleDefault" id="5ue-Sf-Blf">
487850 <rect key="frame" x="0.0" y="28" width="375" height="44"/>
488851 <autoresizingMask key="autoresizingMask"/>
489852 <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="5ue-Sf-Blf" id="8Dr-YE-x7M">
@@ -491,14 +854,7 @@
491854 <autoresizingMask key="autoresizingMask"/>
492855 <subviews>
493856 <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="e4H-yo-W57">
494− <rect key="frame" x="16" y="12" width="33.5" height="20.5"/>
495− <autoresizingMask key="autoresizingMask"/>
496− <fontDescription key="fontDescription" type="system" pointSize="17"/>
497− <nil key="textColor"/>
498− <nil key="highlightedColor"/>
499− </label>
500− <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QSb-Bd-4gu">
501− <rect key="frame" x="315" y="12" width="44" height="20.5"/>
857+ <rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
502858 <autoresizingMask key="autoresizingMask"/>
503859 <fontDescription key="fontDescription" type="system" pointSize="17"/>
504860 <nil key="textColor"/>
@@ -518,124 +874,80 @@
518874 </objects>
519875 <point key="canvasLocation" x="3614" y="938"/>
520876 </scene>
521− <!--Dialog View Controller-->
522− <scene sceneID="9ac-1R-s4m">
877+ <!--Chat Info View Controller-->
878+ <scene sceneID="1br-Xg-fR9">
523879 <objects>
524− <tableViewController storyboardIdentifier="DialogViewController" id="AVr-rR-mka" customClass="DialogViewController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
525− <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">
880+ <tableViewController storyboardIdentifier="ChatInfoViewController" id="Rfd-Hp-Qqv" customClass="ChatInfoViewController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
881+ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Rvg-ok-cnE">
526882 <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
527883 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
528− <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
884+ <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
529885 <prototypes>
530− <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="bgd-9d-SmR">
531− <rect key="frame" x="0.0" y="28" width="375" height="44"/>
886+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="cell" textLabel="EdJ-of-2FG" style="IBUITableViewCellStyleDefault" id="hl5-Uc-R53">
887+ <rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
532888 <autoresizingMask key="autoresizingMask"/>
533− <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="bgd-9d-SmR" id="9W4-bO-4MN">
889+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hl5-Uc-R53" id="Oy5-4e-dPI">
534890 <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
535891 <autoresizingMask key="autoresizingMask"/>
892+ <subviews>
893+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="EdJ-of-2FG">
894+ <rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
895+ <autoresizingMask key="autoresizingMask"/>
896+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
897+ <nil key="textColor"/>
898+ <nil key="highlightedColor"/>
899+ </label>
900+ </subviews>
536901 </tableViewCellContentView>
537902 </tableViewCell>
538903 </prototypes>
904+ <sections/>
539905 <connections>
540− <outlet property="dataSource" destination="AVr-rR-mka" id="quz-3V-jDT"/>
541− <outlet property="delegate" destination="AVr-rR-mka" id="R4y-E9-LVr"/>
906+ <outlet property="dataSource" destination="Rfd-Hp-Qqv" id="ThC-G8-che"/>
907+ <outlet property="delegate" destination="Rfd-Hp-Qqv" id="1WG-zg-Ahf"/>
542908 </connections>
543909 </tableView>
544910 </tableViewController>
545− <placeholder placeholderIdentifier="IBFirstResponder" id="Tzh-O8-O7T" userLabel="First Responder" sceneMemberID="firstResponder"/>
911+ <placeholder placeholderIdentifier="IBFirstResponder" id="KPW-41-EQd" userLabel="First Responder" sceneMemberID="firstResponder"/>
546912 </objects>
547− <point key="canvasLocation" x="4336" y="937"/>
913+ <point key="canvasLocation" x="5057" y="938"/>
548914 </scene>
549− <!--Chat Info View Controller-->
550− <scene sceneID="1br-Xg-fR9">
915+ <!--Tags Suggestion Controller-->
916+ <scene sceneID="gfS-IA-vBj">
551917 <objects>
552− <tableViewController id="Rfd-Hp-Qqv" customClass="ChatInfoViewController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
553− <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Rvg-ok-cnE">
918+ <tableViewController storyboardIdentifier="TagsSuggestionController" id="shX-JH-apo" customClass="TagsSuggestionController" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
919+ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="jTj-mU-lli">
554920 <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
555921 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
556− <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
557− <sections>
558− <tableViewSection headerTitle="Title" id="EwY-Ph-im0">
559− <cells>
560− <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="hl5-Uc-R53">
561− <rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
562− <autoresizingMask key="autoresizingMask"/>
563− <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hl5-Uc-R53" id="Oy5-4e-dPI">
564− <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
565− <autoresizingMask key="autoresizingMask"/>
566− </tableViewCellContentView>
567− </tableViewCell>
568− </cells>
569− </tableViewSection>
570− <tableViewSection headerTitle="Opportunities" id="FhP-ZG-Wz1">
571− <cells>
572− <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="q4L-ZD-zVb" style="IBUITableViewCellStyleDefault" id="lrc-b1-1JB">
573− <rect key="frame" x="0.0" y="155.5" width="375" height="44"/>
574− <autoresizingMask key="autoresizingMask"/>
575− <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lrc-b1-1JB" id="4YN-xu-f78">
576− <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
577− <autoresizingMask key="autoresizingMask"/>
578− <subviews>
579− <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Leave chat" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="q4L-ZD-zVb">
580− <rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
581− <autoresizingMask key="autoresizingMask"/>
582− <fontDescription key="fontDescription" type="system" pointSize="17"/>
583− <color key="textColor" red="1" green="0.14913141730000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
584− <nil key="highlightedColor"/>
585− </label>
586− </subviews>
587− </tableViewCellContentView>
588− </tableViewCell>
589− </cells>
590− </tableViewSection>
591− <tableViewSection headerTitle="Actions" id="oLK-Mz-9rh">
592− <cells>
593− <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="mO7-R4-T0a" style="IBUITableViewCellStyleDefault" id="6N5-8b-glu">
594− <rect key="frame" x="0.0" y="255.5" width="375" height="44"/>
595− <autoresizingMask key="autoresizingMask"/>
596− <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="6N5-8b-glu" id="pKT-jH-Ur5">
597− <rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
598− <autoresizingMask key="autoresizingMask"/>
599− <subviews>
600− <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Show participants" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="mO7-R4-T0a">
601− <rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
602− <autoresizingMask key="autoresizingMask"/>
603− <fontDescription key="fontDescription" type="system" pointSize="17"/>
604− <nil key="textColor"/>
605− <nil key="highlightedColor"/>
606− </label>
607− </subviews>
608− </tableViewCellContentView>
609− </tableViewCell>
610− <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="fXd-wg-rwW" style="IBUITableViewCellStyleDefault" id="VQR-WR-KA7">
611− <rect key="frame" x="0.0" y="299.5" width="375" height="44"/>
612− <autoresizingMask key="autoresizingMask"/>
613− <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VQR-WR-KA7" id="mkm-sp-LKZ">
614− <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
922+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
923+ <prototypes>
924+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="cell" textLabel="Lbu-Z7-fTN" style="IBUITableViewCellStyleDefault" id="pnl-jg-9DL">
925+ <rect key="frame" x="0.0" y="28" width="375" height="44"/>
926+ <autoresizingMask key="autoresizingMask"/>
927+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pnl-jg-9DL" id="Vsm-Ic-k7e">
928+ <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
929+ <autoresizingMask key="autoresizingMask"/>
930+ <subviews>
931+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Lbu-Z7-fTN">
932+ <rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
615933 <autoresizingMask key="autoresizingMask"/>
616− <subviews>
617− <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Add participants" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="fXd-wg-rwW">
618− <rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
619− <autoresizingMask key="autoresizingMask"/>
620− <fontDescription key="fontDescription" type="system" pointSize="17"/>
621− <nil key="textColor"/>
622− <nil key="highlightedColor"/>
623− </label>
624− </subviews>
625− </tableViewCellContentView>
626− </tableViewCell>
627− </cells>
628− </tableViewSection>
629− </sections>
934+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
935+ <nil key="textColor"/>
936+ <nil key="highlightedColor"/>
937+ </label>
938+ </subviews>
939+ </tableViewCellContentView>
940+ </tableViewCell>
941+ </prototypes>
630942 <connections>
631− <outlet property="dataSource" destination="Rfd-Hp-Qqv" id="ThC-G8-che"/>
632− <outlet property="delegate" destination="Rfd-Hp-Qqv" id="1WG-zg-Ahf"/>
943+ <outlet property="dataSource" destination="shX-JH-apo" id="a9b-xm-VIg"/>
944+ <outlet property="delegate" destination="shX-JH-apo" id="Cg4-aC-E7h"/>
633945 </connections>
634946 </tableView>
635947 </tableViewController>
636− <placeholder placeholderIdentifier="IBFirstResponder" id="KPW-41-EQd" userLabel="First Responder" sceneMemberID="firstResponder"/>
948+ <placeholder placeholderIdentifier="IBFirstResponder" id="RsJ-xx-GJW" userLabel="First Responder" sceneMemberID="firstResponder"/>
637949 </objects>
638− <point key="canvasLocation" x="5057" y="938"/>
950+ <point key="canvasLocation" x="5925" y="1339"/>
639951 </scene>
640952 <!--Channel View Controller-->
641953 <scene sceneID="myW-UY-uGB">
GDproject/Constants.swift+8
@@ -59,5 +59,13 @@ let channelViewControllerId = "ChannelViewController"
5959
6060let addToChannelVCId = "AddToChannelVC"
6161
62+let taggsSelectionViewController = "TaggsSelectionViewController"
63+
64+let dialogViewController = "DialogViewController"
65+
66+let tagsSuggestionController = "TagsSuggestionController"
67+
68+let chatInfoViewController = "ChatInfoViewController"
69+
6270let blueSystemColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5)
6371
GDproject/Controller/ News and channels/AddToChannelVC.swift+41−26
@@ -15,35 +15,42 @@ enum DataSourse{
1515class AddToChannelVC: UITableViewController {
1616
1717 var channel: Model.Channels?
18− var dataSourse: DataSourse = .people
1918 weak var update: UpdatableChannel?
2019
2120 // data sources for people and hashtags
2221 var dataSourcePeople: [Model.Users] = Model.Channels.fullPeople
23− var fullTags: [String] = CompletionTree.getCompletion(tree: Model.hashTagTree!, word: "")
24−
2522
2623 var reloadtable: Bool = false {
2724 didSet{
2825 tableView.reloadData()
2926 }
3027 }
28+
29+ var filteredDataSource = [Model.Users]()
30+
31+ func filterContentForSearchText(_ searchText: String, scope: String = "All")
32+ {
33+ filteredDataSource = dataSourcePeople.filter { $0.fullName().lowercased().contains(searchText.lowercased()) }
34+ tableView.reloadData()
35+ }
3136
3237 let searchC = UISearchController(searchResultsController: nil)
3338
3439 override func viewDidLoad() {
3540 super.viewDidLoad()
3641
42+ navigationItem.title = "People"
3743 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
3844 self.navigationItem.searchController = searchC
3945 navigationItem.largeTitleDisplayMode = .never
4046 self.navigationItem.hidesSearchBarWhenScrolling = false
41−
47+ definesPresentationContext = true
48+ searchC.searchResultsUpdater = self
49+ searchC.obscuresBackgroundDuringPresentation = false
4250 }
4351
4452 override func viewWillDisappear(_ animated: Bool) {
4553 super.viewWillDisappear(animated)
46−
4754 update?.updateChannel(with: channel!)
4855 }
4956 // MARK: - Table view data source
@@ -52,12 +59,19 @@ class AddToChannelVC: UITableViewController {
5259 return 1
5360 }
5461
62+ var isFiltering: Bool {
63+ return searchC.isActive && !searchBarIsEmpty()
64+ }
65+
66+ func searchBarIsEmpty() -> Bool {
67+ return searchC.searchBar.text?.isEmpty ?? true
68+ }
69+
5570 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
56− switch dataSourse {
57− case .people:
71+ if isFiltering {
72+ return filteredDataSource.count
73+ } else {
5874 return dataSourcePeople.count
59− default:
60− return fullTags.count
6175 }
6276 }
6377
@@ -65,17 +79,16 @@ class AddToChannelVC: UITableViewController {
6579 {
6680 let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
6781
68− switch dataSourse {
69− case .people:
70− cell.textLabel?.text = dataSourcePeople[indexPath.row].fullName()
71− if channel!.people.contains(dataSourcePeople[indexPath.row].id) {
82+ if isFiltering{
83+ cell.textLabel?.text = filteredDataSource[indexPath.row].fullName()
84+ if channel!.people.contains(filteredDataSource[indexPath.row].id) {
7285 cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)
7386 } else {
7487 cell.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
7588 }
76− default:
77− cell.textLabel?.text = "# \(fullTags[indexPath.row])"
78− if channel!.tags.contains(fullTags[indexPath.row]) {
89+ } else {
90+ cell.textLabel?.text = dataSourcePeople[indexPath.row].fullName()
91+ if channel!.people.contains(dataSourcePeople[indexPath.row].id) {
7992 cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)
8093 } else {
8194 cell.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
@@ -89,29 +102,31 @@ class AddToChannelVC: UITableViewController {
89102
90103 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
91104 if let cell = tableView.cellForRow(at: indexPath) {
92− switch dataSourse {
93− case .people:
105+ if isFiltering{
94106 if cell.backgroundColor == #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) {
95− let filtered = channel!.people.filter{ $0 != dataSourcePeople[indexPath.row].id }
107+ let filtered = channel!.people.filter{ $0 != filteredDataSource[indexPath.row].id }
96108 channel?.people = filtered
97109 cell.backgroundColor = #colorLiteral(red: 0.9999960065, green: 1, blue: 1, alpha: 1)
98110 } else {
99− channel?.people.append(dataSourcePeople[indexPath.row].id)
111+ channel?.people.append(filteredDataSource[indexPath.row].id)
100112 cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)
101113 }
102− default: //tags
114+ } else {
103115 if cell.backgroundColor == #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) {
104− let filtered = channel!.tags.filter{
105− !fullTags[indexPath.row].contains($0)
106− }
107− channel?.tags = filtered
116+ let filtered = channel!.people.filter{ $0 != dataSourcePeople[indexPath.row].id }
117+ channel?.people = filtered
108118 cell.backgroundColor = #colorLiteral(red: 0.9999960065, green: 1, blue: 1, alpha: 1)
109119 } else {
110− channel?.tags.append(fullTags[indexPath.row])
120+ channel?.people.append(dataSourcePeople[indexPath.row].id)
111121 cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)
112122 }
113123 }
114124 }
115125 }
126+}
116127
128+extension AddToChannelVC : UISearchResultsUpdating {
129+ func updateSearchResults(for searchController: UISearchController) {
130+ filterContentForSearchText(searchController.searchBar.text!)
131+ }
117132}
GDproject/Controller/ News and channels/AnonimousChannelController.swiftdeleted−18
@@ -1,18 +0,0 @@
1−//
2−// AnonimousChannelController.swift
3−// GDproject
4−//
5−// Created by cstore on 20/04/2019.
6−// Copyright © 2019 drHSE. All rights reserved.
7−//
8−
9−import UIKit
10−
11−class AnonimousChannelController: UITableViewController {
12−
13− override func viewDidLoad() {
14− super.viewDidLoad()
15− }
16−
17−
18−}
GDproject/Controller/ News and channels/ChannelController.swift+1−1
@@ -73,7 +73,7 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
7373 if let _ = channel?.id { print("update")
7474 Model.updateChannel(with: channel!)
7575 } else { print("create")
76− Model.createChannel(with: channel!)
76+ //Model.createChannel(with: channel!)
7777 }
7878 return
7979 }
GDproject/Controller/ News and channels/ChannelListController.swift+2−2
@@ -30,7 +30,7 @@ class ChannelListController: UITableViewController {
3030 var displayingChannel: Model.Channels?
3131
3232 var toReload: Bool = false {
33− didSet{
33+ didSet {
3434 tableView.reloadData()
3535 }
3636 }
@@ -100,7 +100,7 @@ class ChannelListController: UITableViewController {
100100 @objc func addChannel()
101101 {
102102 // editing mode is on automatically
103− onEditingModeBegins?(Model.Channels(people: [], name: "Untitled", tags: []),IndexPath(row: 1, section: 0))
103+ onEditingModeBegins?(Model.Channels(people: [], name: "Untitled", id: 0, tags: []),IndexPath(row: 1, section: 0))
104104 }
105105
106106 static let generalChannel = Model.Channels(people: [], name: "General", id: -1, tags: [])
GDproject/Controller/ News and channels/ChannelViewController.swift+29−16
@@ -16,7 +16,15 @@ protocol UpdatableChannel: class{
1616 func updateChannel(with channel: Model.Channels)
1717}
1818
19−class ChannelViewController: UITableViewController, UpdatableName, UpdatableChannel {
19+class ChannelViewController: UITableViewController, UpdatableName, UpdatableChannel, TagsReceiver
20+{
21+ var onChoosingHashTags: (([String]?)->())?
22+
23+ var onChoosingPeople: ((Model.Channels?)->())?
24+
25+ func receiveTags(tags: [String]) {
26+ self.channel?.tags = tags
27+ }
2028
2129 func updateName(with name: String){
2230 channel?.name = name
@@ -34,7 +42,7 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan
3442 override func viewDidLoad()
3543 {
3644 super.viewDidLoad()
37−
45+ tableView.keyboardDismissMode = .onDrag
3846 navigationItem.rightBarButtonItems = [ UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(showPreview)), self.editButtonItem]
3947 navigationItem.largeTitleDisplayMode = .never
4048 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
@@ -45,26 +53,32 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan
4553 super.viewWillAppear(animated)
4654 tableView.reloadData()
4755 }
48− // TODO: update channel
56+
4957 override func viewWillDisappear(_ animated: Bool) {
5058
5159 defer {
5260 super.viewWillDisappear(animated)
5361 }
5462
55− guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else {
56− if let _ = channel?.id
63+ guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else
64+ {
65+ if let id = channel?.id, id != 0
5766 {
5867 Model.updateChannel(with: channel!)
5968 } else {
60− Model.createChannel(with: channel!)
69+ Model.createChannel(with: channel!) {
70+ [weak self] in
71+ self?.showAlertOn(result: $0)
72+ return
73+ }
6174 }
6275 return
6376 }
6477 // nou
6578 }
6679
67− @objc func showPreview(){
80+ @objc func showPreview()
81+ {
6882 if let channel = channel {
6983 onShowingPreview?(channel)
7084 }
@@ -139,7 +153,8 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan
139153
140154 override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
141155 {
142− if editingStyle == UITableViewCell.EditingStyle.delete {
156+ if editingStyle == UITableViewCell.EditingStyle.delete
157+ {
143158 if indexPath.section == 1 {
144159 channel?.people.remove(at: indexPath.row-1)
145160 } else {
@@ -160,15 +175,13 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan
160175
161176 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
162177 {
163− if indexPath.row == 0 && indexPath.section != 0
178+ if indexPath.row == 0 && indexPath.section == 1
164179 {
165− let vc = storyboard?.instantiateViewController(withIdentifier: addToChannelVCId) as! AddToChannelVC
166−
167− vc.dataSourse = indexPath.section == 1 ? .people : .tags
168− vc.channel = channel
169− vc.update = self
170−
171− navigationController?.pushViewController(vc, animated: true)
180+ onChoosingPeople?(channel)
181+ }
182+
183+ if indexPath.row == 0 && indexPath.section == 2 {
184+ onChoosingHashTags?(channel?.tags)
172185 }
173186 }
174187}
GDproject/Controller/ News and channels/FullPostController.swift+3−19
@@ -25,27 +25,10 @@ class FullPostController: UITableViewController {
2525
2626 func setUpNavigationBar(){
2727 navigationItem.largeTitleDisplayMode = .never
28− navigationItem.title = "\(post?.authorId ?? 0)"
29− navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(self.options))]
28+ navigationItem.title = "Post"
3029 }
3130
32− @objc func options(){
33− // drafts
34− // saved
35−
36− let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
37− let editAction = UIAlertAction(title: "Send via message", style: .default)
38− let shareAction = UIAlertAction(title: "Send via project", style: .default)
39− let settingsAction = UIAlertAction(title: "Copy link", style: .default)
40− let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
41−
42− optionMenu.addAction(editAction)
43− optionMenu.addAction(shareAction)
44− optionMenu.addAction(settingsAction)
45− optionMenu.addAction(cancelAction)
46−
47− self.present(optionMenu, animated: true, completion: nil)
48− }
31+
4932 // MARK: - Table view data source
5033
5134 override func numberOfSections(in tableView: UITableView) -> Int {
@@ -102,6 +85,7 @@ class FullPostController: UITableViewController {
10285 cell.onUserDisplay = { [weak self] (id) in
10386 let vc = self?.storyboard!.instantiateViewController(withIdentifier: profileViewController) as! ProfileViewController
10487 vc.idProfile = id
88+
10589 self?.navigationController?.pushViewController(vc, animated: true)
10690 }
10791 default:
GDproject/Controller/ News and channels/NewPostViewController.swift+46−10
@@ -10,20 +10,38 @@ import UIKit
1010import Cartography
1111import Marklight
1212import TinyConstraints
13+import TaggerKit
1314// import WSTagsField
1415
15−class NewPostViewController: UIViewController, UITextViewDelegate {
16+class NewPostViewController: UIViewController, UITextViewDelegate, TagsReceiver
17+{
18+ func receiveTags(tags: [String]) {
19+ currentTags = tags
20+ }
1621
1722 @IBOutlet weak var view1: UIView!
1823
19− @IBOutlet weak var viewForTags: UIView!
24+ var currentTags: [String] = []
25+
26+ // We want the whole experience, let's create two TKCollectionViews
27+ let productTags = TKCollectionView()
28+
29+ @IBAction func chooseTags(_ sender: UIButton) {
30+ let vc = storyboard?.instantiateViewController(withIdentifier: taggsSelectionViewController) as! TaggsSelectionViewController
31+
32+ NewPostViewController.draft = textView.text
33+ vc.receiver = self
34+ vc.currentTags = currentTags
35+
36+ navigationController?.pushViewController(vc, animated: true)
37+ }
2038
2139 // fileprivate let tagsField = WSTagsField()
2240 // Keep strong instance of the `NSTextStorage` subclass
2341 let textStorage = MarklightTextStorage()
2442
2543 static var draft: String = ""
26− static var hashTagsDraft: [String] = []
44+ static var hashTagsDraft: [String] = [ ]
2745
2846 var textView: UITextView!
2947
@@ -54,7 +72,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
5472
5573 setUpMD()
5674 setUpTextView()
57− setUpAccessoryView()
75+ // setUpAccessoryView()
5876 //setUpTagsView()
5977 }
6078
@@ -91,7 +109,8 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
91109 override func viewWillAppear(_ animated: Bool) {
92110 super.viewWillAppear(animated)
93111
94− navigationController?.navigationBar.prefersLargeTitles = false
112+ //navigationController?.navigationBar.prefersLargeTitles = false
113+ navigationItem.largeTitleDisplayMode = .never
95114 navigationItem.title = "New post"
96115 textView.text = NewPostViewController.draft
97116 // tagsField.addTags(NewPostViewController.hashTagsDraft)
@@ -122,7 +141,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
122141 func setUpTextView(){
123142 view.addConstraint(bottomTextViewConstraint)
124143 view1.addSubview(textView)
125− textView.edgesToSuperview(insets: .top(8) + .left(8) + .bottom(40+8) + .right(8))
144+ textView.edgesToSuperview(insets: .top(8) + .left(8) + .bottom(8) + .right(8))
126145
127146 if #available(iOS 11.0, *) {
128147 textView.smartDashesType = .no
@@ -191,7 +210,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
191210 }
192211
193212 func setUpMD(){
194− textStorage.marklightTextProcessor.codeColor = UIColor.orange
213+ textStorage.marklightTextProcessor.codeColor = UIColor.gray
195214 textStorage.marklightTextProcessor.quoteColor = UIColor.darkGray
196215 textStorage.marklightTextProcessor.syntaxColor = UIColor.blue
197216 textStorage.marklightTextProcessor.codeFontName = "Courier"
@@ -213,10 +232,27 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
213232 var indexOfPost = 0
214233 // MARK:- new post
215234 @objc func newPost(){
216− Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: /*tagsField.tags.map { $0.text }*/ [])
235+ self.navigationItem.title = "Sending..."
236+ Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: currentTags) {
237+ [weak self] in
238+
239+ switch $0 {
240+ case .success1, .success:
241+ NewPostViewController.draft = ""
242+ NewPostViewController.hashTagsDraft = []
243+ self?.moveBackToParentVC?()
244+ default:
245+ self?.showAlertOn(result: $0)
246+ self?.navigationItem.title = "New post"
247+ }
248+
249+ }
250+
251+ // NewPostViewController.draft = ""
252+ // NewPostViewController.hashTagsDraft = []
217253 // adding row to uiTableView after adding new post
218254 // myProtocol?.addPost(post: p)
219− moveBackToParentVC?()
255+ // moveBackToParentVC?()
220256 // somewhere here i will be sending server notifications about new post arrival
221257 }
222258
@@ -228,7 +264,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
228264 [weak self]
229265 _ in
230266 NewPostViewController.draft = self?.textView.text ?? ""
231− NewPostViewController.hashTagsDraft = /*self?.tagsField.tags.map { $0.text } ??*/ []
267+ NewPostViewController.hashTagsDraft = self?.currentTags ?? []
232268 self?.moveBackToParentVC?()
233269 }
234270
GDproject/Controller/ News and channels/NewsController.swift+53−14
@@ -12,6 +12,7 @@ import Cartography
1212
1313protocol UpdateableWithChannel: class {
1414 var channel: Model.Channels? { get set }
15+ func updateChannel(on channel: Model.Channels)
1516}
1617
1718protocol UpdateableWithUser: class {
@@ -19,8 +20,14 @@ protocol UpdateableWithUser: class {
1920}
2021// MARK:- Controller with posts and channels availiable.
2122// Search is availiable within every table (posts and channels). Has button-functionality for boths post and chnnels
22−class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWithChannel
23+class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWithChannel, UISearchResultsUpdating
2324{
25+ func updateChannel(on channel: Model.Channels) {
26+ self.channel = channel
27+ news.currChannel = channel
28+ decideWhatChannelDisplay()
29+ }
30+
2431 var changedChannelName: ((String)->())?
2532
2633 @IBOutlet weak var tableView: UITableView!
@@ -31,7 +38,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi
3138 // MARK: - Output -
3239 var onSelectChannel: (() -> Void)?
3340
34− var searchController = UISearchController(searchResultsController: nil)
41+ var searchController: UISearchController?
3542
3643 var news = NewsVC()
3744 var type: HeaderType? = .NEWS
@@ -41,6 +48,13 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi
4148 override func viewDidLoad()
4249 {
4350 super.viewDidLoad()
51+
52+ let updater = TagsSuggestionController()
53+ updater.delegate = self
54+ searchController = UISearchController(searchResultsController: updater)
55+ navigationItem.largeTitleDisplayMode = .always
56+
57+ navigationController?.navigationBar.prefersLargeTitles = true
4458 navigationItem.title = "Loading ..."
4559 tableView.refreshControl = refreshContr
4660 // Configure Refresh Control
@@ -49,8 +63,9 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi
4963 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
5064 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
5165
52− // setUpSearchContr()
66+ setUpSearchContr()
5367
68+ // navigationItem.
5469 news.viewController = self
5570 news.type = type == .NEWS ? .NEWS : type!
5671 news.currChannel = channel
@@ -59,14 +74,15 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi
5974 }
6075
6176 func setUpSearchContr(){
62− searchController.delegate = self
63− searchController.obscuresBackgroundDuringPresentation = false
77+ searchController?.delegate = self
78+ searchController?.obscuresBackgroundDuringPresentation = false
6479 navigationItem.searchController = searchController
6580 definesPresentationContext = true
66− searchController.searchBar.placeholder = "Search anything"
81+ searchController?.searchResultsUpdater = self
82+ searchController?.searchBar.placeholder = "Search tags"
6783 }
6884
69− @objc func refreshPostsData( _ ff: UIRefreshControl){
85+ @objc func refreshPostsData( _ ff: UIRefreshControl) {
7086 decideWhatChannelDisplay()
7187 }
7288
@@ -76,7 +92,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi
7692
7793 override func viewWillAppear(_ animated: Bool) {
7894 super.viewWillAppear(animated)
79− searchController.isActive = false
95+ searchController?.isActive = false
8096 // TODO:- display something if no posts are availiable
8197
8298 decideWhatChannelDisplay()
@@ -85,16 +101,18 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi
85101 }
86102
87103 func decideWhatChannelDisplay(){
104+
88105 switch type! {
89106 case .NEWS, .NONE:
90107 if let channel = channel, let id = channel.id, id != -1 {
91108
92− Model.getChannel(with: id) { [weak self] in
109+ Model.getAnonymousChannel(by: channel) { [weak self] in
93110 self?.news.dataSourse = $0.posts
94111 self?.news.dictionary = $0.users
95112 self?.refreshContr.endRefreshing()
96113 self?.changedChannelName?(channel.name)
97114 }
115+
98116 } else {
99117 Model.getLast { [weak self] in
100118 self?.news.dataSourse = $0.posts
@@ -104,12 +122,10 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi
104122 }
105123 }
106124 default:
107− if let anonChannel = anonymousChannel {
108− news.dataSourse = anonChannel.posts
109− news.dictionary = anonChannel.users
110− changedChannelName?("Anonymous")
111− }
125+ refreshContr.endRefreshing()
126+ break
112127 }
128+
113129 }
114130
115131 func setUpNavigationItemsforPosts(){
@@ -117,4 +133,27 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi
117133 tableView.dataSource = news
118134 tableView.reloadData()
119135 }
136+
137+ func updateSearchResults(for searchController: UISearchController)
138+ {
139+ let filteredData = CompletionTree.getCompletion(tree: Model.hashTagTree!, word: searchController.searchBar.text?.lowercased() ?? "")
140+
141+ // Apply the filtered results to the search results table.
142+ if let resultsController = searchController.searchResultsController as? TagsSuggestionController {
143+ resultsController.suggestions = filteredData
144+ resultsController.tableView.reloadData()
145+ }
146+ }
147+
148+ func willPresentSearchController(_ searchController: UISearchController) {
149+ searchController.searchResultsController?.view.isHidden = false;
150+ }
151+
152+ func didPresentSearchController(_ searchController: UISearchController) {
153+ searchController.searchResultsController?.view.isHidden = false;
154+ }
155+
156+ func didDismissSearchController(_ searchController: UISearchController) {
157+ searchController.searchBar.text = ""
158+ }
120159}
GDproject/Controller/ News and channels/NewsVC.swift+12−11
@@ -19,7 +19,9 @@ struct PostCellData{
1919
2020 let markdownParser = MarkdownParser(font: UIFont.systemFont(ofSize: 16))
2121 markdownParser.enabledElements = .disabledAutomaticLink
22− markdownParser.code.textBackgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
22+ markdownParser.code.font = UIFont(name: "Menlo", size: 16)
23+ markdownParser.code.textBackgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
24+ markdownParser.code.color = #colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1)
2325
2426 return markdownParser.parse(markdown)
2527 }
@@ -42,6 +44,7 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
4244 }
4345
4446 (viewController as? NewsController)?.tableView.reloadData()
47+ (viewController as? ProfileViewController)?.tableView.reloadData()
4548 }
4649 }
4750
@@ -85,19 +88,17 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
8588 vc.idProfile = id
8689 self?.viewController!.navigationController!.pushViewController(vc, animated: true)
8790 }
91+
92+ cell.onAnonymousChannelDisplay = {
93+ [weak self] in
94+ (self?.viewController as? UpdateableWithChannel)?.updateChannel(on: Model.Channels(people: [], name: $0, id: 0, tags: [$0]))
95+ }
8896 default:
8997 cell.onUserDisplay = { (id) in
9098 print("tapped when profile is opened already \(id)")
9199 }
92100 }
93101
94− cell.onAnonymousChannelDisplay = {
95− [weak self] (tag) in
96− Model.getAnonymousChannel(by: Model.AnonymousChannel(people: [], tags: [tag]),
97− completion: { (tuple) in self?.onChannelDidChange?(tuple) }
98− )
99− }
100−
101102 cell.selectionStyle = .none
102103 return cell
103104 }
@@ -117,13 +118,13 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
117118 // pagination
118119 if indexPath.row == cellDataSourse.count - 1 && prevLast != indexPath.row
119120 {
120− if let ch = currChannel, let id = ch.id, ch.id != -1{
121+ if let ch = currChannel, let id = ch.id, id != -1 {
121122 // check this!
122− Model.getChannel(with: id, on: 10, from: dataSourse.last?.id )
123− { [weak self] in
123+ Model.getAnonymousChannel(by: ch, exclusiveFrom: dataSourse.last?.id) { [weak self] in
124124 self?.dataSourse.append(contentsOf: $0.posts)
125125 $0.users.forEach { self?.dictionary[$0.key] = $0.value }
126126 }
127+
127128 } else {
128129 // check this!
129130 Model.getLast(on: 10, from: dataSourse.last?.id )
GDproject/Controller/ News and channels/PostViewCell.swift+7−3
@@ -8,11 +8,11 @@
88
99import UIKit
1010import Cartography
11−import MarkdownKit
1211import TinyConstraints
1312
1413class PostViewCell: UITableViewCell
1514{
15+
1616 var onUserDisplay: ((Int)->())?
1717
1818 var onAnonymousChannelDisplay: ((String)->())?
@@ -42,6 +42,7 @@ class PostViewCell: UITableViewCell
4242
4343 let shareButton: UIButton = {
4444 let button = UIButton(type: UIButton.ButtonType.detailDisclosure)
45+ button.isHidden = true
4546 return button
4647 }()
4748
@@ -86,7 +87,7 @@ class PostViewCell: UITableViewCell
8687 nameLabel.setTitle("\(post.user?.firstName ?? "") \(post.user?.lastName ?? "")", for: .normal)
8788 nameLabel.addTarget(self, action: #selector(displayProfile), for: .touchUpInside)
8889 if let user = post.user{
89− fullNameLabel.text = "\(user.firstName) \(user.middleName) \(user.lastName)"
90+ fullNameLabel.text = "\(user.faculty.name)"
9091 }
9192 else {
9293 fullNameLabel.text = "\(post.authorId)"
@@ -123,6 +124,7 @@ class PostViewCell: UITableViewCell
123124 let button = UIButton()
124125 button.setTitle("#" + hash, for: .normal)
125126 button.addTarget(self, action: #selector(setAnonymousChannel(on:)), for: .touchUpInside)
127+ // button.addGestureRecognizer(longPressRecognizer)
126128 button.titleLabel?.font = UIFont.monospacedDigitSystemFont(ofSize: 15, weight: .semibold)
127129 button.setTitleColor(#colorLiteral(red: 0, green: 0.4784313725, blue: 1, alpha: 1), for: .normal)
128130 buttons.append(button)
@@ -180,7 +182,9 @@ class PostViewCell: UITableViewCell
180182 }
181183
182184 @objc func displayProfile(){
183− onUserDisplay?(post!.authorId)
185+ if let id = post?.authorId {
186+ onUserDisplay?(id)
187+ }
184188 }
185189
186190 @objc func setAnonymousChannel(on button: UIButton){
GDproject/Controller/ News and channels/TaggsSelectionViewController.swiftnew file+106
@@ -0,0 +1,106 @@
1+//
2+// TaggsSelectionViewController.swift
3+// GDproject
4+//
5+// Created by cstore on 03/05/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+import TaggerKit
11+
12+protocol TagsReceiver: class {
13+ func receiveTags(tags: [String])
14+}
15+
16+class TaggsSelectionViewController: UIViewController {
17+
18+ @IBOutlet weak var addTagsTextField: TKTextField!
19+
20+ @IBOutlet weak var searchContainerView: UIView!
21+
22+ @IBOutlet weak var testContainer: UIView!
23+
24+ // We want the whole experience, let's create two TKCollectionViews
25+ let productTags = TKCollectionView()
26+ let allTags = TKCollectionView()
27+
28+ var currentTags: [String]?
29+ weak var receiver: TagsReceiver?
30+
31+ // MARK: - Lifecycle Methods
32+ override func viewDidLoad() {
33+ super.viewDidLoad()
34+
35+ navigationItem.title = "Hashtags"
36+ addTagsTextField.delegate = self
37+ // Customisation example
38+ // testCollection.customFont = UIFont.boldSystemFont(ofSize: 14) // Custom font
39+ // testCollection.customCornerRadius = 14.0 // Corner radius of tags
40+ // testCollection.customSpacing = 20.0 // Spacing between cells
41+ // testCollection.customBackgroundColor = UIColor.red // Background of cells
42+
43+
44+ // These are the tags already added by the user, give an aray of strings to the collection
45+ if let tags = currentTags {
46+ productTags.tags = tags
47+ }
48+ // These are intended to be all the tags the user has added in the app, which are going to be filtered
49+ allTags.tags = CompletionTree.getCompletion(tree: Model.hashTagTree!, word: "")
50+
51+ /*
52+ We set this collection's action to .removeTag,
53+ becasue these are supposed to be the tags the user has already added
54+ */
55+ productTags.action = .removeTag
56+
57+
58+ // Set the current controller as the delegate of both collections
59+ productTags.delegate = self
60+ allTags.delegate = self
61+
62+ // "testCollection" takes the tags sent by "searchCollection"
63+ allTags.receiver = productTags
64+
65+ // The tags in "searchCollection" are going to be added, so we set the action to addTag
66+ allTags.action = .addTag
67+
68+
69+ // Set the sender and receiver of the TextField
70+ addTagsTextField.sender = allTags
71+ addTagsTextField.receiver = productTags
72+
73+ add(productTags, toView: testContainer)
74+ add(allTags, toView: searchContainerView)
75+ }
76+
77+ override func viewWillDisappear(_ animated: Bool) {
78+ super.viewWillDisappear(animated)
79+ if let receiver = receiver{
80+ receiver.receiveTags(tags: productTags.tags)
81+ }
82+ }
83+
84+ /*
85+ These methods come from UIViewController now conforming to TKCollectionViewDelegate,
86+ You use these to do whatever you want when a tag is added or removed (e.g. save to file, etc)
87+ */
88+ override func tagIsBeingAdded(name: String?) {
89+ // Example: save testCollection.tags to UserDefault
90+ print("added \(name!)")
91+ }
92+
93+ override func tagIsBeingRemoved(name: String?) {
94+ print("removed \(name!)")
95+ }
96+}
97+
98+// textField deletage to close keyboard on return
99+extension TaggsSelectionViewController: UITextFieldDelegate
100+{
101+ func textFieldShouldReturn(_ textField: UITextField) -> Bool
102+ {
103+ self.view.endEditing(true)
104+ return false
105+ }
106+}
GDproject/Controller/ News and channels/TagsSuggestionController.swiftnew file+52
@@ -0,0 +1,52 @@
1+//
2+// AnonimousChannelController.swift
3+// GDproject
4+//
5+// Created by cstore on 20/04/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class TagsSuggestionController: UITableViewController
12+{
13+ weak var delegate: UpdateableWithChannel?
14+
15+ var suggestions = [String]()
16+ var channel: Model.Channels?
17+
18+ override func viewDidLoad()
19+ {
20+ super.viewDidLoad()
21+ if let tree = Model.hashTagTree {
22+ suggestions = CompletionTree.getCompletion(tree: tree, word: "")
23+ print(suggestions)
24+ }
25+ tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
26+ tableView.reloadData()
27+ }
28+
29+ override func numberOfSections(in tableView: UITableView) -> Int {
30+ return 1
31+ }
32+
33+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
34+ return suggestions.count
35+ }
36+
37+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
38+ {
39+ let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
40+ cell.textLabel?.text = suggestions[indexPath.row]
41+
42+ return cell
43+ }
44+
45+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
46+ {
47+ channel = Model.Channels(people: [], name: "\(self.suggestions[indexPath.row])", id: 0, tags: [self.suggestions[indexPath.row]])
48+
49+ delegate?.updateChannel(on: channel!)
50+ self.dismiss(animated: true)
51+ }
52+}
GDproject/Controller/Coordinators/ChannelsCoordinator.swift+20
@@ -55,6 +55,26 @@ class ChannelsCoordinator: BaseCoordinator{
5555 let vc = self.presentNewsController(with: ch, previewMode: true)
5656 mainContentVC?.navigationController?.pushViewController(vc, animated: true)
5757 }
58+
59+ // to go for choosing hashtags
60+ mainContentVC.onChoosingHashTags = { [weak mainContentVC, unowned self] ch in
61+ let vc = self.storyboard.instantiateViewController(withIdentifier: taggsSelectionViewController) as! TaggsSelectionViewController
62+
63+ vc.currentTags = ch
64+ vc.receiver = mainContentVC
65+
66+ self.navigationController?.pushViewController(vc, animated: true)
67+ }
68+
69+ mainContentVC.onChoosingPeople = { [weak mainContentVC, unowned self] channel in
70+ let vc = self.storyboard.instantiateViewController(withIdentifier: addToChannelVCId) as! AddToChannelVC
71+
72+ vc.channel = channel
73+ vc.update = mainContentVC
74+
75+ self.navigationController?.pushViewController(vc, animated: true)
76+ }
77+
5878 mainContentVC.channel = channel
5979 return mainContentVC
6080 }
GDproject/Controller/Coordinators/LogInCoordinator.swift+37−8
@@ -10,6 +10,8 @@ import UIKit
1010
1111class LogInCoordinator: BaseCoordinator {
1212
13+ let storyboard = UIStoryboard(name: "Main", bundle: nil)
14+
1315 var didEndFlow: (()->())?
1416 var window: UIWindow!
1517 var navigationController: UINavigationController?
@@ -22,20 +24,47 @@ class LogInCoordinator: BaseCoordinator {
2224
2325 override func start(){
2426 let logInVC = LogInViewController()
25− logInVC.authenticate = { [weak self] (id) in
27+ logInVC.authenticate = { [weak self, weak logInVC] (email) in
2628
27− Model.authenticate(with: id) {
28− (res) in
29+ DataStorage.standard.setEmail(email: email)
30+ Model.authenticate(with: email) { [weak self]
31+ (authStatus) in
32+
33+ print(authStatus.userStatus)
2934
30− if (res) {
31− DataStorage.standard.setUserKey(with: id)
32− self?.didEndFlow?()
35+ if (authStatus.userStatus == "invalid") {
36+ logInVC?.presentAlertInvalidCode()
3337 }
34− else {
35− DataStorage.standard.setUserKey(with: 0)
38+ else if (authStatus.userStatus == "canRegister") { // register form
39+ self?.presentRegisterVC()
40+ } else { // validation code
41+ self?.presentViewControllerForCodeInput()
3642 }
3743 }
3844 }
45+
3946 navigationController?.pushViewController(logInVC, animated: false)
4047 }
48+
49+ func presentViewControllerForCodeInput()
50+ {
51+ let vc = storyboard.instantiateViewController(withIdentifier: "CodeViewController") as! CodeViewController
52+ self.navigationController?.pushViewController(vc, animated: true)
53+ vc.onSuccessLogIn = {
54+ [weak self] in self?.didEndFlow?()
55+ }
56+ }
57+
58+ func presentRegisterVC(){
59+
60+ let vc1 = storyboard.instantiateViewController(withIdentifier: "RegisterTableViewController") as! RegisterTableViewController
61+
62+ vc1.onRegistration = { [weak self] in
63+ self?.presentViewControllerForCodeInput()
64+ }
65+
66+ self.navigationController?.pushViewController(vc1, animated: true)
67+ }
68+
69+
4170}
GDproject/Controller/Coordinators/MessagesCoordinator.swift+45−3
@@ -32,14 +32,56 @@ class MessagesCoordinator: BaseCoordinator {
3232 vc.onUserDisplayList = { [weak vc, unowned self] in
3333
3434 let newVC = self.storyboard.instantiateViewController(withIdentifier: peopleToWriteVC) as! PeopleToWriteViewController
35+
36+ newVC.whatToDoWithSelection = { [weak newVC, weak self] people in
37+ newVC?.navigationController?.popViewController(animated: true)
38+
39+ // detect is it a user or group dialog
40+ let count = people.count
41+ if count == 1 {
42+
43+ let user = people.first!.key
44+ let createdDialog = Model.Dialog.userChat(Model.UserChat(user: user))
45+
46+ let vc = DialogViewController()
47+ vc.users = Model.Channels.fullPeopleDict
48+ vc.dialog = createdDialog
49+ self?.navigationController?.pushViewController(vc, animated: true)
50+
51+ } else {
52+ var group = Model.Group(users: people, name: "Untitled", id: 0)
53+ group.users[DataStorage.standard.getUserId()] = Model.UserPermission(isAdmin: true)
54+
55+ Model.createGroupChat(from: group, completion: { [weak self] id in
56+
57+ let newVC1 = DialogViewController()
58+ group.id = id
59+ newVC1.users = Model.Channels.fullPeopleDict
60+ newVC1.dialog = Model.Dialog.groupChat(Model.GroupChat(group: group))
61+ self?.navigationController?.pushViewController(newVC1, animated: true)
62+ })
63+ }
64+ }
65+
3566 vc?.navigationController?.pushViewController(newVC, animated: true)
3667 }
3768
38− vc.onDialogDisplay = { [weak vc, unowned self] in
39− let newVC = self.storyboard.instantiateViewController(withIdentifier: dialogVC) as! DialogViewController
40− newVC.currentDialog = $0
69+ vc.onDialogDisplay = { [weak vc] in
70+
71+ let newVC = DialogViewController()
72+ newVC.dialog = $0.dialog
73+ newVC.users = $0.users
74+ newVC.onUserDisplay = { [weak self] id in
75+
76+ let vc = self?.storyboard.instantiateViewController(withIdentifier: profileViewController) as! ProfileViewController
77+ vc.idProfile = id
78+ self?.navigationController!.pushViewController(vc, animated: true)
79+
80+ }
81+
4182 vc?.navigationController?.pushViewController(newVC, animated: true)
4283 }
84+
4385 navigationController?.viewControllers = [vc]
4486 }
4587}
GDproject/Controller/Coordinators/ProfileCoordinator.swift+22−16
@@ -27,28 +27,34 @@ class ProfileCoordinator: BaseCoordinator {
2727 let storyboard = UIStoryboard(name: "Main", bundle: nil)
2828 let profile = storyboard.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController
2929
30− profile.logOut = { [weak self] in
31− DataStorage.standard.setIsLoggedIn(value: false, with: 0)
32− self?.didEndSession?()
30+ profile.logOut = {
31+ Model.logout() {
32+ [weak self] in
33+
34+ DataStorage.standard.setIsLoggedIn(value: false, with: 0)
35+ self?.didEndSession?()
36+ }
3337 }
3438
35− profile.onSettings = { [weak self, weak storyboard] in
36− let vc = storyboard?.instantiateViewController(withIdentifier: "SettingsViewController") as! SettingsViewController
37− self?.navigationController?.pushViewController(vc, animated: true)
39+ profile.deleteAllSessions = {
40+ Model.deactivateAll() {
41+ [weak self] in
42+
43+ DataStorage.standard.setIsLoggedIn(value: false, with: 0)
44+ self?.didEndSession?()
45+ }
3846 }
3947
40− profile.onChannelsListToAddAPerson = { [weak self, weak storyboard] (user) in
41− let vc = storyboard?.instantiateViewController(withIdentifier: simplifiedChannelsList) as! SimplifiedChannelsList
42− vc.user = user
48+ profile.onSettings = { [weak self, weak storyboard, weak profile] in
49+
50+ let vc = storyboard?.instantiateViewController(withIdentifier: "RegisterTableViewController") as! RegisterTableViewController
51+ vc.delegate = profile
52+ vc.userActive = profile?.user
4353
44− let transition = CATransition()
45− transition.duration = 0.5
46− transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
47− transition.type = CATransitionType.moveIn
48− transition.subtype = CATransitionSubtype.fromTop
49− self?.navigationController?.view.layer.add(transition, forKey: nil)
50− self?.navigationController?.pushViewController(vc, animated: false)
54+ self?.navigationController?.pushViewController(vc, animated: true)
5155 }
56+
57+
5258 navigationController?.setViewControllers([profile], animated: false)
5359 }
5460}
GDproject/Controller/Log In/CodeViewController.swiftnew file+144
@@ -0,0 +1,144 @@
1+//
2+// CodeViewController.swift
3+// GDproject
4+//
5+// Created by cstore on 10/05/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+import TinyConstraints
11+
12+class CodeViewController: UIViewController {
13+
14+ @IBOutlet weak var f1: UITextField!
15+ @IBOutlet weak var f2: UITextField!
16+ @IBOutlet weak var f3: UITextField!
17+ @IBOutlet weak var f4: UITextField!
18+ @IBOutlet weak var f5: UITextField!
19+ @IBOutlet weak var f6: UITextField!
20+
21+ var loading = UIActivityIndicatorView(style: .gray)
22+
23+ var onSuccessLogIn: (()->())?
24+
25+ override func viewDidLoad() {
26+ super.viewDidLoad()
27+ setUpConstraint()
28+
29+ navigationItem.title = DataStorage.standard.getEmail()
30+
31+ f1.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged)
32+ f2.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged)
33+ f3.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged)
34+ f4.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged)
35+ f5.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged)
36+ f6.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged)
37+ }
38+
39+ override func viewWillDisappear(_ animated: Bool) {
40+ super.viewWillDisappear(animated)
41+
42+ loading.stopAnimating()
43+ }
44+
45+ override func viewWillAppear(_ animated: Bool) {
46+ super.viewWillAppear(animated)
47+
48+ f1.becomeFirstResponder()
49+ }
50+
51+ @objc func textFiledDidChange(textField: UITextField) {
52+ let text = textField.text
53+
54+ // only 1 char
55+ if text?.utf16.count == 1 {
56+ switch textField {
57+ case f1:
58+ f2.becomeFirstResponder()
59+ case f2:
60+ f3.becomeFirstResponder()
61+ case f3:
62+ f4.becomeFirstResponder()
63+ case f4:
64+ f5.becomeFirstResponder()
65+ case f5:
66+ f6.becomeFirstResponder()
67+ case f6:
68+ f6.resignFirstResponder()
69+ default:
70+ break
71+ }
72+ }
73+ }
74+
75+
76+ func presentAlertInvalidData(with text: String)
77+ {
78+ let alert = UIAlertController(title: "Invalid code", message: text, preferredStyle: .alert)
79+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
80+ self.present(alert, animated: true, completion: nil)
81+
82+ loading.stopAnimating()
83+ }
84+
85+ @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
86+
87+ @IBAction func whereToGoNext(_ sender: UIButton)
88+ {
89+ guard let f1t = f1.text, let f2t = f2.text, let f3t = f3.text, let f4t = f4.text, let f5t = f5.text, let f6t = f6.text else {
90+ presentAlertInvalidData(with: "Some fields are missing");
91+ return
92+ }
93+
94+ if f1t.isEmpty || f2t.isEmpty || f3t.isEmpty || f4t.isEmpty || f5t.isEmpty || f6t.isEmpty {
95+ presentAlertInvalidData(with: "Some fields are missing");
96+ return
97+ }
98+
99+ let code = "\(f1.text!)\(f2.text!)\(f3.text!)\(f4.text!)\(f5.text!)\(f6.text!)"
100+ if let codeToInt = Int(code)
101+ {
102+ loading.startAnimating()
103+ Model.verify(with: codeToInt) { [weak self] in
104+ // if everything is okay we can authemticicate
105+ if !$0 {
106+ self?.presentAlertInvalidData(with: "Wrong code. Try again!")
107+ self?.loading.stopAnimating()
108+ return
109+ } else {
110+ // Model.authemticiateMe
111+ Model.authenticateMe { [weak self] (res) in
112+ if res {
113+ self?.onSuccessLogIn?()
114+ }
115+ }
116+ return
117+ }
118+ }
119+ }
120+ }
121+
122+ func setUpConstraint()
123+ {
124+ self.view.addSubview(loading)
125+ loading.centerInSuperview()
126+ // for keyboard notifications
127+ NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillShowNotification, object: nil)
128+
129+ NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillHideNotification, object: nil)
130+ }
131+
132+ @objc func handleKeyboardNotifications(notification: NSNotification){
133+ if let userInfo = notification.userInfo {
134+ // UIKeyboardFrameEndUserInfoKey
135+ let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
136+ bottomConstraint.constant = notification.name == UIResponder.keyboardWillShowNotification ? keyBoardFrame.height - self.view.safeAreaInsets.bottom : 0
137+
138+ UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: {
139+ self.view.layoutIfNeeded()
140+ })
141+ }
142+ }
143+
144+}
GDproject/Controller/Log In/FacultyTableViewController.swiftnew file+88
@@ -0,0 +1,88 @@
1+//
2+// FacultyTableViewController.swift
3+// GDproject
4+//
5+// Created by cstore on 10/05/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+
11+protocol ChosenFactulty: class {
12+ func onChooseFaculty(faculty: Model.Faculty)
13+}
14+
15+class FacultyTableViewController: UITableViewController {
16+
17+ weak var delegate: ChosenFactulty?
18+
19+ var currentFaculties: [Model.Faculty] = []{
20+ didSet {
21+ tableView.reloadData()
22+ }
23+ }
24+
25+ var isFiltering: Bool {
26+ return searchController.isActive && !searchBarIsEmpty()
27+ }
28+
29+ func searchBarIsEmpty() -> Bool {
30+ return searchController.searchBar.text?.isEmpty ?? true
31+ }
32+
33+ var searchController = UISearchController(searchResultsController: nil)
34+
35+ override func viewDidLoad() {
36+ super.viewDidLoad()
37+
38+ self.navigationItem.title = "Faculties"
39+ self.navigationItem.searchController = searchController
40+ self.navigationItem.hidesSearchBarWhenScrolling = false
41+ searchController.obscuresBackgroundDuringPresentation = false
42+
43+ setUpTimer()
44+ }
45+
46+ func setUpTimer(){
47+ Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { (timer) in
48+ DispatchQueue.main.async { [weak self] in
49+
50+ if self!.isFiltering {
51+ Model.searchFaculty(string: (self?.searchController.searchBar.text)!) { [weak self] in
52+ self?.currentFaculties = $0
53+ }
54+ }
55+ }
56+ }
57+ }
58+
59+
60+ // MARK: - Table view data source
61+
62+ override func numberOfSections(in tableView: UITableView) -> Int {
63+ return 1
64+ }
65+
66+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
67+ return currentFaculties.count
68+ }
69+
70+
71+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
72+ {
73+ let cell = tableView.dequeueReusableCell(withIdentifier: "F", for: indexPath)
74+
75+ cell.textLabel?.text = currentFaculties[indexPath.row].name
76+ cell.detailTextLabel?.text = currentFaculties[indexPath.row].campusName
77+ cell.selectionStyle = .none
78+
79+ return cell
80+ }
81+
82+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
83+ delegate?.onChooseFaculty(faculty: currentFaculties[indexPath.row])
84+ searchController.dismiss(animated: true, completion: nil)
85+ self.navigationController?.popViewController(animated: true)
86+ }
87+
88+}
GDproject/Controller/Log In/InititalsViewCell.swiftnew file+23
@@ -0,0 +1,23 @@
1+//
2+// InititalsViewCell.swift
3+// GDproject
4+//
5+// Created by cstore on 10/05/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class InititalsViewCell: UITableViewCell {
12+
13+ @IBOutlet weak var initialsTextField: UITextField!
14+
15+ override func awakeFromNib() {
16+ super.awakeFromNib()
17+ // Initialization code
18+ }
19+
20+ func fill(with name: String){
21+ initialsTextField.text = name
22+ }
23+}
GDproject/Controller/Log In/LogInCoordinator1.swiftdeleted−48
@@ -1,48 +0,0 @@
1−////
2−//// LogInCoordinator.swift
3−//// GDproject
4−////
5−//// Created by cstore on 23/02/2019.
6−//// Copyright © 2019 drHSE. All rights reserved.
7−////
8−//
9−//import UIKit
10−//
11−//class LogInCoordinator{
12−//
13−// private weak var navigationController: UINavigationController?
14−// private var window: UIWindow!
15−// // MARK:- Init
16−// init(navigationController: UINavigationController, window: UIWindow) {
17−// self.navigationController = navigationController
18−// self.window = window
19−// }
20−//
21−// func start(){
22−// showLogInPage()
23−// }
24−//
25−// // MARK: - Private implementation
26−//// private func showStatusPage(){
27−//// let controller = TabbarCoordinator(window: window)
28−//// controller.start()
29−//// }
30−//
31−// private func showLogInPage(){
32−// let controller = UIStoryboard.makeLogIn()
33−//
34−// controller.onLogIn = {
35−// (id) in Model.authenticate(with: id) {
36−// (res) in
37−//
38−// if (res) { DataStorage.standard.setUserKey(with: id) }
39−// else { DataStorage.standard.setUserKey(with: 0) }
40−//
41−// controller.authenticateSucceeded = res
42−// }
43−// }
44−//
45−// navigationController?.pushViewController(controller, animated: false)
46−// }
47−//
48−//}
GDproject/Controller/Log In/LogInViewController.swift+29−4
@@ -14,7 +14,24 @@ import Result
1414
1515class LogInViewController: UIViewController {
1616
17− var authenticate: ((Int)->())?
17+ var loading = UIActivityIndicatorView(style: .gray)
18+
19+ var authenticate: ((String)->())?
20+
21+ func presentAlertInvalidCode()
22+ {
23+ let alert = UIAlertController(title: "Invalid email", message: "Try again. Use @hse.ru mail.", preferredStyle: .alert)
24+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
25+ self.present(alert, animated: true, completion: nil)
26+ mailTextField.text = ""
27+ loading.stopAnimating()
28+ }
29+
30+ override func viewWillDisappear(_ animated: Bool) {
31+ super.viewWillDisappear(animated)
32+
33+ loading.stopAnimating()
34+ }
1835
1936 let logInLabel: UILabel = {
2037 let label = UILabel()
@@ -30,6 +47,8 @@ class LogInViewController: UIViewController {
3047 textField.placeholder = "Mail"
3148 textField.borderStyle = .roundedRect
3249 textField.textColor = .black
50+ textField.autocorrectionType = UITextAutocorrectionType.no
51+ textField.autocapitalizationType = UITextAutocapitalizationType.none
3352 textField.clearButtonMode = .always
3453 return textField
3554 }()
@@ -45,7 +64,8 @@ class LogInViewController: UIViewController {
4564 }()
4665
4766 @objc func handleTap(){
48− authenticate?(Int(mailTextField.text!)!)
67+ loading.startAnimating()
68+ authenticate?(mailTextField.text!)
4969 }
5070
5171 private lazy var keyboardBar = UIView()
@@ -55,6 +75,10 @@ class LogInViewController: UIViewController {
5575 func setUpView(){
5676 // logIn stack with textField and label
5777 view.addSubview(contentView)
78+ view.addSubview(loading)
79+
80+ loading.centerInSuperview()
81+
5882 contentView.edgesToSuperview(excluding: .bottom, insets: .left(16) + .right(16) + .top(80), usingSafeArea: true)
5983 let views = [logInLabel, mailTextField]
6084 contentView.stack(views, axis: .vertical, spacing: 10)
@@ -62,6 +86,7 @@ class LogInViewController: UIViewController {
6286
6387 func configureKeyboard(){
6488
89+ mailTextField.keyboardType = .emailAddress
6590 // configure keyboardBar setUp
6691 view.addSubview(keyboardBar)
6792 keyboardBar.height(50)
@@ -72,14 +97,14 @@ class LogInViewController: UIViewController {
7297 // configure keyboardBar components
7398 keyboardBar.addSubview(logInButton)
7499 logInButton.height(50)
75− logInButton.leftToSuperview(view.leftAnchor, offset: 16, relation: .equal, isActive: true)
100+ logInButton.rightToSuperview(view.rightAnchor, offset: -16, relation: .equal, isActive: true)
76101
77102 }
78103
79104 func logicOfLogInInputValidation() -> ((String?)->()) {
80105
81106 let logic: ((String?)->()) = { [weak self] (someText) in
82− if let text = someText, !text.isEmpty, let _ = Int(text) {
107+ if let text = someText, !text.isEmpty, text.contains("@") {
83108 self?.logInButton.isEnabled = true
84109 } else {
85110 self?.logInButton.isEnabled = false
GDproject/Controller/Log In/RegisterTableViewController.swiftnew file+198
@@ -0,0 +1,198 @@
1+//
2+// RegisterTableViewController.swift
3+// GDproject
4+//
5+// Created by cstore on 10/05/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class RegisterTableViewController: UITableViewController, ChosenFactulty
12+{
13+
14+ func presentAlertInvalidData(message: String)
15+ {
16+ let alert = UIAlertController(title: "Invalid data", message: message, preferredStyle: .alert)
17+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
18+ self.present(alert, animated: true, completion: nil)
19+ }
20+
21+ func onChooseFaculty(faculty: Model.Faculty) {
22+ self.faculty = faculty
23+ tableView.reloadData()
24+ }
25+
26+ weak var delegate: UpdateUser?
27+
28+ var onRegistration: (()->())?
29+
30+ var faculty: Model.Faculty?
31+ var user: Model.NewRegistration = Model.NewRegistration(email: DataStorage.standard.getEmail()!, firstName: nil, middleName: nil, lastName: nil, faculty: nil)
32+
33+ var userActive: Model.Users? {
34+ didSet {
35+ if let userA = userActive {
36+ user.firstName = userA.firstName
37+ user.middleName = userA.middleName
38+ user.lastName = userA.lastName
39+
40+ faculty = userA.faculty
41+ self.updateUserIfCan = true
42+ tableView.reloadData()
43+ }
44+ }
45+ }
46+
47+ var updateUserIfCan: Bool = false
48+
49+ override func viewDidLoad()
50+ {
51+ super.viewDidLoad()
52+
53+ navigationItem.largeTitleDisplayMode = .never
54+ tableView.keyboardDismissMode = .interactive
55+ navigationItem.title = user.email
56+ navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(endRegistration))
57+ }
58+
59+
60+ @objc func endRegistration()
61+ {
62+ let fieldsMissing = "Some fields are missing. Add more information"
63+ updateUser()
64+ guard let faculty = faculty else { presentAlertInvalidData(message: fieldsMissing); return }
65+
66+ user.faculty = faculty.url
67+
68+ guard let name = user.firstName else { presentAlertInvalidData(message: fieldsMissing); return }
69+ guard let middle = user.middleName else { presentAlertInvalidData(message: fieldsMissing); return }
70+ guard let last = user.lastName else { presentAlertInvalidData(message: fieldsMissing); return }
71+
72+ if updateUserIfCan {
73+ guard let delegate = delegate else { return }
74+ guard var userA = userActive else { return }
75+
76+ // updating current
77+ userA.firstName = name
78+ userA.faculty = faculty
79+ userA.middleName = middle
80+ userA.lastName = last
81+
82+ Model.userUpdate(with: user) { [weak self] in
83+
84+ if $0 {
85+ delegate.updateUserObj(with: userA)
86+ self?.navigationController?.popViewController(animated: true)
87+ } else {
88+ self?.presentAlertInvalidData(message: "Something went wrong! Try again")
89+ }
90+
91+ }
92+ } else {
93+
94+ Model.register(object: user)
95+ { [weak self] in
96+ self?.onRegistration?()
97+ }
98+ }
99+ }
100+ // MARK: - Table view data source
101+
102+ override func numberOfSections(in tableView: UITableView) -> Int {
103+ return 2
104+ }
105+
106+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
107+
108+ switch section {
109+ case 0:
110+ return 3
111+ default:
112+ if let _ = faculty {
113+ return 2
114+ } else {
115+ return 1
116+ }
117+ }
118+ }
119+
120+ override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
121+ {
122+ switch section {
123+ case 0:
124+ return "Initials"
125+ default:
126+ return "Faculty"
127+ }
128+ }
129+
130+
131+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
132+ {
133+ switch indexPath.section {
134+ case 0:
135+ let cell = tableView.dequeueReusableCell(withIdentifier: "nameCell", for: indexPath) as! InititalsViewCell
136+ switch indexPath.row{
137+ case 0:
138+ if let name = user.firstName {
139+ cell.fill(with: name)
140+ } else {
141+ cell.initialsTextField.placeholder = "First name"
142+ }
143+ case 1:
144+ if let name = user.middleName {
145+ cell.fill(with: name)
146+ } else {
147+ cell.initialsTextField.placeholder = "Middle name"
148+ }
149+ default:
150+ if let name = user.lastName {
151+ cell.fill(with: name)
152+ } else {
153+ cell.initialsTextField.placeholder = "Last name"
154+ }
155+ }
156+ cell.selectionStyle = .none
157+ return cell
158+ default:
159+ switch indexPath.row {
160+ case 0:
161+ let cell = tableView.dequeueReusableCell(withIdentifier: "cell111", for: indexPath)
162+ cell.selectionStyle = .none
163+ return cell
164+ default:
165+ let cell = tableView.dequeueReusableCell(withIdentifier: "facultyCell", for: indexPath)
166+ cell.textLabel?.text = faculty!.name
167+ cell.detailTextLabel?.text = faculty!.campusName
168+ cell.selectionStyle = .none
169+ return cell
170+ }
171+ }
172+ }
173+
174+
175+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
176+ if indexPath.section == 1 && indexPath.row == 0 {
177+
178+ updateUser()
179+
180+ let vc = storyboard?.instantiateViewController(withIdentifier: "FacultyTableViewController") as! FacultyTableViewController
181+
182+ vc.delegate = self
183+
184+ self.navigationController?.pushViewController(vc, animated: true)
185+ }
186+ }
187+
188+ func updateUser(){
189+ guard let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? InititalsViewCell else {return}
190+ guard let cell1 = tableView.cellForRow(at: IndexPath(row: 1, section: 0)) as? InititalsViewCell else {return}
191+ guard let cell2 = tableView.cellForRow(at: IndexPath(row: 2, section: 0)) as? InititalsViewCell else {return}
192+
193+ user.firstName = cell.initialsTextField.text!
194+ user.middleName = cell1.initialsTextField.text!
195+ user.lastName = cell2.initialsTextField.text!
196+ }
197+
198+}
GDproject/Controller/Messages/ChatInfoViewController.swift+270−56
@@ -8,87 +8,301 @@
88
99import UIKit
1010
11−
1211/// Class for displaying chat info
1312///
1413class ChatInfoViewController: UITableViewController {
1514
15+ enum PersonStatus{
16+ case admin
17+ case ordinary
18+ }
19+
20+ weak var delegate: UpdatableGroup?
21+
22+ var groupChat: Model.Group? {
23+ didSet{
24+ if let groupChat = groupChat {
25+ usersArray = groupChat.users.map { $0.key }
26+ }
27+ }
28+ }
29+
30+ var usersArray: [Int] = [] {
31+ didSet {
32+ tableView.reloadData()
33+ }
34+ }
35+
36+ var users: [Int: Model.Users] = [:]
37+
38+ func canIEditThisChat() -> PersonStatus
39+ {
40+ let myId = UserDefaults.standard.integer(forKey: UserDefaultsKeys.id.rawValue)
41+
42+ if let groupChatUserPermission = groupChat?.users[myId]?.isAdmin {
43+ return groupChatUserPermission ? .admin : .ordinary
44+ }
45+
46+ return .ordinary
47+ }
48+
49+ var myPermissions: PersonStatus = .ordinary
50+
51+
1652
1753 override func viewDidLoad() {
1854 super.viewDidLoad()
1955
20− // Uncomment the following line to preserve selection between presentations
21− // self.clearsSelectionOnViewWillAppear = false
22−
23− // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
24− // self.navigationItem.rightBarButtonItem = self.editButtonItem
56+ tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
57+ myPermissions = canIEditThisChat()
58+ tableView.reloadData()
59+
60+ switch myPermissions {
61+ case .admin:
62+ navigationItem.rightBarButtonItem = self.editButtonItem
63+ default:
64+ return
65+ }
2566 }
2667
2768 // MARK: - Table view data source
2869
2970 override func numberOfSections(in tableView: UITableView) -> Int {
30− // #warning Incomplete implementation, return the number of sections
31− return 0
71+ return 3
3272 }
3373
3474 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
35− // #warning Incomplete implementation, return the number of rows
36− return 0
75+ switch section {
76+ case 0:
77+ return 1
78+ case 1:
79+ return 1
80+ default:
81+ return usersArray.count + (myPermissions == .ordinary ? 0 : 1)
82+ }
3783 }
3884
39− /*
40− override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
41− let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
42−
43− // Configure the cell...
85+
86+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
87+ {
88+ let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
4489
90+ if indexPath.row == 0 {
91+ switch indexPath.section {
92+ case 0:
93+ cell.textLabel?.text = groupChat!.name
94+ cell.selectionStyle = .none
95+ return cell
96+ case 1:
97+ cell.textLabel?.text = "Leave chat"
98+ cell.textLabel?.textColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1)
99+ cell.selectionStyle = .none
100+ return cell
101+ default:
102+ switch myPermissions{
103+ case .admin:
104+ cell.textLabel?.text = "Add participants"
105+ cell.accessoryType = .disclosureIndicator
106+ case .ordinary:
107+ cell.textLabel?.text = name(for: users[usersArray[indexPath.row]])
108+ }
109+ cell.selectionStyle = .none
110+ return cell
111+ }
112+ }
113+
114+ switch myPermissions{
115+ case .admin:
116+ cell.textLabel?.text = name(for: users[usersArray[indexPath.row-1]])
117+ case .ordinary:
118+ cell.textLabel?.text = name(for: users[usersArray[indexPath.row]])
119+ }
120+ cell.selectionStyle = .none
45121 return cell
46122 }
47− */
48−
49− /*
50− // Override to support conditional editing of the table view.
51− override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
52− // Return false if you do not want the specified item to be editable.
53− return true
123+
124+ private func name(for user: Model.Users?) -> String {
125+ if let user = user, let perm = groupChat?.users[user.id]?.isAdmin {
126+ if perm {
127+ return "🤴🏻 \(user.fullName())"
128+ }
129+ return "👤 \(user.fullName())"
130+ }
131+
132+ return "left"
54133 }
55− */
56−
57− /*
58− // Override to support editing the table view.
59− override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
60− if editingStyle == .delete {
61− // Delete the row from the data source
62− tableView.deleteRows(at: [indexPath], with: .fade)
63− } else if editingStyle == .insert {
64− // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
65− }
66− }
67− */
68−
69− /*
70− // Override to support rearranging the table view.
71− override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
72−
134+
135+ override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
136+ {
137+ switch section {
138+ case 0:
139+ return "Title"
140+ case 1:
141+ return "Opportunities"
142+ default:
143+ return "Participants"
144+ }
73145 }
74− */
146+
75147
76− /*
77− // Override to support conditional rearranging of the table view.
78− override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
79− // Return false if you do not want the item to be re-orderable.
148+ func editName(for cell: UITableViewCell){
149+ let alert = UIAlertController(title: "Are you sure?", message: "Title:", preferredStyle: .alert)
150+
151+ let action1 = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
152+
153+ alert.addTextField { [weak self] (tf) in
154+ tf.textColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
155+ tf.text = self?.groupChat?.name
156+ }
157+
158+ let action2 = UIAlertAction(title: "OK", style: .default) { [unowned self] _ in
159+ let name = alert.textFields?.first?.text
160+ cell.textLabel?.text = name
161+ self.groupChat?.name = name!
162+ Model.updateGroupChat(with: self.groupChat!)
163+ self.delegate?.updateGroup(with: self.groupChat!)
164+ }
165+
166+ alert.addAction(action1)
167+ alert.addAction(action2)
168+
169+ present(alert, animated: true, completion: nil)
170+ }
171+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
172+ {
173+ guard let groupChat = groupChat else {
174+ return
175+ }
176+
177+ guard let cell = tableView.cellForRow(at: indexPath) else {
178+ return
179+ }
180+
181+ if indexPath.row == 0 {
182+ switch indexPath.section{
183+ case 0:
184+ switch myPermissions{
185+ case .admin:
186+ editName(for: cell)
187+ default:
188+ break
189+ }
190+ case 1:
191+ Model.leaveGroupChat(id: groupChat.id) {
192+ [weak self] in
193+ self?.navigationController?.popViewController(animated: true)
194+ }
195+ default:
196+ switch myPermissions{
197+ case .admin:
198+ showUserChoiceVC()
199+ default:
200+ showParticipantPage(with: usersArray[indexPath.row])
201+ }
202+ }
203+ }
204+
205+
206+ if indexPath.section == 2 && indexPath.row != 0 {
207+ switch myPermissions{
208+ case .admin:
209+ showParticipantPage(with: usersArray[indexPath.row-1])
210+ default:
211+ showParticipantPage(with: usersArray[indexPath.row])
212+ }
213+ }
214+ }
215+
216+ var onUserDiplay: ((Int)->())?
217+
218+ func showParticipantPage(with user: Int?) {
219+ if let user = user {
220+ onUserDiplay?(user)
221+ }
222+ }
223+
224+ func showUserChoiceVC()
225+ {
226+ let vc = storyboard?.instantiateViewController(withIdentifier: peopleToWriteVC) as! PeopleToWriteViewController
227+
228+ vc.whatToDoWithSelection = { [weak self, weak vc] mapa in
229+
230+ mapa.forEach { self?.users[$0.key] = Model.Channels.fullPeopleDict[$0.key] }
231+ mapa.forEach { self?.groupChat?.users[$0.key] = $0.value }
232+ vc?.navigationController?.popViewController(animated: true)
233+ }
234+
235+ navigationController?.pushViewController(vc, animated: true)
236+ }
237+
238+ override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
239+ {
240+ if editingStyle == UITableViewCell.EditingStyle.delete {
241+ groupChat?.users[usersArray[indexPath.row-1]] = nil
242+ }
243+
244+ tableView.reloadData()
245+ }
246+
247+ override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
248+ if indexPath.row == 0 {
249+ return false
250+ }
251+
80252 return true
81253 }
82− */
83−
84− /*
85− // MARK: - Navigation
86−
87− // In a storyboard-based application, you will often want to do a little preparation before navigation
88− override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
89− // Get the new view controller using segue.destination.
90− // Pass the selected object to the new view controller.
254+
255+ override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
256+ {
257+ let editButton = UITableViewRowAction(style: .normal, title: "Promote") { [unowned self] (rowAction, indexPath) in
258+
259+ self.tableView.beginUpdates()
260+ self.groupChat?.users[self.usersArray[indexPath.row-1]]?.isAdmin = true
261+ self.tableView.reloadRows(at: [indexPath], with: .none)
262+ self.tableView.endUpdates()
263+ }
264+
265+ editButton.backgroundColor = #colorLiteral(red: 0.476841867, green: 0.5048075914, blue: 1, alpha: 1)
266+
267+ let restrictButton = UITableViewRowAction(style: .normal, title: "Restrict") { [unowned self] (rowAction, indexPath) in
268+
269+ self.tableView.beginUpdates()
270+ self.groupChat?.users[self.usersArray[indexPath.row-1]]?.isAdmin = false
271+ self.tableView.reloadRows(at: [indexPath], with: .none)
272+ self.tableView.endUpdates()
273+ }
274+
275+ restrictButton.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
276+
277+ let deleteButton = UITableViewRowAction(style: .normal, title: "Delete") { [unowned self] (action, indexPath) in
278+
279+ self.tableView.beginUpdates()
280+ self.groupChat?.users[self.usersArray[indexPath.row-1]] = nil
281+ self.tableView.deleteRows(at: [indexPath], with: .none)
282+ self.tableView.endUpdates()
283+ }
284+
285+ deleteButton.backgroundColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1)
286+
287+ return [editButton, restrictButton, deleteButton]
288+ }
289+
290+ /// update chatInfo
291+ override func viewWillDisappear(_ animated: Bool) {
292+
293+ defer {
294+ super.viewWillDisappear(animated)
295+ }
296+
297+ guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else {
298+ switch myPermissions {
299+ case .ordinary:
300+ return
301+ case .admin:
302+ Model.updateGroupChat(with: groupChat!)
303+ delegate?.updateGroup(with: groupChat!)
304+ }
305+ return
306+ }
91307 }
92− */
93−
94308}
GDproject/Controller/Messages/Dialog/DialogCell.swiftnew file+132
@@ -0,0 +1,132 @@
1+//
2+// DialogCell.swift
3+// GDproject
4+//
5+// Created by cstore on 05/05/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+import TinyConstraints
11+import MarkdownKit
12+
13+class DialogCell: UITableViewCell {
14+
15+ let nameLabel: UIButton = {
16+ let button = UIButton()
17+ button.setTitleColor(.black, for: .normal)
18+ button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
19+ button.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
20+ return button
21+ }()
22+
23+ @objc func displayProfile()
24+ {
25+ if let id = self.user?.id {
26+ print("diplay")
27+ onUserDisplay?(id)
28+ }
29+ }
30+
31+ var onUserDisplay: ((Int)->())?
32+
33+
34+ let timeLabel: UILabel = {
35+ let label = UILabel()
36+ label.font = UIFont.systemFont(ofSize: 9)
37+ label.textAlignment = NSTextAlignment.right
38+ label.textColor = #colorLiteral(red: 0.4352941176, green: 0.4431372549, blue: 0.4745098039, alpha: 1)
39+ return label
40+ }()
41+
42+ func createTextView(with text: NSAttributedString, _ isSelectable: Bool) -> UITextView
43+ {
44+ let textView = UITextView()
45+ textView.isScrollEnabled = false
46+ textView.isEditable = false
47+ textView.sizeToFit()
48+
49+ if isSelectable {
50+ textView.isSelectable = true
51+ } else {
52+ textView.isUserInteractionEnabled = false
53+ }
54+
55+ textView.attributedText = text
56+ return textView
57+ }
58+
59+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
60+ {
61+ super.init(style: style, reuseIdentifier: reuseIdentifier)
62+ }
63+
64+ required init?(coder aDecoder: NSCoder)
65+ {
66+ fatalError("init(coder:) has not been implemented")
67+ }
68+
69+ var user: Model.Users? {
70+ didSet{
71+ nameLabel.setTitle(user!.fullName(), for: .normal)
72+ }
73+ }
74+
75+ var textView: UITextView!
76+
77+ func fill(with markdownText: NSAttributedString, byUser: Model.Users, when: String)
78+ {
79+ let myId = DataStorage.standard.getUserId()
80+ // important
81+ contentView.subviews.forEach({ $0.removeFromSuperview() })
82+
83+ nameLabel.addTarget(self, action: #selector(displayProfile), for: .touchUpInside)
84+
85+ self.user = byUser
86+ textView = createTextView(with: markdownText, true)
87+ textView.layer.cornerRadius = 10
88+ textView.clipsToBounds = true
89+
90+ self.contentView.addSubview(textView)
91+ self.contentView.addSubview(timeLabel)
92+
93+ timeLabel.text = when.getDate()
94+ textView.width(min: 50, max: self.contentView.bounds.width-70, priority: .required, isActive: true)
95+
96+ if myId == byUser.id
97+ {
98+ layoutMyMessage()
99+ } else {
100+ layOutOtherMessage()
101+ }
102+ }
103+
104+ func layOutOtherMessage(){
105+ self.contentView.addSubview(nameLabel)
106+
107+ nameLabel.setTitle(user!.fullName(), for: .normal)
108+
109+ nameLabel.edgesToSuperview(excluding: .bottom, insets: .top(4) + .left(8))
110+ textView.edgesToSuperview(excluding: [.top, .right] , insets: .left(8) + .bottom(20))
111+ textView.topToBottom(of: nameLabel)
112+
113+ textView.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
114+ textView.textColor = .white
115+ timeLabel.edgesToSuperview(excluding: .top, insets: .right(70) + .left(8))
116+ timeLabel.topToBottom(of: textView, offset: 4)
117+ timeLabel.textAlignment = .left
118+ }
119+
120+ func layoutMyMessage()
121+ {
122+ textView.edgesToSuperview(excluding: .left, insets: .right(8) + .bottom(20) + .top(4))
123+ textView.backgroundColor = UIColor(red:0.08, green:0.49, blue:0.98, alpha:1.0)
124+ textView.textColor = .white
125+ timeLabel.edgesToSuperview(excluding: .top, insets: .left(70) + .right(8))
126+ timeLabel.topToBottom(of: textView, offset: 4)
127+ timeLabel.textAlignment = .right
128+ }
129+}
130+
131+
132+
GDproject/Controller/Messages/Dialog/DialogViewController.swiftnew file+340
@@ -0,0 +1,340 @@
1+//
2+// DialogViewController.swift
3+// GDproject
4+//
5+// Created by cstore on 01/05/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+import TinyConstraints
11+import Marklight
12+
13+protocol UpdatableGroup:class {
14+ func updateGroup(with group: Model.Group)
15+}
16+
17+class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegate, UITableViewDataSource
18+{
19+ var onUserDisplay: ((Int)->())?
20+
21+ var tableView: UITableView = UITableView()
22+
23+ func updateGroup(with group: Model.Group){
24+ self.groupChat?.group = group
25+ setTitleForGroup(groupChat: groupChat!)
26+ }
27+
28+ let cellId = "cell3"
29+ var onInfoShow: (()->())?
30+
31+ var dialog: Model.Dialog? {
32+ didSet{
33+ switch dialog! {
34+ case .groupChat(let chat):
35+ self.groupChat = chat
36+ case .userChat(let chat):
37+ self.userChat = chat
38+ }
39+ }
40+ }
41+
42+ var groupChat: Model.GroupChat?
43+ var userChat: Model.UserChat?
44+
45+ var users: [Int: Model.Users]?
46+
47+ var groupId: Int?
48+
49+ var cellData: [PostCellData] = [] {
50+ didSet {
51+ tableView.reloadData()
52+ canBePaginated = true
53+ }
54+ }
55+
56+ override func viewDidLoad() {
57+ super.viewDidLoad()
58+
59+ setConstraints()
60+ magicForKeyboardChanges()
61+
62+ self.tableView.delegate = self
63+ self.tableView.dataSource = self
64+
65+ tableView.setContentOffset(CGPoint(x: 0, y: 50), animated: false)
66+
67+ navigationItem.largeTitleDisplayMode = .never
68+
69+ tableView.keyboardDismissMode = .onDrag
70+ tableView.separatorStyle = .none
71+ tableView.register(DialogCell.self, forCellReuseIdentifier: cellId)
72+ tableView.transform = CGAffineTransform(scaleX: 1, y: -1)
73+ tableView.contentInsetAdjustmentBehavior = .never
74+ tableView.contentOffset = CGPoint(x: 0, y: 30)
75+
76+ if let groupChat = groupChat {
77+ setTitleForGroup(groupChat: groupChat)
78+ } else if let userChat = userChat{
79+ setTitleForChat(userChat: userChat)
80+ }
81+ }
82+
83+ var messageSendView: UIView = {
84+ let view = UIView()
85+ view.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
86+ return view
87+ }()
88+
89+ var messageTextView: UITextView =
90+ {
91+ let textStorage = MarklightTextStorage()
92+ textStorage.marklightTextProcessor.codeColor = UIColor.orange
93+ textStorage.marklightTextProcessor.quoteColor = UIColor.darkGray
94+ textStorage.marklightTextProcessor.syntaxColor = UIColor.blue
95+ textStorage.marklightTextProcessor.codeFontName = "Courier"
96+ textStorage.marklightTextProcessor.fontTextStyle = UIFont.TextStyle.subheadline.rawValue
97+ textStorage.marklightTextProcessor.hideSyntax = false
98+
99+ let layoutManager = NSLayoutManager()
100+
101+ // Assign the `UITextView`'s `NSLayoutManager` to the `NSTextStorage` subclass
102+ //textStorage.addLayoutManager(textView.layoutManager)
103+ textStorage.addLayoutManager(layoutManager)
104+
105+ let textContainer = NSTextContainer()
106+ layoutManager.addTextContainer(textContainer)
107+
108+ let textView = UITextView(frame: CGRect.zero, textContainer: textContainer)
109+ textView.isEditable = true
110+ textView.isScrollEnabled = true
111+ textView.backgroundColor = .white
112+ return textView
113+ }()
114+
115+ var sendButton: UIButton = {
116+ let button = UIButton()
117+ button.setTitle("Send", for: .normal)
118+ button.setTitleColor(.blue, for: .normal)
119+ button.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
120+ button.addTarget(self, action: #selector(sendMessage), for: .touchUpInside)
121+ return button
122+ }()
123+
124+ @objc func sendMessage()
125+ {
126+ var destination: Model.MessageDestination?
127+
128+ if let group = groupChat {
129+ destination = Model.MessageDestination.groupChatDestination(group.group.id)
130+ } else if let user = userChat {
131+ destination = Model.MessageDestination.userChatDestination(user.user)
132+ }
133+
134+ if let destination = destination
135+ {
136+ Model.sendMessage(message: Model.SendMessage(body: Model.Attachments(markdown: messageTextView.text), destination: destination)) { [unowned self] in
137+
138+ switch $0 {
139+ case .success, .success1:
140+ self.getMessagesNew(for: self.dialog!)
141+ self.messageTextView.text = ""
142+ default:
143+ self.showAlertOn(result: $0)
144+ }
145+ }
146+
147+ prevLast = -1
148+ }
149+ }
150+
151+ var lineView: UIView = {
152+ let view = UIView()
153+ view.backgroundColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
154+ return view
155+ }()
156+
157+ var bottomConstraint: NSLayoutConstraint!
158+
159+ func setConstraints(){
160+ self.view.addSubview(tableView)
161+ self.view.addSubview(messageSendView)
162+
163+ messageSendView.addSubview(messageTextView)
164+ messageSendView.addSubview(sendButton)
165+ messageSendView.addSubview(lineView)
166+
167+ messageSendView.edgesToSuperview(excluding: [.top,.bottom])
168+
169+ bottomConstraint = NSLayoutConstraint(item: messageSendView, attribute: .bottom, relatedBy: .equal, toItem: self.view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0)
170+ view.addConstraint(bottomConstraint)
171+
172+ messageSendView.height(60)
173+
174+ sendButton.edgesToSuperview(excluding: .left)
175+ sendButton.width(60)
176+
177+ lineView.edgesToSuperview(excluding: .bottom)
178+ lineView.height(0.5)
179+
180+ messageTextView.edgesToSuperview(excluding: .right)
181+ messageTextView.rightToLeft(of: sendButton)
182+
183+ tableView.edgesToSuperview(excluding: .bottom)
184+ tableView.bottomToTop(of: messageSendView)
185+
186+ self.view.layoutSubviews()
187+ }
188+
189+ func magicForKeyboardChanges()
190+ {
191+ NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillShowNotification, object: nil)
192+
193+ NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillHideNotification, object: nil)
194+ }
195+
196+ @objc func handleKeyboardNotifications(notification: NSNotification){
197+ if let userInfo = notification.userInfo{
198+ // UIKeyboardFrameEndUserInfoKey
199+
200+ let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
201+
202+ bottomConstraint.constant = notification.name == UIResponder.keyboardWillShowNotification ? -(keyBoardFrame.height) + self.view.safeAreaInsets.bottom : 0
203+
204+ UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: {
205+ self.view.layoutIfNeeded()
206+ }) { (completed) in
207+
208+ }
209+ }
210+ }
211+
212+ func setTitleForChat(userChat: Model.UserChat){
213+ navigationItem.title = "\(users![userChat.user]!.fullName())"
214+ navigationItem.rightBarButtonItem = UIBarButtonItem(title: "👤", style: .plain, target: self, action: #selector(showPersonPage))
215+ }
216+
217+ @objc func showPersonPage(){
218+ if let id = userChat?.user{
219+ onUserDisplay?(id)
220+ }
221+ }
222+
223+ func setTitleForGroup(groupChat: Model.GroupChat){
224+ navigationItem.title = "\(groupChat.group.name)"
225+ navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Info", style: .plain, target: self, action: #selector(moveToInfoVC))
226+ }
227+
228+ // onInfoShow
229+ @objc func moveToInfoVC(){
230+ let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: chatInfoViewController) as! ChatInfoViewController
231+
232+ vc.delegate = self
233+ vc.users = users!
234+ vc.onUserDiplay = onUserDisplay
235+
236+ if let groupChat = groupChat {
237+ vc.groupChat = groupChat.group
238+ }
239+
240+ navigationController?.pushViewController(vc, animated: true)
241+ }
242+
243+ var currentMessagesInChat: [Model.LastMessage] = [] {
244+ didSet
245+ {
246+ cellData = currentMessagesInChat.map { PostCellData(attributedData: PostCellData.create(with: [$0.body])) }
247+ }
248+ }
249+
250+ // MARK: - Table view data source
251+
252+ func numberOfSections(in tableView: UITableView) -> Int {
253+ return 1
254+ }
255+
256+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
257+ return cellData.count
258+ }
259+
260+ override func viewWillAppear(_ animated: Bool)
261+ {
262+ super.viewWillAppear(animated)
263+ tabBarController?.tabBar.isHidden = true
264+
265+ if let dialog = dialog {
266+ getMessagesNew(for: dialog)
267+ }
268+ }
269+
270+ func getMessages(for dialog: Model.Dialog, starting from: Int? = nil)
271+ {
272+ switch dialog {
273+ case .groupChat(let groupChat):
274+ Model.getMessagesFor(typeOfChat: Model.Dialog.groupChat(groupChat), chat: groupChat.group.id, exclusiveFrom: from)
275+ { [weak self] in
276+ self?.currentMessagesInChat.append(contentsOf: $0)
277+ }
278+ case .userChat(let userChat):
279+ Model.getMessagesFor(typeOfChat: Model.Dialog.userChat(userChat), chat: userChat.user, exclusiveFrom: from)
280+ { [weak self] in
281+ self?.currentMessagesInChat.append(contentsOf: $0)
282+ }
283+ }
284+ }
285+
286+
287+ func getMessagesNew(for dialog: Model.Dialog, starting from: Int? = nil)
288+ {
289+ switch dialog {
290+ case .groupChat(let groupChat):
291+ Model.getMessagesFor(typeOfChat: Model.Dialog.groupChat(groupChat), chat: groupChat.group.id, exclusiveFrom: from)
292+ { [weak self] in
293+ self?.currentMessagesInChat = $0
294+ }
295+ case .userChat(let userChat):
296+ Model.getMessagesFor(typeOfChat: Model.Dialog.userChat(userChat), chat: userChat.user, exclusiveFrom: from)
297+ { [weak self] in
298+ self?.currentMessagesInChat = $0
299+ }
300+ }
301+ }
302+
303+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
304+ {
305+ let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! DialogCell
306+
307+ //In cellForRowAtIndexPath
308+ cell.transform = CGAffineTransform(scaleX: 1, y: -1)
309+ cell.selectionStyle = .none
310+
311+
312+ if let user = users?[currentMessagesInChat[indexPath.row].author]
313+ {
314+ cell.fill(with: cellData[indexPath.row].attributedData, byUser: user, when: currentMessagesInChat[indexPath.row].time)
315+
316+ cell.onUserDisplay = onUserDisplay
317+ }
318+
319+ return cell
320+ }
321+
322+ var prevLast = -1
323+ var canBePaginated = false
324+ func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
325+ {
326+ if indexPath.row == cellData.count - 1 && prevLast != indexPath.row && canBePaginated && cellData.count >= 24
327+ {
328+ print("exclusiveFrom \(currentMessagesInChat.last?.id ?? 0)")
329+ if let dialog = dialog
330+ {
331+ getMessages(for: dialog, starting: currentMessagesInChat.last?.id)
332+ }
333+
334+ prevLast = indexPath.row
335+ }
336+ }
337+}
338+
339+
340+
GDproject/Controller/Messages/DialogViewController.swiftdeleted−92
@@ -1,92 +0,0 @@
1−//
2−// DialogViewController.swift
3−// GDproject
4−//
5−// Created by cstore on 01/05/2019.
6−// Copyright © 2019 drHSE. All rights reserved.
7−//
8−
9−import UIKit
10−
11−class DialogViewController: UITableViewController {
12−
13− var onInfoShow: (()->())?
14−
15− var currentDialog: (id: Int, name: String)?
16−
17− override func viewDidLoad() {
18− super.viewDidLoad()
19−
20− if let person = currentDialog {
21− navigationItem.title = person.name
22− }
23− }
24−
25− // MARK: - Table view data source
26−
27− override func numberOfSections(in tableView: UITableView) -> Int {
28− // #warning Incomplete implementation, return the number of sections
29− return 0
30− }
31−
32− override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
33− // #warning Incomplete implementation, return the number of rows
34− return 0
35− }
36−
37− /*
38− override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
39− let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
40−
41− // Configure the cell...
42−
43− return cell
44− }
45− */
46−
47− /*
48− // Override to support conditional editing of the table view.
49− override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
50− // Return false if you do not want the specified item to be editable.
51− return true
52− }
53− */
54−
55− /*
56− // Override to support editing the table view.
57− override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
58− if editingStyle == .delete {
59− // Delete the row from the data source
60− tableView.deleteRows(at: [indexPath], with: .fade)
61− } else if editingStyle == .insert {
62− // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
63− }
64− }
65− */
66−
67− /*
68− // Override to support rearranging the table view.
69− override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
70−
71− }
72− */
73−
74− /*
75− // Override to support conditional rearranging of the table view.
76− override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
77− // Return false if you do not want the item to be re-orderable.
78− return true
79− }
80− */
81−
82− /*
83− // MARK: - Navigation
84−
85− // In a storyboard-based application, you will often want to do a little preparation before navigation
86− override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
87− // Get the new view controller using segue.destination.
88− // Pass the selected object to the new view controller.
89− }
90− */
91−
92−}
GDproject/Controller/Messages/MessageViewCell.swiftnew file+38
@@ -0,0 +1,38 @@
1+//
2+// MessageViewCell.swift
3+// GDproject
4+//
5+// Created by cstore on 13/05/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class MessageViewCell: UITableViewCell {
12+
13+ @IBOutlet weak var dialogName: UILabel!
14+
15+ @IBOutlet weak var lastMessagePreview: UILabel!
16+
17+ @IBOutlet weak var dateLabel: UILabel!
18+
19+ override func awakeFromNib() {
20+ super.awakeFromNib()
21+ // Initialization code
22+ }
23+
24+ func fill(with dialog: Model.Dialog, user: Model.Users? = nil)
25+ {
26+ switch dialog {
27+
28+ case .groupChat(let group):
29+ dialogName.text = group.group.name
30+ lastMessagePreview.text = group.lastMessage?.body.markdown
31+ dateLabel.text = group.lastMessage?.time.getDate()
32+ case .userChat(let userChat):
33+ dialogName.text = "👤 \(user!.fullName())"
34+ lastMessagePreview.text = userChat.lastMessage?.body.markdown
35+ dateLabel.text = userChat.lastMessage?.time.getDate()
36+ }
37+ }
38+}
GDproject/Controller/Messages/MessagesViewController.swift+122−54
@@ -8,26 +8,85 @@
88
99import UIKit
1010
11−class MessagesViewController: UITableViewController {
11+protocol SelectableDialog: class {
12+ func openDialog(with dialog: Model.Dialog)
13+}
1214
15+class MessagesViewController: UITableViewController, UISearchControllerDelegate, UISearchResultsUpdating, SelectableDialog {
16+
17+ func openDialog(with dialog: Model.Dialog) {
18+ onDialogDisplay?((dialog, users))
19+ }
20+
21+ func updateSearchResults(for searchController: UISearchController) {
22+
23+ let filteredData = currentActiveDialogs
24+ let inputText = searchController.searchBar.text ?? ""
25+
26+ let filteredResults = filteredData.filter {
27+
28+ switch $0 {
29+ case .groupChat(let gropuChat):
30+ return gropuChat.group.name.contains(inputText)
31+
32+ case .userChat(let userChat):
33+ let user = users[userChat.user]
34+ guard let u = user else { return false }
35+ return u.fullName().contains(inputText)
36+
37+ }
38+
39+ }
40+
41+ // Apply the filtered results to the search results table.
42+ if let resultsController = searchController.searchResultsController as? SuggestedDialogsTableViewController {
43+ resultsController.users = users
44+ resultsController.currentDialogs = filteredResults
45+ resultsController.tableView.reloadData()
46+ }
47+ }
48+
1349 // curreent Active which can be displayed
14− var currentActiveDialogs: [(id: Int, name: String)] = [(id: Int, name: String)]()
50+ var currentActiveDialogs: [Model.Dialog] = [] {
51+ didSet {
52+ tableView.reloadData()
53+ }
54+ }
55+
56+ func setUpSearchContr(){
57+
58+ searchController?.delegate = self
59+ searchController?.obscuresBackgroundDuringPresentation = true
60+ navigationItem.searchController = searchController
61+ definesPresentationContext = true
62+ searchController?.searchResultsUpdater = self
63+ searchController?.searchBar.placeholder = "Search dialogs"
64+
65+ }
66+
67+ // curreent users
68+ var users: [Int: Model.Users] = [:]
1569
1670 var onUserDisplayList: (()->())?
1771
18− var onDialogDisplay: (((id: Int, name: String))->())?
72+ var onDialogDisplay: (((dialog: Model.Dialog, users: [Int:Model.Users]))->())?
1973
20− let searchC = UISearchController(searchResultsController: nil)
74+ var searchController: UISearchController?
2175
2276 override func viewDidLoad() {
2377 super.viewDidLoad()
24−
78+
79+ let updater = SuggestedDialogsTableViewController()
80+
81+ updater.delegate = self
82+ searchController = UISearchController(searchResultsController: updater)
83+
2584 // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
2685 self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Write", style: .plain, target: self, action: #selector(choosePerson))
2786
2887 self.navigationItem.title = "Messages"
2988 // self.navigationItem.largeTitleDisplayMode = .always
30− self.navigationItem.searchController = searchC
89+ setUpSearchContr()
3190 navigationController?.navigationBar.prefersLargeTitles = true
3291 self.navigationItem.hidesSearchBarWhenScrolling = false
3392 }
@@ -38,11 +97,14 @@ class MessagesViewController: UITableViewController {
3897
3998 override func viewWillAppear(_ animated: Bool) {
4099 super.viewWillAppear(animated)
41−
42− tableView.reloadData()
100+ tabBarController?.tabBar.isHidden = false
101+ Model.getChatAll { [weak self] in
102+ self?.currentActiveDialogs = $0.0
103+ self?.users = $0.1
104+ }
43105 }
106+
44107 // MARK: - Table view data source
45−
46108 override func numberOfSections(in tableView: UITableView) -> Int {
47109 return 1
48110 }
@@ -54,60 +116,66 @@ class MessagesViewController: UITableViewController {
54116
55117 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
56118 {
57− let cell = tableView.dequeueReusableCell(withIdentifier: "MessagesCell", for: indexPath)
119+ let cell = tableView.dequeueReusableCell(withIdentifier: "MessageViewCell", for: indexPath) as! MessageViewCell
120+
121+ switch currentActiveDialogs[indexPath.row].self {
122+ case .groupChat:
123+ cell.fill(with: currentActiveDialogs[indexPath.row])
124+ case .userChat(let userChat):
125+ cell.fill(with: currentActiveDialogs[indexPath.row], user: users[userChat.user])
126+ }
58127
59− cell.textLabel?.text = currentActiveDialogs[indexPath.row].name
60− cell.detailTextLabel?.text = "from \(currentActiveDialogs[indexPath.row].name) some message is displayed here..."
61−
62128 return cell
63129 }
64130
65131 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
66− onDialogDisplay?(currentActiveDialogs[indexPath.row])
132+ let tuple = (currentActiveDialogs[indexPath.row],users)
133+ onDialogDisplay?(tuple)
67134 }
68− /*
69− // Override to support conditional editing of the table view.
70− override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
71− // Return false if you do not want the specified item to be editable.
72− return true
73− }
74− */
75−
76− /*
77− // Override to support editing the table view.
78− override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
79− if editingStyle == .delete {
80− // Delete the row from the data source
81− tableView.deleteRows(at: [indexPath], with: .fade)
82− } else if editingStyle == .insert {
83− // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
84− }
85− }
86− */
135+}
87136
88− /*
89− // Override to support rearranging the table view.
90− override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
91137
138+class SuggestedDialogsTableViewController: UITableViewController {
139+
140+ var idCell = "cell"
141+
142+ var users: [Int: Model.Users] = [:]
143+
144+ weak var delegate: SelectableDialog?
145+
146+ var currentDialogs: [Model.Dialog] = []
147+
148+ override func viewDidLoad() {
149+ super.viewDidLoad()
150+ tableView.register(UITableViewCell.self, forCellReuseIdentifier: idCell)
151+ tableView.reloadData()
92152 }
93− */
94−
95− /*
96− // Override to support conditional rearranging of the table view.
97− override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
98− // Return false if you do not want the item to be re-orderable.
99− return true
153+
154+ override func numberOfSections(in tableView: UITableView) -> Int {
155+ return 1
100156 }
101− */
102−
103− /*
104− // MARK: - Navigation
105−
106− // In a storyboard-based application, you will often want to do a little preparation before navigation
107− override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
108− // Get the new view controller using segue.destination.
109− // Pass the selected object to the new view controller.
157+
158+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
159+ return currentDialogs.count
160+ }
161+
162+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
163+ {
164+ let cell = tableView.dequeueReusableCell(withIdentifier: idCell, for: indexPath)
165+
166+ switch currentDialogs[indexPath.row] {
167+ case .groupChat(let groupChat):
168+ cell.textLabel?.text = groupChat.group.name
169+ case .userChat(let userChat):
170+ cell.textLabel?.text = users[userChat.user]?.fullName()
171+ }
172+
173+ cell.accessoryType = .disclosureIndicator
174+ return cell
175+ }
176+
177+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
178+ delegate?.openDialog(with: currentDialogs[indexPath.row])
179+ // self.dismiss(animated: true)
110180 }
111− */
112−
113181}
GDproject/Controller/Messages/PeopleToWriteViewController.swift+21−62
@@ -10,99 +10,58 @@ import UIKit
1010
1111class PeopleToWriteViewController: UITableViewController {
1212
13− // TODO: - edit button when it's used for selection
13+ // TODO: - edit button when it's used for selection
14+ var whatToDoWithSelection: (([Int: Model.UserPermission])->())?
1415
1516 let searchC = UISearchController(searchResultsController: nil)
1617
17− let users = [(id: 9,name: "Anna Mikhaleva")]
18+ let users = Model.Channels.fullPeople
19+
20+ var chosenUsers: [Int: Model.UserPermission] = [:]
1821
1922 override func viewDidLoad() {
2023 super.viewDidLoad()
2124
25+ tableView.isEditing = true
2226 self.navigationItem.title = "People"
27+
28+ self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(newMessage))
29+
2330 self.navigationItem.largeTitleDisplayMode = .never
2431 self.navigationItem.searchController = searchC
2532 self.navigationItem.hidesSearchBarWhenScrolling = false
2633 }
2734
28− // MARK: - Table view data source
35+ override func setEditing(_ editing: Bool, animated: Bool) {
36+ super.setEditing(true, animated: animated)
37+ }
2938
3039 override func numberOfSections(in tableView: UITableView) -> Int {
31− // #warning Incomplete implementation, return the number of sections
3240 return 1
3341 }
3442
3543 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
36− // #warning Incomplete implementation, return the number of rows
3744 return users.count
3845 }
3946
47+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
48+ chosenUsers[users[indexPath.row].id] = Model.UserPermission(isAdmin: false)
49+ }
4050
4151 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
4252 {
4353 let cell = tableView.dequeueReusableCell(withIdentifier: "peopleToWriteCell", for: indexPath)
4454
45− cell.textLabel?.text = users[indexPath.row].name
46− cell.detailTextLabel?.text = "\(users[indexPath.row].id)"
55+ cell.textLabel?.text = users[indexPath.row].fullName()
4756
4857 return cell
4958 }
50−
51− override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
52− navigationController?.popViewController(animated: true)
53−
54− guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else
59+
60+ @objc func newMessage()
61+ {
62+ if chosenUsers.count != 0
5563 {
56− (navigationController?.viewControllers.last as? MessagesViewController)?.currentActiveDialogs.append(users[indexPath.row])
57− (navigationController?.viewControllers.last as? MessagesViewController)?.onDialogDisplay?(users[indexPath.row])
58− return
64+ whatToDoWithSelection?(chosenUsers)
5965 }
6066 }
61−
62−
63− /*
64− // Override to support conditional editing of the table view.
65− override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
66− // Return false if you do not want the specified item to be editable.
67− return true
68− }
69− */
70−
71− /*
72− // Override to support editing the table view.
73− override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
74− if editingStyle == .delete {
75− // Delete the row from the data source
76− tableView.deleteRows(at: [indexPath], with: .fade)
77− } else if editingStyle == .insert {
78− // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
79− }
80− }
81− */
82−
83− /*
84− // Override to support rearranging the table view.
85− override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
86−
87− }
88− */
89−
90− /*
91− // Override to support conditional rearranging of the table view.
92− override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
93− // Return false if you do not want the item to be re-orderable.
94− return true
95− }
96− */
97−
98− /*
99− // MARK: - Navigation
100−
101− // In a storyboard-based application, you will often want to do a little preparation before navigation
102− override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
103− // Get the new view controller using segue.destination.
104− // Pass the selected object to the new view controller.
105− }
106− */
107−
10867}
GDproject/Controller/Profile/BasicInfoController.swift+16−11
@@ -16,17 +16,20 @@ struct CellData{
1616
1717class BasicInfoController: UIViewController, UITableViewDelegate, UITableViewDataSource {
1818
19− var dataSourse: [CellData] = [
20− CellData(opened: false, title: "Phone", sectionData: ["+7 901 733 01 79"]),
21− CellData(opened: false, title: "Mail", sectionData: ["vbogomazova@edu.hse.ru"]),
22− CellData(opened: false, title: "Research ID", sectionData: ["4567834336789456737"]),
23− CellData(opened: false, title: "Published", sectionData: ["Это будет очень длинное название книги 1, чтобы сделать проверку", "Книга 2", "Книга 3"]),
24− CellData(opened: false, title: "Courses", sectionData: ["Курс 1", "Курс 2"]),
25− CellData(opened: false, title: "Link", sectionData: ["https://wwww.hse.ru"])
26− ]
19+ var userInfo: Model.Users? {
20+ didSet {
21+ if let userInfo = userInfo {
22+ dataSourse = [CellData(opened: false, title: "Contacts", sectionData: [userInfo.email, userInfo.faculty.address]),
23+ CellData(opened: false, title: "Faculty", sectionData: [userInfo.faculty.campusName, userInfo.faculty.name, userInfo.faculty.address, userInfo.faculty.path]),
24+ CellData(opened: false, title: "Interests", sectionData: userInfo.faculty.tags)]
25+ }
26+ }
27+ }
28+
29+ var dataSourse: [CellData] = [ ]
2730
2831 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
29− if (dataSourse[section].opened){
32+ if (dataSourse[section].opened) {
3033 return dataSourse[section].sectionData.count + 1
3134 } else {
3235 return 1
@@ -45,8 +48,10 @@ class BasicInfoController: UIViewController, UITableViewDelegate, UITableViewDat
4548 cell.selectionStyle = .none
4649 return cell
4750 } else {
48− let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
49− cell.textLabel?.text = dataSourse[indexPath.section].sectionData[indexPath.row - 1]
51+ let cell = tableView.dequeueReusableCell(withIdentifier: infoCellId, for: indexPath) as! InfoCell
52+ cell.fill(title: dataSourse[indexPath.section].sectionData[indexPath.row - 1])
53+ cell.accessoryType = .none
54+ cell.selectionStyle = .none
5055 return cell
5156 }
5257 }
GDproject/Controller/Profile/InfoCell.swift+2−1
@@ -16,8 +16,9 @@ class InfoCell: UITableViewCell{
1616 label.font = UIFont.systemFont(ofSize: 16)
1717 label.sizeToFit()
1818 label.isScrollEnabled = false
19− label.isUserInteractionEnabled = false
19+ // label.isUserInteractionEnabled = false
2020 label.isEditable = false
21+ label.dataDetectorTypes = .all
2122 label.textColor = .black
2223 return label
2324 }()
GDproject/Controller/Profile/ProfileViewController.swift+139−55
@@ -9,8 +9,17 @@
99import UIKit
1010import TinyConstraints
1111
12−class ProfileViewController: UIViewController
12+protocol UpdateUser: class {
13+ func updateUserObj(with user: Model.Users)
14+}
15+
16+class ProfileViewController: UIViewController, UpdateUser
1317{
18+ func updateUserObj(with user: Model.Users)
19+ {
20+ self.user = user
21+ Model.Channels.fullPeopleDict[user.id] = user
22+ }
1423
1524 @IBOutlet weak var segmentedControl: UISegmentedControl!
1625
@@ -32,18 +41,31 @@ class ProfileViewController: UIViewController
3241
3342 var logOut: (()->())?
3443
44+ var deleteAllSessions: (()->())?
45+
3546 var onSettings: (()->())?
3647
37− var onChannelsListToAddAPerson: ((Model.Users)->())?
48+ @IBAction func sendMessage(_ sender: UIButton)
49+ {
50+ if let userId = idProfile
51+ {
52+ let createdDialog = Model.Dialog.userChat(Model.UserChat(user: userId))
53+ let vc = DialogViewController()
54+ vc.users = Model.Channels.fullPeopleDict
55+ vc.dialog = createdDialog
56+ self.navigationController?.pushViewController(vc, animated: true)
57+ }
58+ }
3859
39− var protoDictionary: [Int: UIImage] = [9: #imageLiteral(resourceName: "9"), 5051: #imageLiteral(resourceName: "5051"), 69: #imageLiteral(resourceName: "69"), 42: #imageLiteral(resourceName: "42")]
60+ var protoDictionary: [String: UIImage] = ["135213": #imageLiteral(resourceName: "9"), "135288": #imageLiteral(resourceName: "5051"), "22723" : #imageLiteral(resourceName: "69"), "135083": #imageLiteral(resourceName: "42")]
4061
41− func fill(with user: Model.Users){
42− self.facultyLabel.text = "Студент: Факультет Компьютерных Наук"
62+ func fill(with user: Model.Users)
63+ {
64+ self.facultyLabel.text = user.faculty.name
4365 self.nameLabel.text = "\(user.firstName) \(user.middleName)"
4466 self.surnameLabel.text = "\(user.lastName)"
45− self.profileImageView.image = protoDictionary[user.id]?.roundedImage
46− self.placeLabel.text = "📍Москва, Кочновский пр.3"
67+ self.profileImageView.image = protoDictionary[user.faculty.campusCode]?.roundedImage
68+ self.placeLabel.text = "📍\(user.faculty.address)"
4769 if user.id == DataStorage.standard.getUserId() {
4870 newMessageButton.isHidden = true
4971 } else {
@@ -53,44 +75,61 @@ class ProfileViewController: UIViewController
5375
5476 var user: Model.Users? {
5577 didSet {
56− self.fill(with: user!)
57− Model.getPostsForUser(with: user!.id) { [weak self] (posts) in
58− self?.dataSourse = posts
78+ if let user = user {
79+ self.fill(with: user)
80+ navigationItem.title = "\(user.firstName) \(user.lastName)"
81+ } else if let id = idProfile {
82+ Model.getUsers(for: [id]) { [unowned self ] in
83+ self.user = $0[id]
84+ }
5985 }
60− navigationItem.title = "\(user!.firstName) \(user!.lastName)"
6186 }
6287 }
6388
64− var basicInfo = BasicInfoController()
65− var posts = NewsVC()
89+ var channel: Model.Channels? {
90+ didSet {
91+ self.update()
92+ }
93+ }
6694
67− var dataSourse: [Model.Posts]?{
68− didSet{
69−
70− var newPosts: [Model.Posts] = []
71−
72− dataSourse?.forEach({ (post) in
73− newPosts.append(Model.Posts(body: post.body, authorId: post.authorId, id: post.id, user: user!, date: post.updated, tags: post.tags))
74− })
75−
76− self.posts.dataSourse = newPosts
77− tableView.reloadData()
95+ func update()
96+ {
97+ Model.getAnonymousChannel(by: channel!) { [unowned self] in
98+ self.posts.dataSourse = $0.posts
99+ self.posts.dictionary = $0.users
100+ self.user = $0.users[self.idProfile!]
78101 }
79102 }
80103
104+ var basicInfo = BasicInfoController()
105+ var posts = NewsVC()
106+
81107 override func viewDidLoad()
82108 {
83109 super.viewDidLoad()
84− print("hell")
110+ if idProfile == nil {
111+ idProfile = DataStorage.standard.getUserId()
112+ }
113+
85114 posts.viewController = self
86115 posts.type = .NONE
116+ posts.currChannel = channel
117+
118+ posts.onFullPost = {
119+ [weak self] (type,post) in
120+
121+ let vc = self?.storyboard?.instantiateViewController(withIdentifier: fullPostControllerId) as! FullPostController
122+ vc.type = type
123+ vc.post = post
124+ self?.navigationController!.pushViewController(vc, animated: true)
125+ }
87126
88127 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
89128
90129 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
91130
92131 tableView.register(BasicInfoCell.self, forCellReuseIdentifier: basicInfoCellId)
93−
132+
94133 tableView.register(InfoCell.self, forCellReuseIdentifier: infoCellId)
95134
96135 posts.viewController = self
@@ -100,29 +139,27 @@ class ProfileViewController: UIViewController
100139 tableView.reloadData()
101140 }
102141
103− var idProfile: Int?
142+ var idProfile: Int? {
143+ didSet {
144+ channel = Model.Channels(people: [idProfile!], name: "", id: 0, tags: [])
145+ }
146+ }
104147
105148 override func viewWillAppear(_ animated: Bool) {
149+ tabBarController?.tabBar.isHidden = false
106150
107151 if idProfile == nil {
108152 idProfile = DataStorage.standard.getUserId()
109153 }
154+ user = Model.Channels.fullPeopleDict[idProfile!]
110155
111− if let id = idProfile {
112− if let user = Model.idUser[id] {
113− self.user = user
114− } else {
115− Model.getUsers(for: [id]) { [weak self] (dic) in
116− self?.user = dic[id]
117− }
118− }
119− }
156+ update()
120157
121158 setUpNavigarionBar()
122159 }
123160
124161 func setUpNavigarionBar(){
125− navigationController?.navigationBar.prefersLargeTitles = true
162+ // navigationController?.navigationBar.prefersLargeTitles = true
126163 let uibarbutton = UIBarButtonItem(title: "More", style: .plain, target: self, action: #selector(showInformation))
127164 navigationItem.rightBarButtonItems = [uibarbutton]
128165 navigationItem.largeTitleDisplayMode = .always
@@ -132,29 +169,75 @@ class ProfileViewController: UIViewController
132169
133170 let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
134171
135− let channelAction = UIAlertAction(title: "Add to a channel", style: .default){
136− [weak self] (_) in
137− self?.onChannelsListToAddAPerson?(self!.user!)
138− }
139−
140− let settingsAction = UIAlertAction(title: "Setting", style: .default)
141− { [weak self] (_) in
142− self?.onSettings?()
143− }
144−
172+
145173 let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
146174
147− let logoutAction = UIAlertAction(title: "Log out", style: .destructive)
148− { [weak self]
149− (_) in
150− self?.logOut?()
175+
176+ if idProfile == DataStorage.standard.getUserId() {
177+ let logoutAction = UIAlertAction(title: "Log out", style: .destructive)
178+ { [weak self] (_) in
179+
180+ if let logOut = self?.logOut {
181+ logOut()
182+ } else {
183+ Model.logout() {
184+ DataStorage.standard.setIsLoggedIn(value: false, with: 0)
185+ (UIApplication.shared.delegate as! AppDelegate).relaunch()
186+ }
187+ }
188+ }
189+
190+ let deleetAllSessionsAction = UIAlertAction(title: "Delete all sessions", style: .destructive)
191+ { [weak self] _ in
192+
193+ if let delete = self?.deleteAllSessions {
194+ delete()
195+ } else {
196+ Model.deactivateAll() {
197+ DataStorage.standard.setIsLoggedIn(value: false, with: 0)
198+ (UIApplication.shared.delegate as! AppDelegate).relaunch()
199+ }
200+ }
201+ }
202+
203+ let settingsAction = UIAlertAction(title: "Edit profile", style: .default)
204+ { [weak self] (_) in
205+
206+ if let settings = self?.onSettings{
207+ settings()
208+ } else {
209+ let vc = self?.storyboard?.instantiateViewController(withIdentifier: "RegisterTableViewController") as! RegisterTableViewController
210+ vc.delegate = self
211+ vc.userActive = self?.user
212+
213+ self?.navigationController?.pushViewController(vc, animated: true)
214+ }
215+ }
216+
217+ optionMenu.addAction(settingsAction)
218+ optionMenu.addAction(logoutAction)
219+ optionMenu.addAction(deleetAllSessionsAction)
220+ } else {
221+ let channelAction = UIAlertAction(title: "Add to a channel", style: .default)
222+ {
223+ [weak self] (_) in
224+
225+ let vc = self?.storyboard?.instantiateViewController(withIdentifier: simplifiedChannelsList) as! SimplifiedChannelsList
226+ vc.user = self?.user
227+
228+ let transition = CATransition()
229+ transition.duration = 0.5
230+ transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
231+ transition.type = CATransitionType.moveIn
232+ transition.subtype = CATransitionSubtype.fromTop
233+ self?.navigationController?.view.layer.add(transition, forKey: nil)
234+ self?.navigationController?.pushViewController(vc, animated: false)
235+ }
236+
237+ optionMenu.addAction(channelAction)
151238 }
152239
153− optionMenu.addAction(channelAction)
154− optionMenu.addAction(settingsAction)
155− optionMenu.addAction(logoutAction)
156240 optionMenu.addAction(cancelAction)
157−
158241 self.present(optionMenu, animated: true, completion: nil)
159242 }
160243
@@ -184,6 +267,7 @@ class ProfileViewController: UIViewController
184267 func changeToBasicInfo(){
185268 tableView.delegate = basicInfo
186269 tableView.dataSource = basicInfo
270+ basicInfo.userInfo = self.user
187271 tableView.reloadData()
188272 }
189273}
GDproject/DataStorage.swift+8
@@ -37,6 +37,13 @@ class DataStorage{
3737 return UserDefaults.standard.integer(forKey: UserDefaultsKeys.id.rawValue)
3838 }
3939
40+ func setEmail(email: String) {
41+ UserDefaults.standard.set(email, forKey: UserDefaultsKeys.email.rawValue)
42+ }
43+
44+ func getEmail() -> String? {
45+ return UserDefaults.standard.string(forKey: UserDefaultsKeys.email.rawValue)
46+ }
4047 /**
4148 Function to determine is user logged in already or not
4249 */
@@ -49,4 +56,5 @@ class DataStorage{
4956enum UserDefaultsKeys: String{
5057 case loggedIn
5158 case id
59+ case email
5260}
GDproject/Extentions.swift+47
@@ -60,3 +60,50 @@ extension UIStoryboard {
6060 return UIStoryboard.userEdit.instantiateViewController(withIdentifier: logInController) as! LogInViewController
6161 }
6262}
63+
64+
65+extension String {
66+ func getDate() -> String
67+ {
68+ let dateFormatter = DateFormatter()
69+ dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"//this your string date format
70+ dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
71+ let date = dateFormatter.date(from: self)
72+
73+ dateFormatter.dateFormat = "MMM d, yyyy HH:mm" ///this is what you want to convert format
74+ dateFormatter.timeZone = NSTimeZone.local
75+ let timeStamp = dateFormatter.string(from: date!)
76+
77+ return timeStamp
78+ }
79+}
80+
81+extension UIViewController {
82+ func showAlertOn(result: ResultR) {
83+
84+ let message: String
85+
86+ switch result {
87+ case .impossibleContent:
88+ message = "Impossible content"
89+ case .exceededContent:
90+ message = "The content exceeded"
91+ case .longContent:
92+ message = "The content is too long"
93+ case .badAccess:
94+ message = "Try reloading the page again"
95+ case .alreadyRegistered:
96+ message = "User is already registered"
97+ case .tooMuchToAdd:
98+ message = "Limit of channels exceeded"
99+ case .success, .success1:
100+ return
101+ default:
102+ message = "Something went wrong"
103+ }
104+
105+ let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
106+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
107+ self.present(alert, animated: true, completion: nil)
108+ }
109+}
GDproject/Simple model/CompletionTree.swift+2−1
@@ -27,7 +27,8 @@ struct CompletionTree: Codable {
2727 }
2828 }
2929
30− static func getValues(tree: CompletionTree) -> [String] {
30+ static func getValues(tree: CompletionTree) -> [String]
31+ {
3132 var out = [String]()
3233
3334 if let treeVal = tree.value {
GDproject/Simple model/Model.swift+651−40
@@ -10,29 +10,56 @@ import Foundation
1010import UIKit
1111import Alamofire
1212
13+enum ResultR: Int {
14+ case internalServerError = 500
15+ case exceededContent = 470
16+ case longContent = 471
17+ case incorrectContent = 472
18+ case impossibleContent = 473
19+ case invalidTocken = 498
20+ case alreadyRegistered = 409
21+ case invalidCode = 403
22+ case badAccess = 400
23+ case tooMuchToAdd = 406
24+ case notFound = 404
25+ case success1 = 204
26+ case success = 200
27+}
28+
1329class Model{
1430
1531 static let invalidTocken = 498
1632
17− static var hashTagTree: CompletionTree?
33+ static var hashTagTree: CompletionTree?
1834
1935 private static var isValidTocken: ((Int)->())? = { responce in
2036 print(responce)
2137 if responce == invalidTocken {
38+
2239 DataStorage.standard.setIsLoggedIn(value: false, with: 0)
2340 (UIApplication.shared.delegate as! AppDelegate).relaunch()
41+
42+ Model.logout {
43+ print("Logout Is Successful")
44+ }
2445 }
2546 }
2647
2748 private static let baseUrl = "https://valera-denis.herokuapp.com"
2849 static let decoder = JSONDecoder()
29−
50+
51+ static let authMeURL = URL(string: "\(baseUrl)/authentication/me")!
52+ static let deactivateURL = URL(string: "\(baseUrl)/deactivateAll")!
53+ static let registerMeURL = URL(string: "\(baseUrl)/authentication/register")!
54+ static let authVerifyURL = URL(string: "\(baseUrl)/authentication/verify")!
3055 static let authenticationURL = URL(string:"\(baseUrl)/authentication/login")!
56+ static let logOutURL = URL(string:"\(baseUrl)/authentication/logout")!
3157 static let postsLastURL = URL(string:"\(baseUrl)/posts/last")!
3258 static let postsForUserURL = URL(string:"\(baseUrl)/posts/forUser")!
3359 static let postsPublishURL = URL(string:"\(baseUrl)/posts/publish")!
3460 static let usersURL = URL(string:"\(baseUrl)/users")!
3561 static let usersAllURL = URL(string:"\(baseUrl)/users/all")!
62+ static let usersUpdateURL = URL(string:"\(baseUrl)/users/update")!
3663 static let channelsGetURL = URL(string: "\(baseUrl)/channels/get")!
3764 static let channelsUpdateURL = URL(string: "\(baseUrl)/channels/update")!
3865 static let channelsListURL = URL(string: "\(baseUrl)/channels")!
@@ -40,11 +67,21 @@ class Model{
4067 static let channelsDeleteURL = URL(string: "\(baseUrl)/channels/delete")!
4168 static let channelsGetAnonURL = URL(string: "\(baseUrl)/channels/getAnonymous")!
4269 static let complexURL = URL(string: "\(baseUrl)/complex")!
43− static let hashTagTreeURL = URL(string: "\(baseUrl)/tagCompletions")!
70+ static let hashTagTreeURL = URL(string: "\(baseUrl)/tags/completions")!
71+ static let createGroupChatURL = URL(string: "\(baseUrl)/chats/createGroupChat")!
72+ static let chatsGetAllURL = URL(string: "\(baseUrl)/chats/getAll")!
73+ static let getGroupChatURL = URL(string: "\(baseUrl)/chats/getGroupChat")!
74+ static let leaveGroupChatURL = URL(string: "\(baseUrl)/chats/leaveGroupChat")!
75+ static let updateGroupChatURL = URL(string: "\(baseUrl)/chats/updateGroupChat")!
76+ static let messagesGetGroupChatURL = URL(string: "\(baseUrl)/messages/get/groupChat")! //r
77+ static let messagesSendURL = URL(string: "\(baseUrl)/messages/send")!
78+ static let messagesGetUserChatURL = URL(string: "\(baseUrl)/messages/get/userChat")!
79+ static let facultySearchURL = URL(string: "\(baseUrl)/faculty/search")!
80+
4481
45− struct QueryPosts: Codable {
82+ struct QueryPosts<T: Codable>: Codable {
4683 var users: [Int: Users]
47− var response: [Posts]
84+ var response: [T]
4885 }
4986
5087 struct Posts: Codable {
@@ -111,6 +148,8 @@ class Model{
111148 var lastName: String
112149 var firstName: String
113150 var id: Int
151+ var faculty: Faculty
152+ var email: String
114153
115154 func fullName() -> String {
116155 return "\(firstName) \(lastName)"
@@ -174,24 +213,108 @@ class Model{
174213 }
175214 }
176215
177− static func authenticate(with id: Int, completion: @escaping ((Bool)->())) {
216+ struct AuthStatus: Codable {
217+ var userStatus: String
178218
179− let json: [String:Any] = ["authenticationId" : id]
180− let jsonData = try? JSONSerialization.data(withJSONObject: json)
219+ init(){
220+ userStatus = "invalid"
221+ }
222+ }
223+
224+ static func logout(completion: @escaping (()->())){
225+ var request = URLRequest(url: logOutURL)
226+ request.httpMethod = "POST"
227+
228+ AF.request(request).response { (responce) in
229+ isValidTocken?(responce.response?.statusCode ?? 498)
230+
231+ if let code = responce.response?.statusCode, code == 498 {
232+ return
233+ }
234+
235+ completion()
236+ }
237+ }
238+
239+ /*
240+ "email": "vchernyshev@hse.ru",
241+ "middleName": "Algebrovich",
242+ "lastName": "Leonidov",
243+ "faculty": "cs.hse.ru/dse",
244+ "firstName": "Seva"
245+ */
246+
247+ struct NewRegistration: Codable{
248+ var email: String
249+ var firstName: String?
250+ var middleName: String?
251+ var lastName: String?
252+ var faculty: String?
253+ }
254+
255+ static func register(object: NewRegistration, completion: @escaping (()->())) {
256+ var request = URLRequest(url: registerMeURL)
257+ request.httpBody = try? JSONEncoder().encode(object)
181258
182− var request = URLRequest(url: authenticationURL)
183259 request.httpMethod = "POST"
260+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
184261
185− // insert json data to the request
186− request.httpBody = jsonData
262+ AF.request(request).response { (responce) in
263+
264+ guard let code = responce.response?.statusCode else { return }
265+ isValidTocken?(code)
266+
267+ if let code = responce.response?.statusCode, code == 498 {
268+ return
269+ }
270+
271+ if code == 204 {
272+
273+ let fields = responce.response?.allHeaderFields as? [String :String]
274+ let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: responce.response!.url!)
275+ HTTPCookieStorage.shared.setCookie(cookies[0])
276+
277+ completion()
278+ }
279+ }
280+ }
281+
282+ // true only if everything is good
283+ static func verify(with code: Int, completion: @escaping ((Bool)->())){
284+ var request = URLRequest(url: authVerifyURL)
285+ request.httpMethod = "POST"
286+ request.httpBody = "\(code)".data(using: .utf8)
187287 request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
188288
189289 AF.request(request).response { (responce) in
190290
191− guard let realResponse = responce.response, realResponse.statusCode == 200 else
192− {
193− print("Not a 200 response")
291+ guard let statusCode = responce.response?.statusCode else { completion(false); return }
292+ isValidTocken?(statusCode)
293+
294+ if let code = responce.response?.statusCode, code == 498 {
295+ return
296+ }
297+
298+ if statusCode == 204 {
299+ // at this point cookies are set
300+ completion(true)
301+ } else {
194302 completion(false)
303+ }
304+ }
305+ }
306+
307+ // yes or no
308+ static func authenticateMe(completion: @escaping ((Bool)->())){
309+ var request = URLRequest(url: authMeURL)
310+ request.httpMethod = "POST"
311+
312+ AF.request(request).response { (responce) in
313+
314+ guard let statusCode = responce.response?.statusCode else { completion(false); return }
315+ isValidTocken?(statusCode)
316+
317+ if let code = responce.response?.statusCode, code == 498 {
195318 return
196319 }
197320
@@ -201,26 +324,67 @@ class Model{
201324 return
202325 }
203326
204− let fields = realResponse.allHeaderFields as? [String :String]
327+ DataStorage.standard.setIsLoggedIn(value: true, with: personId)
328+ completion(true)
329+ }
330+ }
331+
332+ // register, ok or invalid
333+ static func authenticate(with email: String, completion: @escaping ((AuthStatus)->())) {
334+
335+ let json: [String:Any] = ["authenticationEmail" : email]
336+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
337+
338+ var request = URLRequest(url: authenticationURL)
339+ request.httpMethod = "POST"
340+
341+ // insert json data to the request
342+ request.httpBody = jsonData
343+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
344+
345+ AF.request(request).response { (response) in
205346
206− let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: realResponse.url!)
347+ guard let realResponse = response.response, realResponse.statusCode == 200 else
348+ {
349+ print("Not a 200 response")
350+ completion(AuthStatus())
351+ return
352+ }
353+
354+ guard let json = response.data else { return }
355+ guard let res = try? decoder.decode(AuthStatus.self, from: json) else { return }
207356
208− HTTPCookieStorage.shared.setCookie(cookies[0])
357+ if realResponse.statusCode == 200 && res.userStatus == "registered"
358+ {
359+ // at this point cookies are set
360+ let fields = realResponse.allHeaderFields as? [String :String]
361+ let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: realResponse.url!)
362+ HTTPCookieStorage.shared.setCookie(cookies[0])
363+ }
209364
210− DataStorage.standard.setIsLoggedIn(value: true, with: personId)
211− completion(true)
365+ completion(res)
212366 }
213367 }
214368
215− struct PostsLastRequest<T: Codable>: Codable {
369+ struct GeneralRequest<T: Codable>: Codable
370+ {
371+ var direction: String
216372 var limit: Int
217373 var exclusiveFrom: Int?
218374 var request: T
219375
376+ init(direction: String = "backward", limit: Int, exclusiveFrom: Int?, request: T) {
377+ self.direction = direction
378+ self.limit = limit
379+ self.exclusiveFrom = exclusiveFrom
380+ self.request = request
381+ }
382+
220383 enum CodingKeys: String, CodingKey {
221384 case limit
222385 case exclusiveFrom
223386 case request
387+ case direction
224388 }
225389
226390 func encode(to encoder: Encoder) throws {
@@ -228,12 +392,13 @@ class Model{
228392 try container.encode(limit, forKey: .limit)
229393 try container.encode(exclusiveFrom, forKey: .exclusiveFrom)
230394 try container.encode(request, forKey: .request)
395+ try container.encode(direction, forKey: .direction)
231396 }
232397 }
233398
234399 static func getLast(on limit: Int = 10, from pointInTime: Int? = nil, completion: @escaping (((users:[Int: Users], posts:[Posts]))->()))
235400 {
236− let postRequest = PostsLastRequest<[Int]>(limit: limit, exclusiveFrom: pointInTime, request: [])
401+ let postRequest = GeneralRequest<[Int]>(limit: limit, exclusiveFrom: pointInTime, request: [])
237402
238403 var request = URLRequest(url: postsLastURL)
239404 request.httpBody = try? JSONEncoder().encode(postRequest)
@@ -247,9 +412,13 @@ class Model{
247412
248413 isValidTocken?(response.response?.statusCode ?? 498)
249414
415+ if let code = response.response?.statusCode, code == 498 {
416+ return
417+ }
418+
250419 guard let json = response.data else { return }
251420
252− guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { print("no")
421+ guard let newQueery = try? decoder.decode(QueryPosts<Posts>.self, from: json) else { print("no")
253422 return }
254423
255424 idUser = newQueery.users
@@ -258,7 +427,7 @@ class Model{
258427 }
259428
260429
261− static func createAndPublish(body: [Attachments], tags: [String]){
430+ static func createAndPublish(body: [Attachments], tags: [String], completion: @escaping ((ResultR)->())){
262431 let jsonUpd = CreatedPost(body: body, tags: tags)
263432 var request = URLRequest(url: postsPublishURL)
264433
@@ -272,17 +441,30 @@ class Model{
272441 (response) in
273442
274443 isValidTocken?(response.response?.statusCode ?? 498)
444+
445+ if let code = response.response?.statusCode, code == 498 {
446+ return
447+ }
448+
449+ if let code = response.response?.statusCode,
450+ let result = ResultR(rawValue: code)
451+ {
452+ completion(result)
453+ } else {
454+ completion(.internalServerError)
455+ }
275456 }
276457 }
277458
459+// static func requestForPosts(limit: Int = 10, ){
460+//
461+// }
278462
279− static func getPostsForUser(with id: Int, completion: @escaping (([Posts])->())){
280− let json = [
281− "request" : id,
282− "limit": 10
283− ]
463+ static func getPostsForUser(for limit: Int = 10, from pointInTime: Int? = nil, with id: Int, completion: @escaping (([Posts])->()))
464+ {
465+ let postsRequest = GeneralRequest<Int>(limit: limit, exclusiveFrom: pointInTime, request: id)
284466
285− let jsonData = try? JSONSerialization.data(withJSONObject: json)
467+ let jsonData = try? JSONEncoder().encode(postsRequest)
286468
287469 var request = URLRequest(url: postsForUserURL)
288470 request.httpMethod = "POST"
@@ -296,9 +478,13 @@ class Model{
296478
297479 isValidTocken?(response.response?.statusCode ?? 498)
298480
481+ if let code = response.response?.statusCode, code == 498 {
482+ return
483+ }
484+
299485 guard let json = response.data else { return }
300486
301− guard let newPost = try? decoder.decode(QueryPosts.self, from: json) else { return }
487+ guard let newPost = try? decoder.decode(QueryPosts<Posts>.self, from: json) else { return }
302488
303489 completion(newPost.response)
304490 }
@@ -308,7 +494,7 @@ class Model{
308494
309495 static func getUsers(for ids: [Int], completion: @escaping (([Int:Users])->())){
310496 let json = "\(Set(ids))"
311− print(json)
497+
312498 var request = URLRequest(url: usersURL)
313499
314500 request.httpMethod = "POST"
@@ -322,11 +508,16 @@ class Model{
322508
323509 isValidTocken?(response.response?.statusCode ?? 498)
324510
511+ if let code = response.response?.statusCode, code == 498 {
512+ return
513+ }
514+
325515 guard let json = response.data else { return }
326516
327517 guard let users = try? decoder.decode([Users].self, from: json) else { return }
328518
329519 var dict: [Int:Users] = [:]
520+
330521 users.forEach({ (user) in
331522 dict[user.id] = user
332523 })
@@ -338,7 +529,7 @@ class Model{
338529 // get channel (with id): in responce -- PostQuery
339530 static func getChannel(with channelId: Int, on limit: Int = 10, from pointInTime: Int? = nil, completion: @escaping (((users:[Int: Users], posts:[Posts]))->()))
340531 {
341− let postRequest = PostsLastRequest<Int>(limit: limit, exclusiveFrom: pointInTime, request: channelId)
532+ let postRequest = GeneralRequest<Int>(limit: limit, exclusiveFrom: pointInTime, request: channelId)
342533 let jsonData = try? JSONEncoder().encode(postRequest)
343534
344535 var request = URLRequest(url: channelsGetURL)
@@ -353,10 +544,13 @@ class Model{
353544
354545 isValidTocken?(response.response?.statusCode ?? 498)
355546
547+ if let code = response.response?.statusCode, code == 498 {
548+ return
549+ }
356550
357551 guard let json = response.data else { return }
358552
359− guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return }
553+ guard let newQueery = try? decoder.decode(QueryPosts<Posts>.self, from: json) else { return }
360554
361555 idUser = newQueery.users
362556 completion((newQueery.users, newQueery.response))
@@ -364,7 +558,7 @@ class Model{
364558 }
365559
366560
367− static func createChannel(with channel: Channels) {
561+ static func createChannel(with channel: Channels, completion: @escaping ((ResultR)->())) {
368562
369563 var request = URLRequest(url: channelsCreateURL)
370564 request.httpMethod = "POST"
@@ -375,6 +569,13 @@ class Model{
375569 (response) in
376570
377571 isValidTocken?(response.response?.statusCode ?? 498)
572+
573+ if let code = response.response?.statusCode, let result = ResultR(rawValue: code) {
574+ completion(result)
575+ return
576+ }
577+
578+ completion(ResultR.internalServerError)
378579 }
379580
380581 }
@@ -387,6 +588,9 @@ class Model{
387588 AF.request(request).responseJSON { (response) in
388589
389590 isValidTocken?(response.response?.statusCode ?? 498)
591+ if let code = response.response?.statusCode, code == 498 {
592+ return
593+ }
390594
391595 guard let json = response.data else { return }
392596
@@ -405,6 +609,9 @@ class Model{
405609
406610 AF.request(request).response { (response) in
407611 isValidTocken?(response.response?.statusCode ?? 498)
612+ if let code = response.response?.statusCode, code == 498 {
613+ return
614+ }
408615 }
409616 completion()
410617 }
@@ -420,6 +627,9 @@ class Model{
420627 (response) in
421628
422629 isValidTocken?(response.response?.statusCode ?? 498)
630+ if let code = response.response?.statusCode, code == 498 {
631+ return
632+ }
423633 }
424634 }
425635
@@ -442,6 +652,9 @@ class Model{
442652 }
443653
444654 isValidTocken?(response.response?.statusCode ?? 498)
655+ if let code = response.response?.statusCode, code == 498 {
656+ return
657+ }
445658 }
446659 }
447660
@@ -504,23 +717,26 @@ class Model{
504717 }
505718 }
506719 */
507− static func getAnonymousChannel(by anonymousChannel: Model.AnonymousChannel, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){
720+ static func getAnonymousChannel(by anonymousChannel: Model.Channels, exclusiveFrom: Int? = nil, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){
508721
722+ let req = GeneralRequest<Model.Channels>(limit: 10, exclusiveFrom: exclusiveFrom, request: anonymousChannel)
723+
509724 var request = URLRequest(url: channelsGetAnonURL)
510725 request.httpMethod = "POST"
511− request.httpBody = try? JSONEncoder().encode(anonymousChannel)
726+ request.httpBody = try? JSONEncoder().encode(req)
512727
513728 request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
514729 AF.request(request).response {
515730 (response) in
516731
517732 isValidTocken?(response.response?.statusCode ?? 498)
733+ if let code = response.response?.statusCode, code == 498 {
734+ return
735+ }
518736
519737 guard let json = response.data else { return }
520738
521− guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return }
522−
523− // idUser = newQueery.users
739+ guard let newQueery = try? decoder.decode(QueryPosts<Posts>.self, from: json) else { return }
524740 completion((newQueery.users, newQueery.response))
525741 }
526742 }
@@ -529,11 +745,406 @@ class Model{
529745
530746 AF.request(URLRequest(url: hashTagTreeURL)).responseJSON {
531747 (response) in
532−
748+ isValidTocken?(response.response?.statusCode ?? 498)
749+ if let code = response.response?.statusCode, code == 498 {
750+ return
751+ }
533752 guard let json = response.data else { return }
534753 guard let tree = try? decoder.decode(CompletionTree.self, from: json) else { return }
535754 completion(tree)
536755 }
537756 }
538757
758+ static func getChatAll(limit: Int = 10, exclusiveFrom: Int? = nil, request: [Int] = [], completion: @escaping ((([Dialog],[Int:Model.Users]))->()))
759+ {
760+ let req = GeneralRequest<[Int]>(limit: limit, exclusiveFrom: exclusiveFrom, request: request)
761+ var request = URLRequest(url: chatsGetAllURL)
762+ request.httpMethod = "POST"
763+ request.httpBody = try? JSONEncoder().encode(req)
764+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
765+
766+ AF.request(request).responseJSON { response in
767+ isValidTocken?(response.response?.statusCode ?? 498)
768+
769+ if let code = response.response?.statusCode, code == 498 {
770+ return
771+ }
772+
773+ guard let json = response.data else { return }
774+ let dialogs = try! decoder.decode(QueryPosts<Dialog>.self, from: json)
775+
776+ completion((dialogs.response,dialogs.users))
777+ }
778+ }
779+
780+ enum Dialog: Codable
781+ {
782+ case groupChat(GroupChat)
783+ case userChat(UserChat)
784+
785+ private enum DialogKeys: CodingKey{
786+ case groupChat
787+ case userChat
788+ }
789+
790+ init(from decoder: Decoder) throws {
791+ let container = try decoder.container(keyedBy: DialogKeys.self)
792+ if container.contains(.groupChat){
793+ self = .groupChat(try GroupChat(from: container.superDecoder(forKey: .groupChat)))
794+ } else if container.contains(.userChat){
795+ self = .userChat(try UserChat(from: container.superDecoder(forKey: .userChat)))
796+ } else {
797+ throw DecodingError.keyNotFound(DialogKeys.groupChat, DecodingError.Context(codingPath: container.codingPath, debugDescription: "hz"))
798+ }
799+ }
800+
801+ func encode(to encoder: Encoder) throws {
802+ var container = encoder.container(keyedBy: DialogKeys.self)
803+ switch self{
804+ case .groupChat(let chat):
805+ try chat.encode(to: container.superEncoder(forKey: .groupChat))
806+ case .userChat(let chat):
807+ try chat.encode(to: container.superEncoder(forKey: .userChat))
808+ }
809+ }
810+ }
811+
812+ struct GroupChat: Codable {
813+ var group: Group
814+ var lastMessage: LastMessage?
815+
816+
817+ init(group: Group, lastMessage: LastMessage? = nil) {
818+ self.group = group
819+ self.lastMessage = lastMessage
820+ }
821+ }
822+
823+ struct UserChat: Codable {
824+ var user: Int
825+ var lastMessage: LastMessage?
826+
827+ init(user: Int, lastMessage: LastMessage? = nil) {
828+ self.user = user
829+ self.lastMessage = lastMessage
830+ }
831+ }
832+
833+ struct Group: Codable {
834+ var users: [Int: UserPermission]
835+ var name: String
836+ var id: Int
837+
838+ init(users: [Int: UserPermission] = [:], name: String = "", id: Int) {
839+ self.id = id
840+ self.users = users
841+ self.name = name
842+ }
843+ }
844+
845+ struct UserPermission: Codable {
846+ var isAdmin: Bool
847+ }
848+
849+ struct LastMessage: Codable {
850+ var body: Attachments
851+ var destination: MessageDestination
852+ var time: String
853+ var author: Int
854+ var id: Int
855+
856+ enum MessageCodingKeys: CodingKey{
857+ case user
858+ case group
859+ case body
860+ case time
861+ case author
862+ case id
863+ }
864+
865+ init(from decoder: Decoder) throws
866+ {
867+ let container = try decoder.container(keyedBy: MessageCodingKeys.self)
868+ time = try container.decode(String.self, forKey: .time)
869+ body = try container.decode(Attachments.self, forKey: .body)
870+ author = try container.decode(Int.self, forKey: .author)
871+ id = try container.decode(Int.self, forKey: .id)
872+
873+ if container.contains(.user){
874+ destination = MessageDestination.userChatDestination(try Int(from: container.superDecoder(forKey: .user)))
875+ } else if container.contains(.group){
876+ destination = MessageDestination.groupChatDestination(try Int(from: container.superDecoder(forKey: .group)))
877+ } else {
878+ throw DecodingError.keyNotFound(MessageCodingKeys.group, DecodingError.Context(codingPath: container.codingPath, debugDescription: "hz gr"))
879+ }
880+ }
881+
882+ func encode(to encoder: Encoder) throws {
883+ var container = encoder.container(keyedBy: MessageCodingKeys.self)
884+
885+ try container.encode(time, forKey: .time)
886+ try container.encode(id, forKey: .id)
887+ try container.encode(author, forKey: .author)
888+ try container.encode(body, forKey: .body)
889+
890+ switch destination {
891+ case .userChatDestination(let uId):
892+ try uId.encode(to: container.superEncoder(forKey: .user))
893+ case .groupChatDestination(let gId):
894+ try gId.encode(to: container.superEncoder(forKey: .group))
895+ }
896+ }
897+ }
898+
899+ enum MessageDestination: Codable
900+ {
901+ func encode(to encoder: Encoder) throws
902+ {
903+ var container = encoder.container(keyedBy: MessCodingKeys.self)
904+ switch self {
905+ case .userChatDestination(let uId):
906+ try uId.encode(to: container.superEncoder(forKey: .user))
907+ case .groupChatDestination(let gId):
908+ try gId.encode(to: container.superEncoder(forKey: .group))
909+ }
910+ }
911+
912+ init(from decoder: Decoder) throws
913+ {
914+ let container = try decoder.container(keyedBy: MessCodingKeys.self)
915+ if container.contains(.user){
916+ self = .userChatDestination(try Int(from: container.superDecoder(forKey: .user)))
917+ } else if container.contains(.group){
918+ self = .groupChatDestination(try Int(from: container.superDecoder(forKey: .group)))
919+ } else {
920+ throw DecodingError.keyNotFound(MessCodingKeys.group, DecodingError.Context(codingPath: container.codingPath, debugDescription: "hz gr"))
921+ }
922+ }
923+
924+ enum MessCodingKeys: CodingKey{
925+ case user
926+ case group
927+ }
928+
929+ case userChatDestination(Int)
930+ case groupChatDestination(Int)
931+
932+ }
933+
934+ static func createGroupChat(from group: Group, completion: @escaping ((Int)->())) {
935+ let req = group
936+ var request = URLRequest(url: createGroupChatURL)
937+ request.httpMethod = "POST"
938+ request.httpBody = try? JSONEncoder().encode(req)
939+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
940+
941+ AF.request(request).response { (response) in
942+
943+ isValidTocken?(response.response?.statusCode ?? 498)
944+ if let code = response.response?.statusCode, code == 498 {
945+ return
946+ }
947+
948+ guard let json = response.data else { return }
949+ let stringInt = String.init(data: json, encoding: String.Encoding.utf8)
950+ let intId = Int.init(stringInt!)
951+
952+ completion(intId!)
953+ }
954+ }
955+
956+ static func getMessagesFor(typeOfChat: Model.Dialog, chat id: Int, exclusiveFrom: Int? = nil, limit l: Int = 25, direction: String = "backward", completion: @escaping (([LastMessage])->()))
957+ {
958+ let req = GeneralRequest<Int>(direction: direction, limit: l, exclusiveFrom: exclusiveFrom, request: id)
959+ var request: URLRequest?
960+
961+ switch typeOfChat {
962+ case .groupChat:
963+ request = URLRequest(url: messagesGetGroupChatURL)
964+ case .userChat:
965+ request = URLRequest(url: messagesGetUserChatURL)
966+ }
967+
968+ request!.httpMethod = "POST"
969+ request!.httpBody = try? JSONEncoder().encode(req)
970+ request!.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
971+
972+ AF.request(request!).response { (response) in
973+
974+ isValidTocken?(response.response?.statusCode ?? 498)
975+ if let code = response.response?.statusCode, code == 498 {
976+ return
977+ }
978+
979+ guard let json = response.data else { return }
980+ guard let messages = try? decoder.decode([LastMessage].self, from: json) else { return }
981+
982+ completion(messages)
983+ }
984+ }
985+
986+ static func leaveGroupChat(id: Int, completion: @escaping (()->()))
987+ {
988+ var request = URLRequest(url: leaveGroupChatURL)
989+ request.httpMethod = "POST"
990+ request.httpBody = "\(id)".data(using: .utf8)
991+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
992+
993+ AF.request(request).response { (response) in
994+ isValidTocken?(response.response?.statusCode ?? 498)
995+
996+ if let code = response.response?.statusCode, code == 498 {
997+ return
998+ }
999+ }
1000+
1001+ completion()
1002+ }
1003+
1004+ static func updateGroupChat(with group: Model.Group)
1005+ {
1006+ var request = URLRequest(url: updateGroupChatURL)
1007+ request.httpMethod = "POST"
1008+ request.httpBody = try? JSONEncoder().encode(group)
1009+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
1010+
1011+ AF.request(request).response { (response) in
1012+ isValidTocken?(response.response?.statusCode ?? 498)
1013+
1014+ if let code = response.response?.statusCode, code == 498 {
1015+ return
1016+ }
1017+ }
1018+
1019+ // completion()
1020+ }
1021+
1022+ struct SendMessage: Codable {
1023+ var body: Attachments
1024+ var destination: MessageDestination
1025+
1026+ init(from decoder: Decoder) throws
1027+ {
1028+ let container = try decoder.container(keyedBy: LastMessage.MessageCodingKeys.self)
1029+ body = try container.decode(Attachments.self, forKey: .body)
1030+
1031+ if container.contains(.user){
1032+ destination = MessageDestination.userChatDestination(try Int(from: container.superDecoder(forKey: .user)))
1033+ } else if container.contains(.group){
1034+ destination = MessageDestination.groupChatDestination(try Int(from: container.superDecoder(forKey: .group)))
1035+ } else {
1036+ throw DecodingError.keyNotFound(LastMessage.MessageCodingKeys.group, DecodingError.Context(codingPath: container.codingPath, debugDescription: "hz gr1"))
1037+ }
1038+ }
1039+
1040+ func encode(to encoder: Encoder) throws {
1041+ var container = encoder.container(keyedBy: LastMessage.MessageCodingKeys.self)
1042+ try container.encode(body, forKey: .body)
1043+
1044+ switch destination {
1045+ case .userChatDestination(let uId):
1046+ try uId.encode(to: container.superEncoder(forKey: .user))
1047+ case .groupChatDestination(let gId):
1048+ try gId.encode(to: container.superEncoder(forKey: .group))
1049+ }
1050+ }
1051+
1052+ init(body: Attachments, destination: MessageDestination)
1053+ {
1054+ self.body = body
1055+ self.destination = destination
1056+ }
1057+ }
1058+
1059+ static func sendMessage(message: SendMessage, completion: @escaping ((ResultR)->())){
1060+
1061+ var request = URLRequest(url: messagesSendURL)
1062+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
1063+ request.httpMethod = "POST"
1064+ request.httpBody = try? JSONEncoder().encode(message)
1065+
1066+ AF.request(request).response { (response) in
1067+ isValidTocken?(response.response?.statusCode ?? 498)
1068+
1069+ if let code = response.response?.statusCode, code == 498 {
1070+ return
1071+ }
1072+
1073+ if let result = ResultR(rawValue: response.response!.statusCode){
1074+ completion(result)
1075+ } else {
1076+ completion(.internalServerError)
1077+ }
1078+ }
1079+ }
1080+
1081+ struct Faculty: Codable
1082+ {
1083+
1084+ var path: String
1085+ var url: String
1086+ var campusName: String
1087+ var campusCode: String
1088+ var name: String
1089+ var tags: [String]
1090+ var address: String
1091+
1092+ }
1093+
1094+ static func searchFaculty(string: String, completion: @escaping (([Model.Faculty])->()))
1095+ {
1096+ var request = URLRequest(url: facultySearchURL)
1097+ request.httpMethod = "POST"
1098+ request.httpBody = string.data(using: .utf8)
1099+ request.setValue("text/plain;charset=utf-8", forHTTPHeaderField: "Content-Type")
1100+
1101+ AF.request(request).response { (response) in
1102+ isValidTocken?(response.response?.statusCode ?? 498)
1103+
1104+ if let code = response.response?.statusCode, code == 498 {
1105+ return
1106+ }
1107+
1108+ guard let json = response.data else { return }
1109+ guard let faculties = try? decoder.decode([Faculty].self, from: json) else { return }
1110+
1111+ completion(faculties)
1112+ }
1113+ }
1114+
1115+ static func userUpdate(with newUser: NewRegistration, completion: @escaping ((Bool)->())) {
1116+ var request = URLRequest(url: usersUpdateURL)
1117+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
1118+ request.httpMethod = "POST"
1119+ request.httpBody = try? JSONEncoder().encode(newUser)
1120+
1121+ AF.request(request).response { (response) in
1122+ isValidTocken?(response.response?.statusCode ?? 498)
1123+
1124+ if let code = response.response?.statusCode, code == 498 {
1125+ return
1126+ }
1127+
1128+ if let code = response.response?.statusCode, code != 204{
1129+ completion (false)
1130+ } else {
1131+ completion(true)
1132+ }
1133+ }
1134+ }
1135+
1136+ static func deactivateAll(completion: @escaping (()->())){
1137+ var request = URLRequest(url: deactivateURL)
1138+ request.httpMethod = "DELETE"
1139+
1140+ AF.request(request).response { (responce) in
1141+ isValidTocken?(responce.response?.statusCode ?? 498)
1142+
1143+ if let code = responce.response?.statusCode, code == 498 {
1144+ return
1145+ }
1146+
1147+ completion()
1148+ }
1149+ }
5391150}