git.iko.soy/dashared/GDproject
on master help sign in
Merge pull request #12 from dashared/developer

version 3: channels

commit1aeab21feca61a688206191a370af83edbc9173b authorDarya Rednikina <31587869+dashared@users.noreply.github.com> dateFri 15 Mar 2019, 23:18 (8 years ago) parentf5ee61b25c84f2
45 files changed+1709−256
Cartfile+3−1 GDproject.xcodeproj/project.pbxproj+37−1 GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcscheme+106 GDproject/Base.lproj/Main.storyboard+79−14 GDproject/Constants.swift+5 GDproject/Controller/ News and channels/ChannelController.swift+139−58 GDproject/Controller/ News and channels/ChannelListController.swift+54−41 GDproject/Controller/ News and channels/ChannelsCoordinator.swift+2−2 GDproject/Controller/ News and channels/FullPostController.swift+19−7 GDproject/Controller/ News and channels/NewPostViewController.swift+87−21 GDproject/Controller/ News and channels/NewsController.swift+87−19 GDproject/Controller/ News and channels/NewsVC.swift+44−4 GDproject/Controller/ News and channels/PostViewCell.swift+51−35 GDproject/Controller/ News and channels/SimplifiedChannelsList.swift+94 GDproject/Controller/Coordinator tab bar?/TabbarCoordinator.swift+3−2 GDproject/Controller/Log In/LogInCoordinator.swift+48 GDproject/Controller/LogInViewController.swift → GDproject/Controller/Log In/LogInViewController.swift+27−12 GDproject/Controller/Profile/BasicInfoController.swift+4−5 GDproject/Controller/Profile/ProfileViewController.swift+81−17 GDproject/Controller/RepeatingTimer.swift+59 GDproject/DataStorage.swift+22−7 GDproject/Extentions.swift+6−2 GDproject/Simple model/Channel.swift+2−2 GDproject/Simple model/Model.swift+420 GDproject/Supporting files/AppDelegate.swift+16−6 GDproject/Supporting files/Assets.xcassets/1.imageset/1.pdf GDproject/Supporting files/Assets.xcassets/1.imageset/Contents.json+25 GDproject/Supporting files/Assets.xcassets/2.imageset/2.pdf GDproject/Supporting files/Assets.xcassets/2.imageset/Contents.json+21 GDproject/Supporting files/Assets.xcassets/3.imageset/3.pdf GDproject/Supporting files/Assets.xcassets/3.imageset/Contents.json+21 GDproject/Supporting files/Assets.xcassets/4.imageset/4.pdf GDproject/Supporting files/Assets.xcassets/4.imageset/Contents.json+21 GDproject/Supporting files/Assets.xcassets/42.imageset/42.png GDproject/Supporting files/Assets.xcassets/42.imageset/Contents.json+21 GDproject/Supporting files/Assets.xcassets/5.imageset/5.pdf GDproject/Supporting files/Assets.xcassets/5.imageset/Contents.json+21 GDproject/Supporting files/Assets.xcassets/5051.imageset/5051.png GDproject/Supporting files/Assets.xcassets/5051.imageset/Contents.json+21 GDproject/Supporting files/Assets.xcassets/69.imageset/69.png GDproject/Supporting files/Assets.xcassets/69.imageset/Contents.json+21 GDproject/Supporting files/Assets.xcassets/9.imageset/9.png GDproject/Supporting files/Assets.xcassets/9.imageset/Contents.json+21 GDproject/Supporting files/Assets.xcassets/share.imageset/Contents.json+21 GDproject/Supporting files/Assets.xcassets/share.imageset/share.png
Cartfile+3−1
@@ -2,4 +2,6 @@ github "robb/Cartography"
22github "ivanbruel/MarkdownKit"
33github "roberthein/TinyConstraints"
44github "macteo/Marklight"
5−github "ElaWorkshop/TagListView"
5+github "ElaWorkshop/TagListView"
6+github "Alamofire/Alamofire" "5.0.0-beta.2"
7+github "whitesmith/WSTagsField"
GDproject.xcodeproj/project.pbxproj+37−1
@@ -7,14 +7,22 @@
77 objects = {
88
99/* Begin PBXBuildFile section */
10+ 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */; };
11+ 123AD0E3223C1C3900326173 /* WSTagsField.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 123AD0E2223C1C3900326173 /* WSTagsField.framework */; };
12+ 123AD0E5223C1C4300326173 /* WSTagsField.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 123AD0E2223C1C3900326173 /* WSTagsField.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1013 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A4221F1B3200F6E42A /* LogInViewController.swift */; };
1114 123E37A7221F1DD700F6E42A /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A6221F1DD700F6E42A /* MainController.swift */; };
15+ 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; };
16+ 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; };
17+ 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
18+ 1253867D2223DC9F00CC5EA7 /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */; };
1219 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; };
1320 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; };
1421 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; };
1522 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1288B5CD221F1158002BE6B1 /* DataStorage.swift */; };
1623 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; };
1724 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; };
25+ 1291BE3622218DBF009D3F23 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator.swift */; };
1826 12D7D133221C321600B35452 /* ChannelListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D132221C321600B35452 /* ChannelListController.swift */; };
1927 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D134221C42B700B35452 /* ChannelController.swift */; };
2028 12D7D137221D78E800B35452 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D136221D78E800B35452 /* Channel.swift */; };
@@ -55,9 +63,11 @@
5563 dstSubfolderSpec = 10;
5664 files = (
5765 12E36DC0221441DB006FCDD7 /* Marklight.framework in Embed Frameworks */,
66+ 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */,
5867 12E36DC4221441EA006FCDD7 /* Cartography.framework in Embed Frameworks */,
5968 12E36DD9221594A9006FCDD7 /* TagListView.framework in Embed Frameworks */,
6069 12E36DBD221441D5006FCDD7 /* TinyConstraints.framework in Embed Frameworks */,
70+ 123AD0E5223C1C4300326173 /* WSTagsField.framework in Embed Frameworks */,
6171 12E36DC2221441E1006FCDD7 /* MarkdownKit.framework in Embed Frameworks */,
6272 );
6373 name = "Embed Frameworks";
@@ -66,14 +76,20 @@
6676/* End PBXCopyFilesBuildPhase section */
6777
6878/* Begin PBXFileReference section */
79+ 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplifiedChannelsList.swift; sourceTree = "<group>"; };
80+ 123AD0E2223C1C3900326173 /* WSTagsField.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WSTagsField.framework; path = Carthage/Build/iOS/WSTagsField.framework; sourceTree = "<group>"; };
6981 123E37A4221F1B3200F6E42A /* LogInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInViewController.swift; sourceTree = "<group>"; };
7082 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
83+ 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = "<group>"; };
84+ 124CC7042221C2BA009DF531 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = "<group>"; };
85+ 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatingTimer.swift; sourceTree = "<group>"; };
7186 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
7287 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = "<group>"; };
7388 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = "<group>"; };
7489 1288B5CD221F1158002BE6B1 /* DataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStorage.swift; sourceTree = "<group>"; };
7590 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = "<group>"; };
7691 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = "<group>"; };
92+ 1291BE3522218DBF009D3F23 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = "<group>"; };
7793 12D7D132221C321600B35452 /* ChannelListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelListController.swift; sourceTree = "<group>"; };
7894 12D7D134221C42B700B35452 /* ChannelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelController.swift; sourceTree = "<group>"; };
7995 12D7D136221D78E800B35452 /* Channel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channel.swift; sourceTree = "<group>"; };
@@ -109,9 +125,11 @@
109125 buildActionMask = 2147483647;
110126 files = (
111127 12E36DB522143D54006FCDD7 /* TinyConstraints.framework in Frameworks */,
128+ 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */,
112129 12E36DDB221594B1006FCDD7 /* MarkdownKit.framework in Frameworks */,
113130 12E36DD8221594A9006FCDD7 /* TagListView.framework in Frameworks */,
114131 12E36DAF22143D40006FCDD7 /* Cartography.framework in Frameworks */,
132+ 123AD0E3223C1C3900326173 /* WSTagsField.framework in Frameworks */,
115133 12E36DB322143D4E006FCDD7 /* Marklight.framework in Frameworks */,
116134 );
117135 runOnlyForDeploymentPostprocessing = 0;
@@ -140,9 +158,20 @@
140158 path = "Coordinator tab bar?";
141159 sourceTree = "<group>";
142160 };
161+ 1291BE3722218DC4009D3F23 /* Log In */ = {
162+ isa = PBXGroup;
163+ children = (
164+ 123E37A4221F1B3200F6E42A /* LogInViewController.swift */,
165+ 1291BE3522218DBF009D3F23 /* LogInCoordinator.swift */,
166+ );
167+ path = "Log In";
168+ sourceTree = "<group>";
169+ };
143170 12E36D8F221424EA006FCDD7 = {
144171 isa = PBXGroup;
145172 children = (
173+ 123AD0E2223C1C3900326173 /* WSTagsField.framework */,
174+ 124CC7042221C2BA009DF531 /* Alamofire.framework */,
146175 12E36DD622159493006FCDD7 /* TagListView.framework */,
147176 12E36DB422143D54006FCDD7 /* TinyConstraints.framework */,
148177 12E36DB222143D4E006FCDD7 /* Marklight.framework */,
@@ -182,6 +211,7 @@
182211 12E36DBA2214401B006FCDD7 /* Simple model */ = {
183212 isa = PBXGroup;
184213 children = (
214+ 124CC7022221C00C009DF531 /* Model.swift */,
185215 12E36DB62214400A006FCDD7 /* Post.swift */,
186216 12D7D136221D78E800B35452 /* Channel.swift */,
187217 12E36DB822144016006FCDD7 /* User.swift */,
@@ -202,8 +232,9 @@
202232 12E36DCF22145923006FCDD7 /* Controller */ = {
203233 isa = PBXGroup;
204234 children = (
235+ 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */,
205236 1291BE32222154C3009D3F23 /* Coordinator tab bar? */,
206− 123E37A4221F1B3200F6E42A /* LogInViewController.swift */,
237+ 1291BE3722218DC4009D3F23 /* Log In */,
207238 1288B5CC221F0EFE002BE6B1 /* Profile */,
208239 12E36DD322156519006FCDD7 /* News and channels */,
209240 );
@@ -215,6 +246,7 @@
215246 children = (
216247 12D7D134221C42B700B35452 /* ChannelController.swift */,
217248 12D7D132221C321600B35452 /* ChannelListController.swift */,
249+ 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */,
218250 12E36DC922144635006FCDD7 /* NewPostViewController.swift */,
219251 12E36DD022148122006FCDD7 /* FullPostController.swift */,
220252 12E36D9D221424EA006FCDD7 /* NewsController.swift */,
@@ -304,9 +336,12 @@
304336 isa = PBXSourcesBuildPhase;
305337 buildActionMask = 2147483647;
306338 files = (
339+ 124CC7032221C00C009DF531 /* Model.swift in Sources */,
307340 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */,
308341 12D7D137221D78E800B35452 /* Channel.swift in Sources */,
342+ 1291BE3622218DBF009D3F23 /* LogInCoordinator.swift in Sources */,
309343 12E36DB922144016006FCDD7 /* User.swift in Sources */,
344+ 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */,
310345 125BD5812217314A008A3575 /* NewsVC.swift in Sources */,
311346 12E36DB72214400A006FCDD7 /* Post.swift in Sources */,
312347 12DB7FDF221877160096878E /* InviteViewController.swift in Sources */,
@@ -321,6 +356,7 @@
321356 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */,
322357 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */,
323358 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */,
359+ 1253867D2223DC9F00CC5EA7 /* RepeatingTimer.swift in Sources */,
324360 12DB7FDB2218590C0096878E /* InfoCell.swift in Sources */,
325361 12DB7FD922181E660096878E /* BasicInfoCell.swift in Sources */,
326362 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */,
GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcschemenew file+106
@@ -0,0 +1,106 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Scheme
3+ LastUpgradeVersion = "1010"
4+ version = "1.3">
5+ <BuildAction
6+ parallelizeBuildables = "YES"
7+ buildImplicitDependencies = "YES">
8+ <BuildActionEntries>
9+ <BuildActionEntry
10+ buildForTesting = "YES"
11+ buildForRunning = "YES"
12+ buildForProfiling = "YES"
13+ buildForArchiving = "YES"
14+ buildForAnalyzing = "YES">
15+ <BuildableReference
16+ BuildableIdentifier = "primary"
17+ BlueprintIdentifier = "12E36D97221424EA006FCDD7"
18+ BuildableName = "GDproject.app"
19+ BlueprintName = "GDproject"
20+ ReferencedContainer = "container:GDproject.xcodeproj">
21+ </BuildableReference>
22+ </BuildActionEntry>
23+ </BuildActionEntries>
24+ </BuildAction>
25+ <TestAction
26+ buildConfiguration = "Debug"
27+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+ shouldUseLaunchSchemeArgsEnv = "YES">
30+ <Testables>
31+ </Testables>
32+ <MacroExpansion>
33+ <BuildableReference
34+ BuildableIdentifier = "primary"
35+ BlueprintIdentifier = "12E36D97221424EA006FCDD7"
36+ BuildableName = "GDproject.app"
37+ BlueprintName = "GDproject"
38+ ReferencedContainer = "container:GDproject.xcodeproj">
39+ </BuildableReference>
40+ </MacroExpansion>
41+ <AdditionalOptions>
42+ </AdditionalOptions>
43+ </TestAction>
44+ <LaunchAction
45+ buildConfiguration = "Debug"
46+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
47+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
48+ launchStyle = "0"
49+ useCustomWorkingDirectory = "NO"
50+ ignoresPersistentStateOnLaunch = "NO"
51+ debugDocumentVersioning = "YES"
52+ debugServiceExtension = "internal"
53+ allowLocationSimulation = "YES">
54+ <BuildableProductRunnable
55+ runnableDebuggingMode = "0">
56+ <BuildableReference
57+ BuildableIdentifier = "primary"
58+ BlueprintIdentifier = "12E36D97221424EA006FCDD7"
59+ BuildableName = "GDproject.app"
60+ BlueprintName = "GDproject"
61+ ReferencedContainer = "container:GDproject.xcodeproj">
62+ </BuildableReference>
63+ </BuildableProductRunnable>
64+ <AdditionalOptions>
65+ <AdditionalOption
66+ key = "MallocStackLogging"
67+ value = ""
68+ isEnabled = "YES">
69+ </AdditionalOption>
70+ <AdditionalOption
71+ key = "PrefersMallocStackLoggingLite"
72+ value = ""
73+ isEnabled = "YES">
74+ </AdditionalOption>
75+ <AdditionalOption
76+ key = "MallocScribble"
77+ value = ""
78+ isEnabled = "YES">
79+ </AdditionalOption>
80+ </AdditionalOptions>
81+ </LaunchAction>
82+ <ProfileAction
83+ buildConfiguration = "Release"
84+ shouldUseLaunchSchemeArgsEnv = "YES"
85+ savedToolIdentifier = ""
86+ useCustomWorkingDirectory = "NO"
87+ debugDocumentVersioning = "YES">
88+ <BuildableProductRunnable
89+ runnableDebuggingMode = "0">
90+ <BuildableReference
91+ BuildableIdentifier = "primary"
92+ BlueprintIdentifier = "12E36D97221424EA006FCDD7"
93+ BuildableName = "GDproject.app"
94+ BlueprintName = "GDproject"
95+ ReferencedContainer = "container:GDproject.xcodeproj">
96+ </BuildableReference>
97+ </BuildableProductRunnable>
98+ </ProfileAction>
99+ <AnalyzeAction
100+ buildConfiguration = "Debug">
101+ </AnalyzeAction>
102+ <ArchiveAction
103+ buildConfiguration = "Release"
104+ revealArchiveInOrganizer = "YES">
105+ </ArchiveAction>
106+</Scheme>
GDproject/Base.lproj/Main.storyboard+79−14
@@ -74,8 +74,8 @@
7474 <nil key="highlightedColor"/>
7575 </label>
7676 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yir-N6-Q6f">
77− <rect key="frame" x="297" y="0.0" width="46" height="30"/>
78− <state key="normal" title="Button"/>
77+ <rect key="frame" x="246" y="0.0" width="97" height="30"/>
78+ <state key="normal" title="New message"/>
7979 </button>
8080 </subviews>
8181 </stackView>
@@ -214,6 +214,50 @@
214214 </objects>
215215 <point key="canvasLocation" x="2803" y="1756"/>
216216 </scene>
217+ <!--Simplified Channels List-->
218+ <scene sceneID="gAa-jy-vpG">
219+ <objects>
220+ <tableViewController storyboardIdentifier="SimplifiedChannelsList" id="pgD-Fz-bkD" customClass="SimplifiedChannelsList" customModule="GDproject" customModuleProvider="target" sceneMemberID="viewController">
221+ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="Onz-Ne-JgO">
222+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
223+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
224+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
225+ <prototypes>
226+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="cell" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="cell1" textLabel="mZB-IA-VFq" detailTextLabel="6A8-yj-Eqe" style="IBUITableViewCellStyleSubtitle" id="6dT-km-gZC">
227+ <rect key="frame" x="0.0" y="28" width="375" height="44"/>
228+ <autoresizingMask key="autoresizingMask"/>
229+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="6dT-km-gZC" id="h7U-va-4PX">
230+ <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
231+ <autoresizingMask key="autoresizingMask"/>
232+ <subviews>
233+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="mZB-IA-VFq">
234+ <rect key="frame" x="16" y="5" width="33.5" height="20.5"/>
235+ <autoresizingMask key="autoresizingMask"/>
236+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
237+ <nil key="textColor"/>
238+ <nil key="highlightedColor"/>
239+ </label>
240+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6A8-yj-Eqe">
241+ <rect key="frame" x="16" y="25.5" width="44" height="14.5"/>
242+ <autoresizingMask key="autoresizingMask"/>
243+ <fontDescription key="fontDescription" type="system" pointSize="12"/>
244+ <nil key="textColor"/>
245+ <nil key="highlightedColor"/>
246+ </label>
247+ </subviews>
248+ </tableViewCellContentView>
249+ </tableViewCell>
250+ </prototypes>
251+ <connections>
252+ <outlet property="dataSource" destination="pgD-Fz-bkD" id="V4y-ql-GOS"/>
253+ <outlet property="delegate" destination="pgD-Fz-bkD" id="Avz-4G-8j6"/>
254+ </connections>
255+ </tableView>
256+ </tableViewController>
257+ <placeholder placeholderIdentifier="IBFirstResponder" id="731-nV-bC3" userLabel="First Responder" sceneMemberID="firstResponder"/>
258+ </objects>
259+ <point key="canvasLocation" x="4372" y="-598"/>
260+ </scene>
217261 <!--Channel List Controller-->
218262 <scene sceneID="4eb-Jz-gcx">
219263 <objects>
@@ -320,23 +364,34 @@
320364 <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
321365 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
322366 <subviews>
323− <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sSz-uJ-A7n">
324− <rect key="frame" x="0.0" y="20" width="375" height="647"/>
367+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aRT-Du-PVE">
368+ <rect key="frame" x="0.0" y="55" width="375" height="612"/>
325369 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
326370 </view>
371+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="368-bf-QU6">
372+ <rect key="frame" x="0.0" y="20" width="375" height="35"/>
373+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
374+ <constraints>
375+ <constraint firstAttribute="height" constant="35" id="5ER-su-0Yf"/>
376+ </constraints>
377+ </view>
327378 </subviews>
328379 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
329380 <constraints>
330− <constraint firstItem="sSz-uJ-A7n" firstAttribute="leading" secondItem="wxD-Ek-mgv" secondAttribute="leading" id="2J7-Bi-PzG"/>
331− <constraint firstItem="wxD-Ek-mgv" firstAttribute="trailing" secondItem="sSz-uJ-A7n" secondAttribute="trailing" id="HrU-bs-qsE"/>
332− <constraint firstItem="wxD-Ek-mgv" firstAttribute="bottom" secondItem="sSz-uJ-A7n" secondAttribute="bottom" id="oZ7-xQ-9Cb"/>
333− <constraint firstItem="sSz-uJ-A7n" firstAttribute="top" secondItem="wxD-Ek-mgv" secondAttribute="top" id="zuN-mZ-hgU"/>
381+ <constraint firstItem="wxD-Ek-mgv" firstAttribute="trailing" secondItem="aRT-Du-PVE" secondAttribute="trailing" id="2ee-e8-G7A"/>
382+ <constraint firstItem="368-bf-QU6" firstAttribute="leading" secondItem="wxD-Ek-mgv" secondAttribute="leading" id="9Rl-wh-kZf"/>
383+ <constraint firstItem="aRT-Du-PVE" firstAttribute="leading" secondItem="wxD-Ek-mgv" secondAttribute="leading" id="UBy-Ch-zSL"/>
384+ <constraint firstItem="aRT-Du-PVE" firstAttribute="top" secondItem="368-bf-QU6" secondAttribute="bottom" id="WaE-kh-6w3"/>
385+ <constraint firstItem="368-bf-QU6" firstAttribute="top" secondItem="wxD-Ek-mgv" secondAttribute="top" id="aI9-JQ-7dq"/>
386+ <constraint firstItem="368-bf-QU6" firstAttribute="trailing" secondItem="wxD-Ek-mgv" secondAttribute="trailing" id="haz-0r-Cud"/>
387+ <constraint firstItem="wxD-Ek-mgv" firstAttribute="bottom" secondItem="aRT-Du-PVE" secondAttribute="bottom" id="x3f-eb-ACt"/>
334388 </constraints>
335389 <viewLayoutGuide key="safeArea" id="wxD-Ek-mgv"/>
336390 </view>
337391 <connections>
338− <outlet property="bottomTextViewConstraint" destination="oZ7-xQ-9Cb" id="6In-71-0TX"/>
339− <outlet property="view1" destination="sSz-uJ-A7n" id="5nX-oT-kUi"/>
392+ <outlet property="bottomTextViewConstraint" destination="x3f-eb-ACt" id="ktT-im-InH"/>
393+ <outlet property="view1" destination="aRT-Du-PVE" id="m3d-kW-vqW"/>
394+ <outlet property="viewForTags" destination="368-bf-QU6" id="M5s-RZ-Fq7"/>
340395 </connections>
341396 </viewController>
342397 <placeholder placeholderIdentifier="IBFirstResponder" id="3fd-7N-vPg" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -387,16 +442,22 @@
387442 </textField>
388443 </subviews>
389444 </stackView>
445+ <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="K3b-gv-JPp">
446+ <rect key="frame" x="177.5" y="333.5" width="20" height="20"/>
447+ </activityIndicatorView>
390448 </subviews>
391449 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
392450 <constraints>
393451 <constraint firstItem="UwC-m5-jA5" firstAttribute="leading" secondItem="LOR-Yw-0FU" secondAttribute="leading" constant="16" id="M6z-nU-H40"/>
452+ <constraint firstItem="K3b-gv-JPp" firstAttribute="centerY" secondItem="LOR-Yw-0FU" secondAttribute="centerY" id="eOp-yQ-n28"/>
453+ <constraint firstItem="K3b-gv-JPp" firstAttribute="centerX" secondItem="LOR-Yw-0FU" secondAttribute="centerX" id="gdW-VU-pE8"/>
394454 <constraint firstItem="LOR-Yw-0FU" firstAttribute="trailing" secondItem="UwC-m5-jA5" secondAttribute="trailing" constant="16" id="jST-rr-MMX"/>
395455 <constraint firstItem="UwC-m5-jA5" firstAttribute="top" secondItem="LOR-Yw-0FU" secondAttribute="top" constant="80" id="rvy-Ii-bwS"/>
396456 </constraints>
397457 <viewLayoutGuide key="safeArea" id="LOR-Yw-0FU"/>
398458 </view>
399459 <connections>
460+ <outlet property="indicatorView" destination="K3b-gv-JPp" id="mSR-QU-VZQ"/>
400461 <outlet property="mailTextField" destination="mFP-WG-jY5" id="7SN-qe-DDm"/>
401462 </connections>
402463 </viewController>
@@ -408,7 +469,7 @@
408469 <scene sceneID="RSd-UJ-34B">
409470 <objects>
410471 <navigationController storyboardIdentifier="navChannels" automaticallyAdjustsScrollViewInsets="NO" id="NuV-ZG-ld2" sceneMemberID="viewController">
411− <tabBarItem key="tabBarItem" title="Posts" id="dH2-sL-YsR"/>
472+ <tabBarItem key="tabBarItem" title="Posts" image="5" selectedImage="5" id="dH2-sL-YsR"/>
412473 <toolbarItems/>
413474 <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="6qB-3A-tuL">
414475 <rect key="frame" x="0.0" y="20" width="375" height="96"/>
@@ -437,7 +498,7 @@
437498 <outlet property="delegate" destination="Nqc-TI-ueQ" id="kTM-Be-8qb"/>
438499 </connections>
439500 </tableView>
440− <navigationItem key="navigationItem" id="b8E-6f-hLM"/>
501+ <navigationItem key="navigationItem" largeTitleDisplayMode="always" id="b8E-6f-hLM"/>
441502 </tableViewController>
442503 <placeholder placeholderIdentifier="IBFirstResponder" id="c9d-Kh-itI" userLabel="First Responder" sceneMemberID="firstResponder"/>
443504 </objects>
@@ -447,7 +508,7 @@
447508 <scene sceneID="dJj-Hp-Wa9">
448509 <objects>
449510 <navigationController storyboardIdentifier="navPosts" automaticallyAdjustsScrollViewInsets="NO" id="tae-5q-wvT" sceneMemberID="viewController">
450− <tabBarItem key="tabBarItem" title="Profile" id="lYy-LQ-AcR"/>
511+ <tabBarItem key="tabBarItem" title="Profile" image="1" selectedImage="1" id="lYy-LQ-AcR"/>
451512 <toolbarItems/>
452513 <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="q3a-ph-c8V">
453514 <rect key="frame" x="0.0" y="20" width="375" height="96"/>
@@ -465,7 +526,7 @@
465526 <!--Navigation Controller-->
466527 <scene sceneID="r0M-xH-UwU">
467528 <objects>
468− <navigationController id="609-2j-gc9" sceneMemberID="viewController">
529+ <navigationController storyboardIdentifier="root" id="609-2j-gc9" sceneMemberID="viewController">
469530 <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Ah8-NU-AsD">
470531 <rect key="frame" x="0.0" y="20" width="375" height="44"/>
471532 <autoresizingMask key="autoresizingMask"/>
@@ -495,4 +556,8 @@
495556 <point key="canvasLocation" x="-466" y="641"/>
496557 </scene>
497558 </scenes>
559+ <resources>
560+ <image name="1" width="30" height="30"/>
561+ <image name="5" width="30" height="30"/>
562+ </resources>
498563</document>
GDproject/Constants.swift+5
@@ -41,3 +41,8 @@ let newsController = "NewsController"
4141
4242/// constant for switching to view controller for log in app
4343let logInController = "LogInController"
44+
45+
46+let profileViewController = "ProfileViewController"
47+
48+let simplifiedChannelsList = "SimplifiedChannelsList"
GDproject/Controller/ News and channels/ChannelController.swift+139−58
@@ -9,7 +9,12 @@
99import UIKit
1010
1111protocol DataDelegate {
12− func passData(for row: Int, channel: Channel)
12+ func passData(for row: Int, channel: Model.Channels)
13+}
14+
15+enum ActiveTable {
16+ case tags
17+ case people
1318}
1419
1520class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, UISearchBarDelegate
@@ -17,14 +22,16 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
1722 @IBOutlet weak var tableView: UITableView!
1823
1924 var index: Int?
20− var channel: Channel?
25+ var channel: Model.Channels?
2126 var myProtocol: DataDelegate?
2227
23− var dataSource: [String] = []
28+ var fullTags: [String] = Array(Model.Channels.fullTags)
29+
30+ var dataSourcePeople: [Model.Users] = []
2431
25− var fullPeople: [String] = ["Other Ann", "Other Ilya", "Other Kostya", "Pasha", "Valera"]
32+ var dataSourceTags: [String] = []
2633
27− var fullTags: [String] = ["# happy", "# sad", "# meme", "# shershakov", "# pupsik"]
34+ var activeDataSource: ActiveTable = .people
2835
2936 @IBOutlet weak var viewww: UIView!
3037
@@ -32,23 +39,58 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
3239
3340 override func viewDidLoad() {
3441 super.viewDidLoad()
42+
43+ dataSourceTags = channel!.tags
44+ setUpController()
45+ tableView.reloadData()
46+ }
47+
48+ var reloadtable: Bool = false {
49+ didSet{
50+ tableView.reloadData()
51+ }
52+ }
53+
54+ override func viewWillAppear(_ animated: Bool) {
55+ super.viewWillAppear(animated)
56+ Model.getUsers(for: channel!.people) { [weak self] (people) in
57+ self?.dataSourcePeople = Array(people.values)
58+ self?.reloadtable = true
59+ }
60+ }
61+
62+ // TODO: update channel
63+ override func viewWillDisappear(_ animated: Bool) {
64+ super.viewWillDisappear(animated)
65+ if let _ = channel?.id { print("update")
66+ Model.updateChannel(with: channel!)
67+ } else { print("create")
68+ Model.createChannel(with: channel!)
69+ }
70+ }
71+
72+ func setUpController(){
73+
3574 tableView.delegate = self
3675 tableView.dataSource = self
37− dataSource = channel!.people
3876 tableView.register(UITableViewCell.self, forCellReuseIdentifier: itemCellId)
3977 tableView.reloadData()
4078 textField.delegate = self
4179 searchBar.delegate = self
4280 searchBar.selectedScopeButtonIndex = 0
43− navigationItem.title = channel?.title
44− textField.placeholder = channel?.title
81+
82+ navigationItem.title = channel?.name
83+ textField.placeholder = channel?.name
84+
4585 textField.addTarget(self, action: #selector(changedText(_:)), for: .editingChanged)
4686 }
4787
88+
89+
4890 @objc func changedText(_ textField: UITextField){
4991 navigationItem.title = textField.text
50− channel?.title = textField.text!
51− myProtocol?.passData(for: index!, channel: channel!)
92+ channel?.name = textField.text!
93+ // myProtocol?.passData(for: index!, channel: channel!)
5294 }
5395
5496 func numberOfSections(in tableView: UITableView) -> Int {
@@ -56,72 +98,76 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
5698 }
5799
58100 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
59− return dataSource.count
101+ switch activeDataSource {
102+ case .people:
103+ return dataSourcePeople.count
104+ case .tags:
105+ return dataSourceTags.count
106+ }
60107 }
61108
62109 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
63− if searchBar.isFirstResponder {
64− if let cell = tableView.cellForRow(at: indexPath)
65− {
66−
110+ if searchBar.isFirstResponder, let cell = tableView.cellForRow(at: indexPath)
111+ {
67112 if cell.accessoryType == .none{
68113 switch searchBar.selectedScopeButtonIndex{
69114 case 0:
70− channel?.people.append(dataSource[indexPath.row])
115+ channel?.people.append(dataSourcePeople[indexPath.row].id)
71116 print("people = \(channel!.people)")
72117 case 1:
73− channel?.hashtags.append(dataSource[indexPath.row])
74− print("hashtags = \(channel!.hashtags)")
118+ channel?.tags.append(dataSourceTags[indexPath.row])
119+ print("hashtags = \(channel!.tags)")
75120 default:
76121 break
77122 }
78−
79123 cell.accessoryType = .checkmark
80124 } else {
81125 cell.accessoryType = .none
82126
83127 switch searchBar.selectedScopeButtonIndex{
84128 case 0:
85− channel?.people = channel!.people.filter({ (s) -> Bool in
86− !fullPeople[indexPath.row].contains(s)
87− })
88− // TODO: dataPass
129+ let filtered = channel!.people.filter{ $0 != Model.Channels.fullPeople[indexPath.row].id }
130+ channel?.people = filtered
89131 case 1:
90− channel?.hashtags = channel!.hashtags.filter({ (s) -> Bool in
91− !fullTags[indexPath.row].contains(s)
92− })
93− // TODO: dataPass
132+ let filtered = channel!.tags.filter{
133+ !fullTags[indexPath.row].contains($0)
134+ }
135+ channel?.tags = filtered
94136 default:
95137 break
96138 }
97139 }
98− }
99− } else
140+ }
141+ else
100142 {
101− tableView.beginUpdates()
102− dataSource.remove(at: indexPath.row)
103− switch searchBar.selectedScopeButtonIndex{
104− case 0:
143+ switch activeDataSource{
144+ case .people:
105145 channel?.people.remove(at: indexPath.row)
106− // TODO: dataPass
107− case 1:
108− channel?.hashtags.remove(at: indexPath.row)
109− // TODO: dataPass
110− default:
111− break
146+ case .tags:
147+ channel?.tags.remove(at: indexPath.row)
148+ }
149+
150+ tableView.beginUpdates()
151+ switch activeDataSource{
152+ case .people:
153+ dataSourcePeople.remove(at: indexPath.row)
154+ case .tags:
155+ dataSourceTags.remove(at: indexPath.row)
112156 }
113157 tableView.deleteRows(at: [indexPath], with: .none)
114158 tableView.endUpdates()
115159 }
116160 }
117161
118− func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
162+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
163+ {
119164 let cell = tableView.dequeueReusableCell(withIdentifier: itemCellId, for: indexPath)
120165
121− cell.textLabel?.text = dataSource[indexPath.row]
166+ cell.textLabel?.text = activeDataSource == .people ? "\(dataSourcePeople[indexPath.row].firstName) \(dataSourcePeople[indexPath.row].middleName) \(dataSourcePeople[indexPath.row].lastName)" : dataSourceTags[indexPath.row]
122167 cell.selectionStyle = .none
123168
124− if channel!.hashtags.contains(dataSource[indexPath.row]) || channel!.people.contains(dataSource[indexPath.row])
169+ if activeDataSource == .tags && channel!.tags.contains(dataSourceTags[indexPath.row]) ||
170+ activeDataSource == .people && channel!.people.contains(dataSourcePeople[indexPath.row].id)
125171 {
126172 cell.accessoryType = .checkmark
127173 } else {
@@ -147,7 +193,7 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
147193 {
148194 let view = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 70))
149195
150− if flag {
196+ if activeDataSource == .tags {
151197 searchBar.selectedScopeButtonIndex = 1
152198 } else {
153199 searchBar.selectedScopeButtonIndex = 0
@@ -165,7 +211,12 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
165211
166212 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
167213 if searchText.isEmpty {
168− dataSource = searchBar.selectedScopeButtonIndex == 0 ? fullPeople : fullTags
214+ switch activeDataSource {
215+ case .people:
216+ dataSourcePeople = Model.Channels.fullPeople
217+ case .tags:
218+ dataSourceTags = fullTags
219+ }
169220 tableView.reloadData()
170221 } else {
171222 filterTableView(ind: searchBar.selectedScopeButtonIndex, text: searchText)
@@ -175,12 +226,13 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
175226 func filterTableView(ind: Int, text: String){
176227 switch ind {
177228 case 0:
178− dataSource = fullPeople.filter({ (s) -> Bool in
179− s.lowercased().contains(text.lowercased())
180− })
229+ dataSourcePeople = Model.Channels.fullPeople.filter {
230+ let fullName = "\($0.firstName) \($0.middleName) \($0.lastName)"
231+ return fullName.lowercased().contains(text.lowercased())
232+ }
181233 tableView.reloadData()
182234 case 1:
183− dataSource = fullTags.filter({ (s) -> Bool in
235+ dataSourceTags = fullTags.filter({ (s) -> Bool in
184236 s.lowercased().contains(text.lowercased())
185237 })
186238 tableView.reloadData()
@@ -189,33 +241,62 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS
189241 }
190242 }
191243
192− var flag = false
193−
194244 func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
245+ activeDataSource = searchBar.selectedScopeButtonIndex == 0 ? .people : .tags
195246 if searchBar.isFirstResponder {
196− dataSource = searchBar.selectedScopeButtonIndex == 0 ? fullPeople : fullTags
197− flag = searchBar.selectedScopeButtonIndex == 0 ? false : true
247+ switch activeDataSource{
248+ case .people :
249+ dataSourcePeople = Model.Channels.fullPeople
250+ dataSourceTags = []
251+ case .tags:
252+ dataSourceTags = fullTags
253+ dataSourcePeople = []
254+ }
198255 if (!(searchBar.text?.isEmpty ?? true))
199256 {
200257 filterTableView(ind: selectedScope, text: searchBar.text!)
201258 }
202259 tableView.reloadData()
203260 } else if !(searchBar.isFirstResponder){
204− dataSource = searchBar.selectedScopeButtonIndex == 0 ? channel!.people : channel!.hashtags
205− flag = searchBar.selectedScopeButtonIndex == 0 ? false : true
261+ switch activeDataSource{
262+ case .people :
263+ dataSourcePeople = channel!.people.map({ (item) -> Model.Users in
264+ Model.Channels.fullPeopleDict[item]!
265+ })
266+ dataSourceTags = []
267+ case .tags:
268+ dataSourceTags = channel!.tags
269+ dataSourcePeople = []
270+ }
206271 tableView.reloadData()
207272 }
208273 }
209274
210275 func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
211− dataSource = searchBar.selectedScopeButtonIndex == 0 ? channel!.people : channel!.hashtags
212− flag = searchBar.selectedScopeButtonIndex == 0 ? false : true
276+ switch activeDataSource{
277+ case .people :
278+ dataSourcePeople = channel!.people.map({ (item) -> Model.Users in
279+ Model.Channels.fullPeopleDict[item]!
280+ })
281+ dataSourceTags = []
282+ case .tags:
283+ dataSourceTags = channel!.tags
284+ dataSourcePeople = []
285+ }
286+ activeDataSource = searchBar.selectedScopeButtonIndex == 0 ? .people : .tags
213287 tableView.reloadData()
214288 }
215289
216290 func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
217− dataSource = searchBar.selectedScopeButtonIndex == 0 ? fullPeople : fullTags
218− flag = searchBar.selectedScopeButtonIndex == 0 ? false : true
291+ switch activeDataSource{
292+ case .people :
293+ dataSourcePeople = Model.Channels.fullPeople
294+ dataSourceTags = []
295+ case .tags:
296+ dataSourceTags = fullTags
297+ dataSourcePeople = []
298+ }
299+ activeDataSource = searchBar.selectedScopeButtonIndex == 0 ? .people : .tags
219300 tableView.reloadData()
220301 }
221302
GDproject/Controller/ News and channels/ChannelListController.swift+54−41
@@ -17,13 +17,19 @@ struct ChannelData{
1717class ChannelListController: UITableViewController, DataDelegate {
1818
1919 // MARK: - Output -
20− var onChannelSelected: ((Channel) -> Void)?
20+ var onChannelSelected: ((Model.Channels) -> Void)?
2121
22− // MARK:- filter search controller
23− var filteredDataSource = [Channel]()
22+ // MARK: - filter search controller
23+ var filteredDataSource = [Model.Channels]()
2424
2525 var myProtocol: DataDelegate?
26− var displayingChannel: Channel?
26+ var displayingChannel: Model.Channels?
27+
28+ var toReload: Bool = false {
29+ didSet{
30+ tableView.reloadData()
31+ }
32+ }
2733
2834 var isFiltering: Bool {
2935 return searchController.isActive && !searchBarIsEmpty()
@@ -35,21 +41,24 @@ class ChannelListController: UITableViewController, DataDelegate {
3541
3642 func filterContentForSearchText(_ searchText: String, scope: String = "All")
3743 {
38− filteredDataSource = ChannelListController.dataSource.filter({(keys : Channel) -> Bool in
39− return keys.title.lowercased().contains(searchText.lowercased())
44+ filteredDataSource = dataSource.filter({(keys : Model.Channels) -> Bool in
45+ return keys.name.lowercased().contains(searchText.lowercased())
4046 })
4147
4248 tableView.reloadData()
4349 }
4450
45− func passData(for row: Int, channel: Channel) {
46− ChannelListController.dataSource[row] = channel
51+ func passData(for row: Int, channel: Model.Channels) {
52+ // dataSource[row] = channel
4753 }
4854
4955 var searchController = UISearchController(searchResultsController: nil)
5056
5157 override func viewDidLoad() {
5258 super.viewDidLoad()
59+
60+ Model.usersAllGet()
61+
5362 setUpNavigationBar()
5463 navigationItem.title = "Channels"
5564 searchController.searchResultsUpdater = self
@@ -60,49 +69,48 @@ class ChannelListController: UITableViewController, DataDelegate {
6069 }
6170
6271 func setUpNavigationBar(){
63− navigationController?.navigationItem.largeTitleDisplayMode = .always
72+ navigationItem.largeTitleDisplayMode = .always
6473 navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addChannel))]
6574 }
6675
67− override func viewWillAppear(_ animated: Bool)
76+ override func viewDidAppear(_ animated: Bool)
6877 {
69− super.viewWillAppear(animated)
78+ super.viewDidAppear(animated)
7079 searchController.isActive = false
71− tableView.reloadData()
80+ askForUpdates()
81+ }
82+
83+ private func askForUpdates(){
84+ Model.channelsList { [weak self] (channels) in
85+ self?.dataSource = [ChannelListController.generalChannel] + channels
86+ self?.toReload = true
87+ }
88+ }
89+
90+ override func viewWillAppear(_ animated: Bool) {
91+ super.viewWillAppear(true)
92+ askForUpdates()
7293 }
7394
7495 @objc func addChannel()
7596 {
76− // insertion
77− tableView.beginUpdates()
78− ChannelListController.dataSource.insert(Channel(title: "Untitled", subtitle: "No", hashtags: [], people: [], posts: []), at: 1)
79− tableView.insertRows(at: [IndexPath(row: 1, section: 0)], with: .none)
80− tableView.endUpdates()
81−
8297 // editing mode is on automatically
8398 let vc = storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController
8499 vc.index = 1
85100 vc.myProtocol = self
86− vc.channel = ChannelListController.dataSource[1]
101+ vc.channel = Model.Channels(people: [], name: "Untitled", tags: [])
87102 navigationController?.pushViewController(vc, animated: true)
88103 }
89104
90− static var dataSource : [Channel] = [
91− Channel(title: "General", subtitle: "All posts", hashtags: ["All"], people: ["All"], posts: []),
92− Channel(title: "Title", subtitle: "subtitle", hashtags: ["# sad", "# happy"], people: ["Seva", "Andrey"], posts: [Post(dataArray: [.text("Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard.")], from: User(name: "vbogomazova", id: 2, fullName: "Богомазова Вероника Львовна"), date: "14.02.19 в 12:05")]),
93− Channel(title: "Title2", subtitle: "subtitle2", hashtags: ["# studyhard", "# university"], people: ["Pasha", "Olya", "Andrey", "Ilya"], posts:
94− [
95− Post(dataArray: [.text("L'avantage du Lorem Ipsum sur un texte générique comme 'Du texte. Du texte. Du texte.' est qu'il possède une distribution de lettres plus ou moins normale.")], from: User(name: "vbogomazova", id: 2, fullName: "Богомазова Вероника Львовна"), date: "14.02.19 в 12:05"),
96− Post(dataArray: [.text("par accident, souvent intentionnellement (histoire d'y rajouter de petits clins d'oeil, voire des phrases embarassantes).")], from: User(name: "vbogomazova", id: 2, fullName: "Богомазова Вероника Львовна"), date: "14.02.19 в 12:05")
97− ]),
98− Channel(title: "Title3", subtitle: "subtitle3", hashtags: ["# lol", "# meme", "# hehe"], people: ["Superman"], posts: [Post(dataArray: [.text("Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, quand un imprimeur anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. Il n'a pas fait que survivre cinq siècles, mais s'est aussi adapté à la bureautique informatique, sans que son contenu n'en soit modifié.")], from: User(name: "vbogomazova", id: 2, fullName: "Богомазова Вероника Львовна"), date: "14.02.19 в 12:05")])
99− ]
105+ static let generalChannel = Model.Channels(people: [], name: "General", id: -1, tags: [])
106+
107+ var dataSource : [Model.Channels] = [ ChannelListController.generalChannel ]
100108
101109 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
102110 if isFiltering {
103111 return filteredDataSource.count
104112 } else {
105− return ChannelListController.dataSource.count
113+ return dataSource.count
106114 }
107115 }
108116
@@ -113,11 +121,11 @@ class ChannelListController: UITableViewController, DataDelegate {
113121
114122
115123 if isFiltering{
116− cell.textLabel?.text = filteredDataSource[indexPath.row].title
117− cell.detailTextLabel?.text = filteredDataSource[indexPath.row].subtitle
124+ cell.textLabel?.text = filteredDataSource[indexPath.row].name
125+ cell.detailTextLabel?.text = filteredDataSource[indexPath.row].tags.reduce(String(), { (r, next) -> String in "\(r) \(next)" })
118126 } else {
119− cell.textLabel?.text = ChannelListController.dataSource[indexPath.row].title
120− cell.detailTextLabel?.text = ChannelListController.dataSource[indexPath.row].subtitle
127+ cell.textLabel?.text = dataSource[indexPath.row].name
128+ cell.detailTextLabel?.text = dataSource[indexPath.row].tags.reduce(String(), { (r, next) -> String in "\(r) \(next)" })
121129 }
122130
123131 return cell
@@ -140,24 +148,29 @@ class ChannelListController: UITableViewController, DataDelegate {
140148 let vc = self?.storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController
141149 vc.index = indexPath.row
142150 vc.myProtocol = self!
143− vc.channel = ChannelListController.dataSource[indexPath.row]
151+ vc.channel = self?.dataSource[indexPath.row]
144152 self?.navigationController?.pushViewController(vc, animated: true)
145153 }
146− editButton.backgroundColor = .green
154+ editButton.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
147155
148156
149157 let deleteButton = UITableViewRowAction(style: .normal, title: "Delete") { [weak self] (action, indexPath) in
150− if (ChannelListController.dataSource[indexPath.row] == self!.displayingChannel!)
158+
159+ Model.channelsDelete(by: self!.dataSource[indexPath.row].id!, completion: {
160+ print("хз что тут делать при успехе")
161+ })
162+
163+ if (self!.dataSource[indexPath.row].id == self!.displayingChannel?.id ?? -1)
151164 {
152− self?.myProtocol?.passData(for: 0, channel: ChannelListController.dataSource[0])
165+ self?.myProtocol?.passData(for: 0, channel: self!.dataSource[0])
153166 }
154167 self?.tableView.beginUpdates()
155− ChannelListController.dataSource.remove(at: indexPath.row)
168+ self?.dataSource.remove(at: indexPath.row)
156169 self?.tableView.deleteRows(at: [indexPath], with: .none)
157170 self?.tableView.endUpdates()
158171 }
159− deleteButton.backgroundColor = .red
160172
173+ deleteButton.backgroundColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1)
161174
162175 return [editButton, deleteButton]
163176 }
@@ -170,7 +183,7 @@ class ChannelListController: UITableViewController, DataDelegate {
170183 myProtocol?.passData(for: 0, channel: filteredDataSource[indexPath.row])
171184 navigationController?.popViewController(animated: true)
172185 } else {
173− myProtocol?.passData(for: 0, channel: ChannelListController.dataSource[indexPath.row])
186+ myProtocol?.passData(for: 0, channel: dataSource[indexPath.row])
174187 navigationController?.popViewController(animated: true)
175188 //onChannelSelected?(ChannelListController.dataSource[indexPath.row])
176189 }
GDproject/Controller/ News and channels/ChannelsCoordinator.swift+2−2
@@ -12,11 +12,11 @@ import UIKit
1212final class ChannelsCoordinator{
1313
1414 // MARK: - Properties
15− private var channel: Channel { didSet { updateInterfaces() } }
15+ private var channel: Model.Channels { didSet { updateInterfaces() } }
1616 private weak var navigationController: UINavigationController?
1717
1818 // MARK:- Init
19− init(currentChannel: Channel, navigationController: UINavigationController) {
19+ init(currentChannel: Model.Channels, navigationController: UINavigationController) {
2020 self.channel = currentChannel
2121 self.navigationController = navigationController
2222 }
GDproject/Controller/ News and channels/FullPostController.swift+19−7
@@ -11,7 +11,8 @@ import TinyConstraints
1111
1212class FullPostController: UITableViewController {
1313
14− var post: Post?
14+ var post: Model.Posts?
15+ var type: HeaderType = .NONE
1516
1617 override func viewDidLoad() {
1718 super.viewDidLoad()
@@ -20,13 +21,11 @@ class FullPostController: UITableViewController {
2021
2122 setUpNavigationBar()
2223 tableView.separatorStyle = .none
23−
24−
2524 }
2625
2726 func setUpNavigationBar(){
28− navigationController?.navigationBar.prefersLargeTitles = false
29− navigationItem.title = "\(post?.fromUser.login ?? "")"
27+ navigationItem.largeTitleDisplayMode = .never
28+ navigationItem.title = "\(post?.authorId ?? 0)"
3029 navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(self.options))]
3130 }
3231
@@ -83,7 +82,7 @@ class FullPostController: UITableViewController {
8382 case 0:
8483 return 1
8584 case 1:
86− return post?.comments.count ?? 0
85+ return 0
8786 default:
8887 return 0
8988 }
@@ -95,7 +94,20 @@ class FullPostController: UITableViewController {
9594
9695 let cell = tableView.dequeueReusableCell(withIdentifier: postCellId) as! PostViewCell
9796
98− cell.fill(with: post!.dataArray, true, post: post!)
97+
98+ cell.fill(with: PostCellData.create(with: post!.body), true, post: post!)
99+
100+ switch type {
101+ case .NEWS:
102+ cell.onUserDisplay = { [weak self] (id) in
103+ let vc = self?.storyboard!.instantiateViewController(withIdentifier: profileViewController) as! ProfileViewController
104+ vc.idProfile = id
105+ self?.navigationController?.pushViewController(vc, animated: true)
106+ }
107+ default:
108+ break
109+ }
110+
99111 cell.selectionStyle = .none
100112 return cell
101113 }
GDproject/Controller/ News and channels/NewPostViewController.swift+87−21
@@ -10,10 +10,15 @@ import UIKit
1010import Cartography
1111import Marklight
1212import TinyConstraints
13+import WSTagsField
1314
1415class NewPostViewController: UIViewController, UITextViewDelegate {
1516
1617 @IBOutlet weak var view1: UIView!
18+
19+ @IBOutlet weak var viewForTags: UIView!
20+
21+ fileprivate let tagsField = WSTagsField()
1722 // Keep strong instance of the `NSTextStorage` subclass
1823 let textStorage = MarklightTextStorage()
1924
@@ -21,6 +26,8 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
2126 var myProtocol: NewPostDelegate?
2227
2328 static var draft: String = ""
29+ static var hashTagsDraft: [String] = []
30+
2431 var textView: UITextView!
2532
2633 // buttons for attaching images
@@ -46,9 +53,42 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
4653 override func viewDidLoad()
4754 {
4855 super.viewDidLoad()
56+ //createTimerForUpdates()
57+
4958 setUpMD()
5059 setUpTextView()
5160 setUpAccessoryView()
61+ setUpTagsView()
62+ }
63+
64+ func setUpTagsView(){
65+ tagsField.frame = viewForTags.bounds
66+ viewForTags.addSubview(tagsField)
67+
68+ // tagsField.translatesAutoresizingMaskIntoConstraints = false
69+ // tagsField.heightAnchor.constraint(lessThanOrEqualToConstant: 150).isActive = true
70+
71+ tagsField.cornerRadius = 3.0
72+ tagsField.spaceBetweenLines = 10
73+ tagsField.spaceBetweenTags = 10
74+
75+ //tagsField.numberOfLines = 3
76+ //tagsField.maxHeight = 100.0
77+
78+ tagsField.layoutMargins = UIEdgeInsets(top: 2, left: 6, bottom: 2, right: 6)
79+ tagsField.contentInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) //old padding
80+
81+ tagsField.placeholder = "Enter a tag"
82+ tagsField.placeholderColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)
83+ tagsField.placeholderAlwaysVisible = true
84+ tagsField.backgroundColor = #colorLiteral(red: 0.937254902, green: 0.937254902, blue: 0.9568627451, alpha: 1)
85+ tagsField.returnKeyType = .next
86+ tagsField.delimiter = ""
87+
88+ tagsField.textDelegate = self
89+ tagsField.acceptTagOption = .space
90+
91+ textFieldEvents()
5292 }
5393
5494 override func viewWillAppear(_ animated: Bool) {
@@ -57,6 +97,8 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
5797 navigationController?.navigationBar.prefersLargeTitles = false
5898 navigationItem.title = "New post"
5999 textView.text = NewPostViewController.draft
100+ tagsField.addTags(NewPostViewController.hashTagsDraft)
101+
60102 }
61103
62104 func setUpAccessoryView(){
@@ -148,23 +190,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
148190
149191 @objc func addMathBrackets()
150192 {
151− if textView.text.count != 0 && textView.text.last != "\n" {
152− textView.insertText("\n\\[\n ")
153− }
154− else {
155− textView.insertText("\\[\n ")
156− }
157−
158− if let selectedRange = textView.selectedTextRange
159− {
160− textView.insertText("\n\\]\n")
161−
162− if let newPosition = textView.position(from: selectedRange.start, offset: 0)
163− {
164− // set the new position
165− textView.selectedTextRange = textView.textRange(from: newPosition, to: newPosition)
166− }
167− }
193+ // nothing
168194 }
169195
170196 func setUpMD(){
@@ -178,6 +204,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
178204 let layoutManager = NSLayoutManager()
179205
180206 // Assign the `UITextView`'s `NSLayoutManager` to the `NSTextStorage` subclass
207+ //textStorage.addLayoutManager(textView.layoutManager)
181208 textStorage.addLayoutManager(layoutManager)
182209
183210 let textContainer = NSTextContainer()
@@ -186,13 +213,12 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
186213 textView = UITextView(frame: view.bounds, textContainer: textContainer)
187214 }
188215
216+ var indexOfPost = 0
189217 // MARK:- new post
190218 @objc func newPost(){
191− let p = Post(dataArray: [.text(textView.text)], from: User(name: "Сева", id: 5, fullName: "Сева Леонидович"), date: "12.01.19")
192−
193−
219+ Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: tagsField.tags.map { $0.text })
194220 // adding row to uiTableView after adding new post
195− myProtocol?.addPost(post: p)
221+ // myProtocol?.addPost(post: p)
196222 moveBackToParentVC()
197223 // somewhere here i will be sending server notifications about new post arrival
198224 }
@@ -205,6 +231,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
205231 [weak self]
206232 _ in
207233 NewPostViewController.draft = self?.textView.text ?? ""
234+ NewPostViewController.hashTagsDraft = self?.tagsField.tags.map { $0.text } ?? []
208235 self?.moveBackToParentVC()
209236 }
210237
@@ -213,6 +240,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
213240 [weak self] (_)
214241 in
215242 NewPostViewController.draft = ""
243+ NewPostViewController.hashTagsDraft = []
216244 self?.moveBackToParentVC()
217245 }
218246
@@ -252,4 +280,42 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
252280 rect.size.height = rect.size.height + textView.textContainerInset.bottom
253281 textView.scrollRectToVisible(rect, animated: animated)
254282 }
283+
284+ fileprivate func textFieldEvents() {
285+ tagsField.onDidAddTag = { _, _ in
286+ print("onDidAddTag")
287+ }
288+
289+ tagsField.onDidRemoveTag = { _, _ in
290+ print("onDidRemoveTag")
291+ }
292+
293+ tagsField.onDidChangeText = { _, text in
294+ print("onDidChangeText")
295+ }
296+
297+ tagsField.onDidChangeHeightTo = { _, height in
298+ print("HeightTo \(height)")
299+ }
300+
301+ tagsField.onDidSelectTagView = { _, tagView in
302+ print("Select \(tagView)")
303+ }
304+
305+ tagsField.onDidUnselectTagView = { _, tagView in
306+ print("Unselect \(tagView)")
307+ }
308+ }
309+}
310+
311+
312+extension NewPostViewController: UITextFieldDelegate {
313+
314+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
315+ if textField == tagsField {
316+ textView.becomeFirstResponder()
317+ }
318+ return true
319+ }
320+
255321}
GDproject/Controller/ News and channels/NewsController.swift+87−19
@@ -11,24 +11,54 @@ import TinyConstraints
1111import Cartography
1212
1313protocol UpdateableWithChannel: class {
14− var channel: Channel? { get set }
14+ var channel: Model.Channels? { get set }
1515}
1616protocol NewPostDelegate {
1717 func addPost(post: Post)
1818}
19−
19+protocol UpdateableWithUser: class {
20+ var user: Model.Users? { get set }
21+}
2022// MARK:- Controller with posts and channels availiable.
2123// Search is availiable within every table (posts and channels). Has button-functionality for boths post and chnnels
2224class NewsController: UITableViewController, UISearchControllerDelegate, NewPostDelegate, UpdateableWithChannel, DataDelegate
2325{
24− func passData(for row: Int, channel: Channel) {
25− self.channel = channel
26+ func passData(for row: Int, channel: Model.Channels) {
27+ if channel.id == -1{
28+ self.channel = nil
29+ } else {
30+ self.channel = channel
31+ }
2632 }
2733
28− var channel: Channel?{
34+ var dictionary: [Int: Model.Users]? {
35+ didSet {
36+
37+ var newPosts: [Model.Posts] = []
38+
39+ posts!.forEach({ (post) in
40+
41+ newPosts.append(Model.Posts(body: post.body, authorId: post.authorId, id: post.id, user: dictionary![post.authorId]!, date: post.updated, tags: post.tags))
42+
43+ post.tags.forEach { Model.Channels.fullTags.insert($0) }
44+
45+ })
46+
47+
48+ news.dataSourse = newPosts
49+ tableView.reloadData()
50+ }
51+ }
52+
53+ var posts: [Model.Posts]? {
2954 didSet{
30− news.dataSourse = channel?.posts ?? []
31− navigationItem.title = channel?.title ?? ""
55+
56+ }
57+ }
58+
59+ var channel: Model.Channels? {
60+ didSet {
61+ navigationItem.title = channel?.name ?? ""
3262 }
3363 }
3464
@@ -36,37 +66,51 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
3666 var onSelectChannel: (() -> Void)?
3767
3868 func addPost(post: Post) {
39− news.dataSourse.insert(post, at: 0)
69+ //news.dataSourse.insert(post, at: 0)
4070 }
4171
4272 var searchController = UISearchController(searchResultsController: nil)
4373
4474 var news = NewsVC()
4575
76+ var refreshContr = UIRefreshControl()
77+
4678 override func viewDidLoad()
4779 {
4880 super.viewDidLoad()
49− channel = ChannelListController.dataSource[0]
50−
81+ navigationItem.title = "Loading ..."
82+ tableView.refreshControl = refreshContr
83+ // Configure Refresh Control
84+ refreshContr.addTarget(self, action: #selector(refreshPostsData(_:)), for: .valueChanged)
85+
5186 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
5287 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
5388
54− searchController.delegate = self
55− searchController.obscuresBackgroundDuringPresentation = false
56− navigationItem.searchController = searchController
57− definesPresentationContext = true
58− searchController.searchBar.placeholder = "Search anything"
89+ // setUpSearchContr()
5990
60− news.dataSourse = channel!.posts
6191 news.viewController = self
62−
63− news.type = .NONE
92+ news.type = .NEWS
6493
6594 setUpNavigationItemsforPosts()
6695
6796 //setUpBanner()
6897 }
6998
99+ func setUpSearchContr(){
100+ searchController.delegate = self
101+ searchController.obscuresBackgroundDuringPresentation = false
102+ navigationItem.searchController = searchController
103+ definesPresentationContext = true
104+ searchController.searchBar.placeholder = "Search anything"
105+ }
106+
107+ @objc func refreshPostsData( _ ff: UIRefreshControl){
108+ decideWhatChannelDisplay()
109+ }
110+
111+ deinit {
112+ print("news clear")
113+ }
70114
71115 let label : UILabel = {
72116 let label = UILabel()
@@ -82,7 +126,31 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
82126 super.viewWillAppear(animated)
83127 searchController.isActive = false
84128 // TODO:- display something if no posts are availiable
85− tableView.reloadData()
129+
130+ decideWhatChannelDisplay()
131+
132+ // tableView.reloadData()
133+ }
134+
135+ func decideWhatChannelDisplay(){
136+ if let channel = channel, let id = channel.id {
137+
138+ Model.getChannel(with: id) { [weak self] in
139+ self?.posts = $0.posts
140+ self?.dictionary = $0.users
141+ self?.refreshContr.endRefreshing()
142+ }
143+
144+ } else {
145+
146+ Model.getLast { [weak self] in
147+ self?.posts = $0.posts
148+ self?.dictionary = $0.users
149+ self?.refreshContr.endRefreshing()
150+ self?.navigationItem.title = "General"
151+ }
152+
153+ }
86154 }
87155
88156 func setUpNavigationItemsforPosts(){
GDproject/Controller/ News and channels/NewsVC.swift+44−4
@@ -7,14 +7,40 @@
77//
88
99import UIKit
10+import MarkdownKit
11+
12+struct PostCellData{
13+ var attributedData = NSAttributedString()
14+
15+ static func create(with attachments: [Model.Attachments]) -> NSAttributedString{
16+ var markdown = ""
17+
18+ attachments.forEach { (attachment) in markdown.append(attachment.markdown) }
19+
20+ let markdownParser = MarkdownParser(font: UIFont.systemFont(ofSize: 16))
21+ markdownParser.enabledElements = .disabledAutomaticLink
22+ markdownParser.code.textBackgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
23+
24+ return markdownParser.parse(markdown)
25+ }
26+}
1027
1128class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
1229
13− var dataSourse: [Post] = []
30+ var dataSourse: [Model.Posts] = [] {
31+ didSet {
32+ cellDataSourse = []
33+ dataSourse.forEach { (item) in
34+ cellDataSourse.append(PostCellData(attributedData: PostCellData.create(with: item.body)))
35+ }
36+ }
37+ }
38+
39+ var cellDataSourse: [PostCellData] = []
1440
1541 var type: HeaderType = .NONE
1642
17− var viewController: UIViewController?
43+ weak var viewController: UIViewController?
1844
1945 func numberOfSections(in tableView: UITableView) -> Int {
2046 return 1
@@ -24,7 +50,7 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
2450 {
2551 tableView.deselectRow(at: indexPath, animated: true)
2652 let vc = viewController!.storyboard!.instantiateViewController(withIdentifier: fullPostControllerId) as! FullPostController
27−
53+ vc.type = type
2854 vc.post = dataSourse[indexPath.row]
2955 viewController!.navigationController!.pushViewController(vc, animated: true)
3056 }
@@ -37,7 +63,21 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
3763 {
3864 let cell = tableView.dequeueReusableCell(withIdentifier: postCellId, for: indexPath) as! PostViewCell
3965
40− cell.fill(with: dataSourse[indexPath.row].dataArray, false, post: dataSourse[indexPath.row])
66+ cell.fill(with: cellDataSourse[indexPath.row].attributedData, false, post: dataSourse[indexPath.row])
67+
68+ switch type {
69+ case .NEWS:
70+ cell.onUserDisplay = { [weak self] (id) in
71+ let vc = self?.viewController!.storyboard!.instantiateViewController(withIdentifier: profileViewController) as! ProfileViewController
72+ vc.idProfile = id
73+ self?.viewController!.navigationController!.pushViewController(vc, animated: true)
74+ }
75+ case .NONE:
76+ cell.onUserDisplay = { (id) in
77+ print("tapped when profile is open already \(id)")
78+ }
79+ }
80+
4181 cell.selectionStyle = .none
4282 return cell
4383 }
GDproject/Controller/ News and channels/PostViewCell.swift+51−35
@@ -13,12 +13,14 @@ import TinyConstraints
1313
1414class PostViewCell: UITableViewCell
1515{
16− let nameLabel: UILabel = {
17− let label = UILabel()
18− label.textColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
19− label.text = "vbogomazova"
20− label.font = UIFont.boldSystemFont(ofSize: 16)
21− return label
16+ var onUserDisplay: ((Int)->())?
17+
18+ let nameLabel: UIButton = {
19+ let button = UIButton()
20+ button.setTitleColor(.black, for: .normal)
21+ button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
22+ button.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
23+ return button
2224 }()
2325
2426 let fullNameLabel: UILabel = {
@@ -29,7 +31,7 @@ class PostViewCell: UITableViewCell
2931 return label
3032 }()
3133
32− let commentsLabel: UILabel = {
34+ let timeLabel: UILabel = {
3335 let label = UILabel()
3436 label.font = UIFont.systemFont(ofSize: 12)
3537 label.textColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
@@ -51,50 +53,52 @@ class PostViewCell: UITableViewCell
5153 fatalError("init(coder:) has not been implemented")
5254 }
5355
54− func createTextView(with text: String) -> UITextView
56+ func createTextView(with text: NSAttributedString, _ isSelectable: Bool) -> UITextView
5557 {
56− let markdownParser = MarkdownParser(font: UIFont.systemFont(ofSize: 16))
57− let markdown = text
58− markdownParser.enabledElements = .disabledAutomaticLink
59− markdownParser.header.fontIncrease = 6
60− markdownParser.code.textBackgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
61−
6258 let textView = UITextView()
6359 textView.isScrollEnabled = false
6460 textView.isEditable = false
65− textView.isUserInteractionEnabled = false
66− textView.attributedText = markdownParser.parse(markdown)
61+ if isSelectable {
62+ //textView.isUserInteractionEnabled = false
63+ textView.isSelectable = true
64+ } else {
65+ textView.isUserInteractionEnabled = false
66+ }
67+
68+ textView.attributedText = text
6769 return textView
6870 }
6971
7072 var views: [UIView] = []
71− var data : [UIImage] = []
72− var post: Post?
73+ var post: Model.Posts?
7374
74− func fill(with info: [Media], _ isFullVersoin: Bool, post: Post)
75+ func fill(with info: NSAttributedString, _ isFullVersoin: Bool, post: Model.Posts)
7576 {
7677 self.post = post
7778 // important
7879 contentView.subviews.forEach({ $0.removeFromSuperview() })
7980
80− data = []
8181 views = []
82− for item in info.enumerated()
83− {
84− switch item.element
85− {
86− case .text(let text):
87− views.append(createTextView(with: text))
88− case .image(let images):
89− self.data = images
90− default:
91− break
92− }
82+ views.append(createTextView(with: info, isFullVersoin))
83+
84+ nameLabel.setTitle("\(post.user?.firstName ?? "") \(post.user?.lastName ?? "")", for: .normal)
85+ nameLabel.addTarget(self, action: #selector(displayProfile), for: .touchUpInside)
86+ if let user = post.user{
87+ fullNameLabel.text = "\(user.firstName) \(user.middleName) \(user.lastName)"
88+ }
89+ else {
90+ fullNameLabel.text = "\(post.authorId)"
9391 }
92+
9493 setUpInStackView(isFullVersoin)
9594 }
9695
96+ var hashtags = [String]()
97+
9798 func setUpInStackView(_ full : Bool){
99+
100+ hashtags = post!.tags
101+
98102 let nameStackView = UIStackView(arrangedSubviews: [nameLabel, fullNameLabel])
99103 nameStackView.axis = .vertical
100104 contentView.addSubview(nameStackView)
@@ -113,7 +117,7 @@ class PostViewCell: UITableViewCell
113117 scrollView.showsHorizontalScrollIndicator = false
114118
115119 var buttons: [UIButton] = []
116− for hash in post!.hashtags {
120+ for hash in hashtags {
117121 let button = UIButton()
118122 button.setTitle("#" + hash, for: .normal)
119123 //button.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
@@ -143,7 +147,13 @@ class PostViewCell: UITableViewCell
143147 contentView.addSubview(mainView)
144148 mainView.edgesToSuperview(excluding: [.top, .bottom])
145149 mainView.topToBottom(of: nameStackView, offset: 5)
146− mainView.height(30)
150+
151+ // TODO:- change!!!!!!!!!!!!
152+ if hashtags.isEmpty{
153+ mainView.height(0)
154+ } else {
155+ mainView.height(30)
156+ }
147157
148158 let stackView = MyStackView(arrangedSubviews: views)
149159 stackView.isFull = full
@@ -157,13 +167,19 @@ class PostViewCell: UITableViewCell
157167
158168 stackView.edgesToSuperview(excluding: [.top, .bottom], insets: .left(16) + .right(16))
159169
160− let commentsSharesStackView = UIStackView(arrangedSubviews: [ commentsLabel, shareButton])
170+ let commentsSharesStackView = UIStackView(arrangedSubviews: [ timeLabel, shareButton])
161171 contentView.addSubview(commentsSharesStackView)
162172
163− commentsLabel.text = "14.02.19 в 23:00"
173+ timeLabel.text = post!.convertDateFormatter()
174+
164175 commentsSharesStackView.axis = .horizontal
165176 commentsSharesStackView.distribution = .equalSpacing
166177 commentsSharesStackView.edgesToSuperview(excluding: .top, insets: .bottom(10) + .left(16) + .right(16))
167178 stackView.bottomToTop(of: commentsSharesStackView)
168179 }
180+
181+ @objc func displayProfile(){
182+ print("buttonTapped")
183+ onUserDisplay?(post!.authorId)
184+ }
169185}
GDproject/Controller/ News and channels/SimplifiedChannelsList.swiftnew file+94
@@ -0,0 +1,94 @@
1+//
2+// SimplifiedChannelsList.swift
3+// GDproject
4+//
5+// Created by cstore on 13/03/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+
10+/// class for ADDing person to existing (or new) channel: like playlist in itunes
11+import UIKit
12+import TinyConstraints
13+
14+class SimplifiedChannelsList: UITableViewController {
15+
16+ var user: Model.Users?
17+
18+ var dataSource: [Model.Channels]?{
19+ didSet{
20+ tableView.reloadData()
21+ }
22+ }
23+
24+ override func viewDidLoad() {
25+ super.viewDidLoad()
26+ setUpNavigationButtons()
27+ navigationItem.title = "All channels"
28+ navigationItem.largeTitleDisplayMode = .never
29+ self.navigationItem.setHidesBackButton(true, animated:true)
30+ tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
31+ }
32+
33+ override func viewWillAppear(_ animated: Bool) {
34+ Model.channelsList { [weak self] (channels) in
35+ self?.dataSource = channels
36+ }
37+ }
38+
39+ func setUpNavigationButtons(){
40+ navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelAction))
41+ }
42+
43+ func completedActions(with channel: Model.Channels){
44+ Model.updateChannel(with: channel)
45+ cancelAction()
46+ }
47+
48+ @objc func cancelAction(){
49+ let transition = CATransition()
50+ transition.duration = 0.5
51+ transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
52+ transition.type = CATransitionType.reveal
53+ transition.subtype = CATransitionSubtype.fromBottom
54+ navigationController?.view.layer.add(transition, forKey: nil)
55+ navigationController?.popViewController(animated: false)
56+ }
57+
58+ override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
59+ let viewHeader = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 30))
60+ viewHeader.backgroundColor = .white
61+ let label = UILabel()
62+
63+ label.text = "All channels"
64+ label.font = UIFont.boldSystemFont(ofSize: 22)
65+ label.textColor = .black
66+
67+ viewHeader.addSubview(label)
68+ label.edgesToSuperview(insets: .left(16))
69+ return viewHeader
70+ }
71+
72+ override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
73+ return 30
74+ }
75+
76+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
77+ return dataSource?.count ?? 0
78+ }
79+
80+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
81+ var channel = dataSource![indexPath.row]
82+ channel.people.append(user!.id)
83+ completedActions(with: channel)
84+ }
85+
86+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
87+ let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
88+
89+ cell.textLabel?.text = dataSource![indexPath.row].name
90+ cell.detailTextLabel?.text = "hello"
91+
92+ return cell
93+ }
94+}
GDproject/Controller/Coordinator tab bar?/TabbarCoordinator.swift+3−2
@@ -2,9 +2,10 @@ import UIKit
22
33class TabbarCoordinator{
44 var window: UIWindow!
5−
5+
66 func start(){
7− window.rootViewController = UIStoryboard.tabBarController()
7+ let tabbar = UIStoryboard.tabBarController()
8+ window.rootViewController = tabbar
89 }
910
1011 init(window: UIWindow) {
GDproject/Controller/Log In/LogInCoordinator.swiftnew file+48
@@ -0,0 +1,48 @@
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/LogInViewController.swift → GDproject/Controller/Log In/LogInViewController.swiftrenamed+27−12
@@ -12,6 +12,19 @@ class LogInViewController: UIViewController {
1212
1313 @IBOutlet weak var mailTextField: UITextField!
1414
15+ @IBOutlet weak var indicatorView: UIActivityIndicatorView!
16+
17+ var onLogIn: ((Int)->())?
18+
19+ var authenticateSucceeded: Bool? {
20+ didSet {
21+ if !authenticateSucceeded! {
22+ indicatorView.stopAnimating()
23+ indicatorView.isHidden = true
24+ }
25+ }
26+ }
27+
1528 static let titleColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5)
1629
1730 var bottomConstraint: NSLayoutConstraint?
@@ -40,14 +53,10 @@ class LogInViewController: UIViewController {
4053
4154 override func viewWillAppear(_ animated: Bool) {
4255 super.viewWillAppear(animated)
43− // MARK:- uncomment when mail registration will be availiable
44− //self.navigationController?.setNavigationBarHidden(true, animated: animated)
4556 }
4657
4758 override func viewWillDisappear(_ animated: Bool) {
4859 super.viewWillDisappear(animated)
49− // MARK:- uncomment when mail registration will be availiable
50− //self.navigationController?.setNavigationBarHidden(false, animated: animated)
5160 }
5261
5362 private func configureTapgesture(){
@@ -62,16 +71,20 @@ class LogInViewController: UIViewController {
6271 @objc func activateLogInProcess(){
6372 if logInButton.isEnabled {
6473 // MARK:- when log in is succeeded do I need to go there?
65− let vc = storyboard!.instantiateViewController(withIdentifier: newsController) as! NewsController
66− present(vc, animated: true) {
67− print("logged in")
74+ if let id = Int(mailTextField.text!){
75+ indicatorView.isHidden = false
76+ indicatorView.startAnimating()
77+ onLogIn?(id)
78+ view.endEditing(true)
79+ } else {
80+ logInButton.isEnabled = false
81+ logInButton.setTitleColor(LogInViewController.titleColor.withAlphaComponent(0.5), for: .normal)
6882 }
69− DataStorage.standard.setIsLoggedIn(value: true)
70− view.endEditing(true)
7183 }
7284 }
7385
7486 func setUpView(){
87+ indicatorView.isHidden = true
7588 mailTextField.delegate = self
7689 view.addSubview(keyboardBar)
7790
@@ -80,6 +93,10 @@ class LogInViewController: UIViewController {
8093
8194 setUpBarComponents()
8295
96+ configureKeyboardBehavior()
97+ }
98+
99+ func configureKeyboardBehavior(){
83100 bottomConstraint = NSLayoutConstraint(item: keyboardBar, attribute: .bottom, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0)
84101 view.addConstraint(bottomConstraint!)
85102
@@ -115,9 +132,7 @@ class LogInViewController: UIViewController {
115132
116133 UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: {
117134 self.view.layoutIfNeeded()
118− }) { (completed) in
119−
120− }
135+ })
121136 }
122137 }
123138
GDproject/Controller/Profile/BasicInfoController.swift+4−5
@@ -39,15 +39,14 @@ class BasicInfoController: UIViewController, UITableViewDelegate, UITableViewDat
3939
4040 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
4141 if indexPath.row == 0 {
42− let cell = tableView.dequeueReusableCell(withIdentifier: basicInfoCellId, for: indexPath) as! BasicInfoCell
43− cell.fill(title: dataSourse[indexPath.section].title)
42+ let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
43+ cell.textLabel?.text = dataSourse[indexPath.section].title
4444 cell.accessoryType = .disclosureIndicator
4545 cell.selectionStyle = .none
4646 return cell
4747 } else {
48− let cell = tableView.dequeueReusableCell(withIdentifier: infoCellId, for: indexPath) as! InfoCell
49− cell.fill(title: dataSourse[indexPath.section].sectionData[indexPath.row - 1])
50− cell.selectionStyle = .none
48+ let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
49+ cell.textLabel?.text = dataSourse[indexPath.section].sectionData[indexPath.row - 1]
5150 return cell
5251 }
5352 }
GDproject/Controller/Profile/ProfileViewController.swift+81−17
@@ -30,27 +30,53 @@ class ProfileViewController: UIViewController
3030
3131 @IBOutlet weak var newMessageButton: UIButton!
3232
33+ var protoDictionary: [Int: UIImage] = [9: #imageLiteral(resourceName: "9"), 5051: #imageLiteral(resourceName: "5051"), 69: #imageLiteral(resourceName: "69"), 42: #imageLiteral(resourceName: "42")]
34+
35+ func fill(with user: Model.Users){
36+ self.facultyLabel.text = "Студент: Факультет Компьютерных Наук"
37+ self.nameLabel.text = "\(user.firstName) \(user.middleName)"
38+ self.surnameLabel.text = "\(user.lastName)"
39+ self.profileImageView.image = protoDictionary[user.id]?.roundedImage
40+ self.placeLabel.text = "📍Москва, Кочновский пр.3"
41+ if user.id == DataStorage.standard.getUserId() {
42+ newMessageButton.isHidden = true
43+ } else {
44+ newMessageButton.isHidden = false
45+ }
46+ }
3347
34− func fill(with user: User){
35− self.facultyLabel.text = user.faculty ?? ""
36− self.nameLabel.text = "\(user.initials.name) \(user.initials.optional ?? "")"
37− self.surnameLabel.text = user.initials.surname
38− self.profileImageView.image = #imageLiteral(resourceName: "kitten").roundedImage
39− self.placeLabel.text = user.placeOfWork
48+ var user: Model.Users? {
49+ didSet {
50+ self.fill(with: user!)
51+ Model.getPostsForUser(with: user!.id) { [weak self] (posts) in
52+ self?.dataSourse = posts
53+ }
54+ navigationItem.title = "\(user!.firstName) \(user!.lastName)"
55+ }
4056 }
4157
42− var user: User = User(surname: "Богомазова", name: "Вероника", optional: "Львовна", emailName: "vbogomazova", id: 2, place: "📍Москва, Кочновский пр. 3", faculty: "Методист: Факультет Компьютерных наук")
58+ var basicInfo = BasicInfoController()
59+ var posts = NewsVC()
4360
44− let basicInfo = BasicInfoController()
45− let posts = NewsVC()
61+ var dataSourse: [Model.Posts]?{
62+ didSet{
63+
64+ var newPosts: [Model.Posts] = []
65+
66+ dataSourse?.forEach({ (post) in
67+ newPosts.append(Model.Posts(body: post.body, authorId: post.authorId, id: post.id, user: user!, date: post.updated, tags: post.tags))
68+ })
69+
70+ self.posts.dataSourse = newPosts
71+ tableView.reloadData()
72+ }
73+ }
4674
4775 override func viewDidLoad()
4876 {
4977 super.viewDidLoad()
5078
51− posts.dataSourse = [Post(dataArray: [.text("Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, quand un imprimeur anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. comme Aldus PageMaker. est qu'il possède une distribution de lettres plus ou moins normale, et en tout cas comparable avec celle du français standard. De nombreuses suites logicielles de mise en page ou éditeurs de sites Web ont fait du Lorem Ipsum leur faux texte par défaut, et une recherche pour 'Lorem Ipsum' vous conduira vers de nombreux sites qui n'en sont encore qu'à leur phase de construction. Plusieurs versions sont apparues avec le temps, parfois par accident, souvent intentionnellement (histoire d'y rajouter de petits clins d'oeil, voire des phrases embarassantes).")]), Post(dataArray: [.text("Il n'a pas fait que survivre cinq siècles, mais s'est aussi adapté à la bureautique informatique, sans que son contenu n'en soit modifié. Il a été popularisé dans les années 1960 grâce à la vente de feuilles Letraset contenant des passages du Lorem Ipsum, et, plus récemment, par son inclusion dans des applications de mise en page de texte, comme Aldus PageMaker. .")])]
5279
53− // basicInfo.dataSourse =
5480
5581 posts.viewController = self
5682 posts.type = .NONE
@@ -65,19 +91,36 @@ class ProfileViewController: UIViewController
6591
6692 posts.viewController = self
6793
68− fill(with: user)
6994 tableView.delegate = posts
7095 tableView.dataSource = posts
7196 tableView.reloadData()
7297 }
7398
99+ var idProfile: Int?
100+
74101 override func viewWillAppear(_ animated: Bool) {
102+ if idProfile == nil {
103+ idProfile = DataStorage.standard.getUserId()
104+ }
105+
106+ if let id = idProfile {
107+ if let user = Model.idUser[id] {
108+ self.user = user
109+ } else {
110+ Model.getUsers(for: [id]) { [weak self] (dic) in
111+ self?.user = dic[id]
112+ }
113+ }
114+
115+ // requst for postsforuser was here. moved because of concrr
116+ }
117+
75118 setUpNavigarionBar()
76119 }
77120
78121 func setUpNavigarionBar(){
79122 navigationController?.navigationBar.prefersLargeTitles = true
80− navigationItem.title = user.login
123+ //navigationItem.title = "\(user?.id ?? 0)"
81124 let uibarbutton = UIBarButtonItem(title: "More", style: .plain, target: self, action: #selector(showInformation))
82125 navigationItem.rightBarButtonItems = [uibarbutton]
83126 navigationItem.largeTitleDisplayMode = .always
@@ -95,7 +138,21 @@ class ProfileViewController: UIViewController
95138 // saved
96139
97140 let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
98− let editAction = UIAlertAction(title: "Edit profile", style: .default)
141+ let channelAction = UIAlertAction(title: "Add to a channel", style: .default){
142+ [weak self] (_) in
143+
144+ let vc = self?.storyboard?.instantiateViewController(withIdentifier: simplifiedChannelsList) as! SimplifiedChannelsList
145+
146+ vc.user = self?.user!
147+
148+ let transition = CATransition()
149+ transition.duration = 0.5
150+ transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
151+ transition.type = CATransitionType.moveIn
152+ transition.subtype = CATransitionSubtype.fromTop
153+ self?.navigationController?.view.layer.add(transition, forKey: nil)
154+ self?.navigationController?.pushViewController(vc, animated: false)
155+ }
99156 let settingsAction = UIAlertAction(title: "Setting", style: .default)
100157 { [weak self] (_) in
101158
@@ -104,17 +161,24 @@ class ProfileViewController: UIViewController
104161 }
105162
106163 let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
107− let shareAction = UIAlertAction(title: "Share", style: .default)
164+ let logoutAction = UIAlertAction(title: "Log out", style: .destructive)
165+ {
166+ (_) in
167+ DataStorage.standard.setIsLoggedIn(value: false, with: 0)
168+ }
108169
109− optionMenu.addAction(editAction)
170+ optionMenu.addAction(channelAction)
110171 optionMenu.addAction(settingsAction)
111172 optionMenu.addAction(copyLink)
112− optionMenu.addAction(shareAction)
173+ optionMenu.addAction(logoutAction)
113174 optionMenu.addAction(cancelAction)
114175
115176 self.present(optionMenu, animated: true, completion: nil)
116177 }
117178
179+ deinit {
180+ print("profile clear")
181+ }
118182
119183 @IBAction func valueChanged(_ sender: UISegmentedControl) {
120184 let index = sender.selectedSegmentIndex
GDproject/Controller/RepeatingTimer.swiftnew file+59
@@ -0,0 +1,59 @@
1+/// RepeatingTimer mimics the API of DispatchSourceTimer but in a way that prevents
2+/// crashes that occur from calling resume multiple times on a timer that is
3+/// already resumed (noted by https://github.com/SiftScience/sift-ios/issues/52
4+
5+import Foundation
6+
7+class RepeatingTimer {
8+
9+ let timeInterval: TimeInterval
10+
11+ init(timeInterval: TimeInterval) {
12+ self.timeInterval = timeInterval
13+ }
14+
15+ private lazy var timer: DispatchSourceTimer = {
16+ let t = DispatchSource.makeTimerSource()
17+ t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval)
18+ t.setEventHandler(handler: { [weak self] in
19+ self?.eventHandler?()
20+ })
21+ return t
22+ }()
23+
24+ var eventHandler: (() -> Void)?
25+
26+ private enum State {
27+ case suspended
28+ case resumed
29+ }
30+
31+ private var state: State = .suspended
32+
33+ deinit {
34+ timer.setEventHandler {}
35+ timer.cancel()
36+ /*
37+ If the timer is suspended, calling cancel without resuming
38+ triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
39+ */
40+ resume()
41+ eventHandler = nil
42+ }
43+
44+ func resume() {
45+ if state == .resumed {
46+ return
47+ }
48+ state = .resumed
49+ timer.resume()
50+ }
51+
52+ func suspend() {
53+ if state == .suspended {
54+ return
55+ }
56+ state = .suspended
57+ timer.suspend()
58+ }
59+}
GDproject/DataStorage.swift+22−7
@@ -7,6 +7,7 @@
77//
88
99import Foundation
10+import UIKit
1011
1112/**
1213 _DataStorage_ class is aimed to store user private settings
@@ -16,24 +17,37 @@ class DataStorage{
1617
1718 private init(){}
1819
20+ //weak var coordinator: LogInCoordinator?
1921 static let standard = DataStorage()
2022
21− // add channel
22−
23− // delete channel
24−
2523 /**
2624 Function for setting log in status of user
2725 */
28− func setIsLoggedIn(value: Bool){
26+ func setIsLoggedIn(value: Bool, with id: Int){
2927 UserDefaults.standard.set(value, forKey: UserDefaultsKeys.loggedIn.rawValue)
28+ setUserKey(with: id)
29+ isLoggedIn = UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue)
30+ }
31+
32+ func setUserKey(with id: Int){
33+ UserDefaults.standard.set(id, forKey: UserDefaultsKeys.id.rawValue)
34+ }
35+
36+ func getUserId() -> Int {
37+ return UserDefaults.standard.integer(forKey: UserDefaultsKeys.id.rawValue)
3038 }
3139
3240 /**
3341 Function to determine is user logged in already or not
3442 */
35− func isLoggedIn() -> Bool {
36− return UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue)
43+ var isLoggedIn: Bool = UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue) {
44+ didSet{
45+ if isLoggedIn && getUserId() != 0 {
46+ (UIApplication.shared.delegate as? AppDelegate)?.tabCoordinator.start()
47+ } else {
48+ (UIApplication.shared.delegate as? AppDelegate)?.logInAgain()
49+ }
50+ }
3751 }
3852}
3953
@@ -42,4 +56,5 @@ class DataStorage{
4256 */
4357enum UserDefaultsKeys: String{
4458 case loggedIn
59+ case id
4560}
GDproject/Extentions.swift+6−2
@@ -52,7 +52,11 @@ extension UIStoryboard {
5252 return UIStoryboard.userEdit.instantiateViewController(withIdentifier: "TabBar") as! UITabBarController
5353 }
5454
55− class func navChannels() ->UINavigationController{
56− return UIStoryboard.userEdit.instantiateViewController(withIdentifier: "navChannels") as! UINavigationController
55+ class func navRoot() -> UINavigationController{
56+ return UIStoryboard.userEdit.instantiateViewController(withIdentifier: "root") as! UINavigationController
57+ }
58+
59+ class func makeLogIn() -> LogInViewController {
60+ return UIStoryboard.userEdit.instantiateViewController(withIdentifier: logInController) as! LogInViewController
5761 }
5862}
GDproject/Simple model/Channel.swift+2−2
@@ -22,9 +22,9 @@ class Channel: Equatable {
2222 var hashtags: [String] = []
2323 var people: [String] = []
2424
25− var posts: [Post] = []
25+ var posts: [Model.Posts] = []
2626
27− init(title: String, subtitle: String, hashtags: [String], people: [String], posts: [Post])
27+ init(title: String, subtitle: String = "none", hashtags: [String] = [], people: [String] = [], posts: [Model.Posts])
2828 {
2929 self.title = title
3030 self.subtitle = subtitle
GDproject/Simple model/Model.swiftnew file+420
@@ -0,0 +1,420 @@
1+//
2+// Model.swift
3+// GDproject
4+//
5+// Created by cstore on 23/02/2019.
6+// Copyright © 2019 drHSE. All rights reserved.
7+//
8+
9+import Foundation
10+import Alamofire
11+
12+class Model{
13+
14+ static let invalidTocken = 498
15+
16+ private static var isValidTocken: ((Int)->())? = { responce in
17+ if responce == invalidTocken {
18+ DataStorage.standard.setIsLoggedIn(value: false, with: 0)
19+ }
20+ }
21+
22+ private static let baseUrl = "https://valera-denis.herokuapp.com"
23+ static let decoder = JSONDecoder()
24+
25+ static let authenticationURL = URL(string:"\(baseUrl)/authentication/login")!
26+ static let postsLastURL = URL(string:"\(baseUrl)/posts/last")!
27+ static let postsForUserURL = URL(string:"\(baseUrl)/posts/forUser")!
28+ static let postsPublishURL = URL(string:"\(baseUrl)/posts/publish")!
29+ static let usersURL = URL(string:"\(baseUrl)/users")!
30+ static let usersAllURL = URL(string:"\(baseUrl)/users/all")!
31+ static let channelsGetURL = URL(string: "\(baseUrl)/channels/get")!
32+ static let channelsUpdateURL = URL(string: "\(baseUrl)/channels/update")!
33+ static let channelsListURL = URL(string: "\(baseUrl)/channels")!
34+ static let channelsCreateURL = URL(string: "\(baseUrl)/channels/create")!
35+ static let channelsDeleteURL = URL(string: "\(baseUrl)/channels/delete")!
36+
37+
38+ struct QueryPosts: Codable{
39+ var users: [Int: Users]
40+ var posts: [Posts]
41+ }
42+
43+ struct Posts: Codable {
44+ var body: [Attachments]
45+ var authorId: Int
46+ var id: Int
47+ var user: Model.Users?
48+ var updated: String
49+ var tags: [String]
50+
51+ init(body: [Attachments], authorId: Int, id: Int, date: String,tags: [String]) {
52+ self.body = body
53+ self.authorId = authorId
54+ self.id = id
55+ self.updated = date
56+ self.tags = tags
57+ }
58+
59+ init(body: [Attachments], authorId: Int, id: Int, user: Model.Users, date: String, tags: [String]) {
60+ self.body = body
61+ self.authorId = authorId
62+ self.id = id
63+ self.user = user
64+ self.updated = date
65+ self.tags = tags
66+ }
67+
68+ func convertDateFormatter() -> String
69+ {
70+
71+ let dateFormatter = DateFormatter()
72+ dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"//this your string date format
73+ dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
74+ let date = dateFormatter.date(from: updated)
75+
76+ dateFormatter.dateFormat = "MMM d, yyyy HH:mm" ///this is what you want to convert format
77+ dateFormatter.timeZone = NSTimeZone.local
78+ let timeStamp = dateFormatter.string(from: date!)
79+
80+ return timeStamp
81+ }
82+ }
83+
84+ struct CreatedPost: Codable {
85+ var body = [Attachments]()
86+ var tags = [String]()
87+
88+ enum CodingKeys: String, CodingKey {
89+ case body
90+ case tags
91+ }
92+
93+ func encode(to encoder: Encoder) throws {
94+ var container = encoder.container(keyedBy: CodingKeys.self)
95+ try container.encode(body, forKey: .body)
96+ try container.encode(tags, forKey: .tags)
97+ }
98+ }
99+
100+ struct Users: Codable{
101+
102+ var middleName: String
103+ var lastName: String
104+ var firstName: String
105+ var id: Int
106+
107+ }
108+
109+ struct Attachments: Codable {
110+ var markdown: String
111+
112+ init(markdown: String) {
113+ self.markdown = markdown
114+ }
115+
116+ enum CodingKeys: String, CodingKey {
117+ case markdown
118+ }
119+
120+ func encode(to encoder: Encoder) throws {
121+ var container = encoder.container(keyedBy: CodingKeys.self)
122+ try container.encode(markdown, forKey: .markdown)
123+ }
124+ }
125+
126+ struct Channels: Codable {
127+
128+ static var fullTags = Set<String>()
129+ static var fullPeople = [Users]()
130+ static var fullPeopleDict = [Int:Users]()
131+
132+ var people: [Int]
133+ var name: String
134+ var id: Int?
135+ var tags: [String]
136+
137+ init(people: [Int], name: String, id: Int, tags: [String]) {
138+ self.id = id
139+ self.people = people
140+ self.tags = tags
141+ self.name = name
142+ }
143+
144+ init(people: [Int], name: String, tags: [String]) {
145+ self.people = people
146+ self.tags = tags
147+ self.name = name
148+ }
149+
150+ enum CodingKeys: String, CodingKey {
151+ case people
152+ case name
153+ case id
154+ case tags
155+ }
156+
157+ func encode(to encoder: Encoder) throws {
158+ var container = encoder.container(keyedBy: CodingKeys.self)
159+ try container.encode(people, forKey: .people)
160+ try container.encode(name, forKey: .name)
161+ try container.encode(id, forKey: .id)
162+ try container.encode(tags, forKey: .tags)
163+ }
164+ }
165+
166+ static func authenticate(with id: Int, completion: @escaping ((Bool)->())) {
167+
168+ let json: [String:Any] = ["authenticationId" : id]
169+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
170+
171+ var request = URLRequest(url: authenticationURL)
172+ request.httpMethod = "POST"
173+
174+ // insert json data to the request
175+ request.httpBody = jsonData
176+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
177+
178+ AF.request(request).response { (responce) in
179+
180+ guard let realResponse = responce.response, realResponse.statusCode == 200 else
181+ {
182+ print("Not a 200 response")
183+ completion(false)
184+ return
185+ }
186+
187+ guard let json = responce.data else { return }
188+ guard let personId = Int(String(data: json, encoding: String.Encoding.utf8)!) else {
189+ completion(false)
190+ return
191+ }
192+
193+ let fields = realResponse.allHeaderFields as? [String :String]
194+
195+ let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: realResponse.url!)
196+
197+ HTTPCookieStorage.shared.setCookie(cookies[0])
198+
199+ DataStorage.standard.setIsLoggedIn(value: true, with: personId)
200+ completion(true)
201+ }
202+ }
203+
204+
205+ static func getLast(completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){
206+ let jsonString = "30"
207+ var request = URLRequest(url: postsLastURL)
208+
209+ request.httpMethod = "POST"
210+
211+ // insert json data to the request
212+ request.httpBody = jsonString.data(using: .utf8)
213+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
214+
215+ AF.request(request).responseJSON {
216+ (response) in
217+
218+ isValidTocken?(response.response?.statusCode ?? 498)
219+
220+ guard let json = response.data else { return }
221+
222+ guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { print("no")
223+ return }
224+
225+ idUser = newQueery.users
226+ completion((newQueery.users, newQueery.posts))
227+ }
228+ }
229+
230+
231+ static func createAndPublish(body: [Attachments], tags: [String]){
232+ let jsonUpd = CreatedPost(body: body, tags: tags)
233+ var request = URLRequest(url: postsPublishURL)
234+
235+ request.httpMethod = "POST"
236+
237+ // insert json data to the request
238+ request.httpBody = try? JSONEncoder().encode(jsonUpd)
239+
240+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
241+ AF.request(request).response {
242+ (response) in
243+
244+ isValidTocken?(response.response?.statusCode ?? 498)
245+ }
246+ }
247+
248+
249+ static func getPostsForUser(with id: Int, completion: @escaping (([Posts])->())){
250+ let json = [
251+ "request" : id,
252+ "limit": 10
253+ ]
254+
255+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
256+
257+ var request = URLRequest(url: postsForUserURL)
258+ request.httpMethod = "POST"
259+
260+ // insert json data to the request
261+ request.httpBody = jsonData
262+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
263+
264+ AF.request(request).responseJSON {
265+ (response) in
266+
267+ isValidTocken?(response.response?.statusCode ?? 498)
268+
269+ guard let json = response.data else { return }
270+
271+ guard let newPost = try? decoder.decode(QueryPosts.self, from: json) else { return }
272+
273+ completion(newPost.posts)
274+ }
275+ }
276+
277+ static var idUser: [Int:Users] = [:]
278+
279+ static func getUsers(for ids: [Int], completion: @escaping (([Int:Users])->())){
280+ let json = "\(Set(ids))"
281+ print(json)
282+ var request = URLRequest(url: usersURL)
283+
284+ request.httpMethod = "POST"
285+
286+ // insert json data to the request
287+ request.httpBody = json.data(using: .utf8)
288+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
289+
290+ AF.request(request).responseJSON {
291+ (response) in
292+
293+ isValidTocken?(response.response?.statusCode ?? 498)
294+
295+ guard let json = response.data else { return }
296+
297+ guard let users = try? decoder.decode([Users].self, from: json) else { return }
298+
299+ var dict: [Int:Users] = [:]
300+ users.forEach({ (user) in
301+ dict[user.id] = user
302+ })
303+
304+ completion(dict)
305+ }
306+ }
307+
308+ // get channel (with id): in responce -- PostQuery
309+ static func getChannel(with channelId: Int, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){
310+ let json = [
311+ "request" : channelId,
312+ "limit": 10
313+ ]
314+
315+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
316+
317+ var request = URLRequest(url: channelsGetURL)
318+ request.httpMethod = "POST"
319+
320+ // insert json data to the request
321+ request.httpBody = jsonData
322+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
323+
324+ AF.request(request).responseJSON {
325+ (response) in
326+
327+ isValidTocken?(response.response?.statusCode ?? 498)
328+
329+
330+ guard let json = response.data else { return }
331+
332+ guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return }
333+
334+ idUser = newQueery.users
335+ completion((newQueery.users, newQueery.posts))
336+ }
337+ }
338+
339+
340+ static func createChannel(with channel: Channels) {
341+
342+ var request = URLRequest(url: channelsCreateURL)
343+ request.httpMethod = "POST"
344+ request.httpBody = try? JSONEncoder().encode(channel)
345+
346+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
347+ AF.request(request).response {
348+ (response) in
349+
350+ isValidTocken?(response.response?.statusCode ?? 498)
351+ }
352+
353+ }
354+
355+ static func channelsList(completion: @escaping (([Channels])->())){
356+ var request = URLRequest(url: channelsListURL)
357+ request.httpMethod = "POST"
358+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
359+
360+ AF.request(request).responseJSON { (response) in
361+
362+ isValidTocken?(response.response?.statusCode ?? 498)
363+
364+ guard let json = response.data else { return }
365+
366+ guard let channelsList = try? decoder.decode([Channels].self, from: json) else { return }
367+
368+ completion(channelsList)
369+ }
370+ }
371+
372+ static func channelsDelete(by id: Int, completion: @escaping(()->())){
373+ var request = URLRequest(url: channelsDeleteURL)
374+ request.httpMethod = "POST"
375+ request.httpBody = "\(id)".data(using: .utf8)
376+ print("\(id)")
377+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
378+
379+ AF.request(request).response { (response) in
380+ isValidTocken?(response.response?.statusCode ?? 498)
381+ }
382+ completion()
383+ }
384+
385+ static func updateChannel(with channel: Channels) {
386+
387+ var request = URLRequest(url: channelsUpdateURL)
388+ request.httpMethod = "POST"
389+ request.httpBody = try? JSONEncoder().encode(channel)
390+
391+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
392+ AF.request(request).response {
393+ (response) in
394+
395+ isValidTocken?(response.response?.statusCode ?? 498)
396+ }
397+ }
398+
399+ static func usersAllGet() {
400+ var request = URLRequest(url: usersAllURL)
401+ request.httpMethod = "POST"
402+
403+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
404+ AF.request(request).response {
405+ (response) in
406+ guard let json = response.data else { return }
407+
408+ guard let users = try? decoder.decode([Users].self, from: json) else { return }
409+
410+ Channels.fullPeople = users
411+ Channels.fullPeopleDict = users.reduce([Int: Users]()) { (dict, person) -> [Int: Users] in
412+ var dict = dict
413+ dict[person.id] = person
414+ return dict
415+ }
416+
417+ isValidTocken?(response.response?.statusCode ?? 498)
418+ }
419+ }
420+}
GDproject/Supporting files/AppDelegate.swift+16−6
@@ -11,7 +11,8 @@ import UIKit
1111@UIApplicationMain
1212class AppDelegate: UIResponder, UIApplicationDelegate {
1313
14− var coordinator: ChannelsCoordinator!
14+
15+ var coordinator: LogInCoordinator!
1516 var tabCoordinator: TabbarCoordinator!
1617
1718 var window: UIWindow?
@@ -19,16 +20,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
1920 return self.window!.rootViewController as? UINavigationController
2021 }
2122
22− private func makeCoordinator(with: UINavigationController) -> ChannelsCoordinator {
23− return ChannelsCoordinator(currentChannel: Channel(title: "example", subtitle: "ex", hashtags: ["none"], people: ["none"], posts: []), navigationController: with)
23+ func logInAgain(){
24+ let root = UIStoryboard.navRoot()
25+ window?.rootViewController = root
26+ window?.makeKeyAndVisible()
27+ coordinator = LogInCoordinator(navigationController: root, window: UIApplication.shared.keyWindow!)
28+ coordinator!.start()
2429 }
2530
31+
2632 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
2733 {
28−
29− // TODO:- determine what to do here: log in or tabbar
34+ coordinator = LogInCoordinator(navigationController: rootController!, window: window!)
3035 tabCoordinator = TabbarCoordinator(window: window!)
31− tabCoordinator.start()
36+ // TODO:- determine what to do here: log in or tabbar
37+ if DataStorage.standard.isLoggedIn {
38+ tabCoordinator.start()
39+ } else {
40+ coordinator.start()
41+ }
3242
3343 return true
3444 }
GDproject/Supporting files/Assets.xcassets/1.imageset/1.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/1.imageset/Contents.jsonnew file+25
@@ -0,0 +1,25 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "1.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ },
21+ "properties" : {
22+ "template-rendering-intent" : "template",
23+ "preserves-vector-representation" : true
24+ }
25+}
GDproject/Supporting files/Assets.xcassets/2.imageset/2.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/2.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "2.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/3.imageset/3.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/3.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "3.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/4.imageset/4.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/4.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "4.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/42.imageset/42.pngnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/42.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "42.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/5.imageset/5.pdfnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/5.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "5.pdf",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/5051.imageset/5051.pngnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/5051.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "5051.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/69.imageset/69.pngnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/69.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "69.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/9.imageset/9.pngnew file

Binary file not shown.

GDproject/Supporting files/Assets.xcassets/9.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "9.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/share.imageset/Contents.jsonnew file+21
@@ -0,0 +1,21 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "share.png",
6+ "scale" : "1x"
7+ },
8+ {
9+ "idiom" : "universal",
10+ "scale" : "2x"
11+ },
12+ {
13+ "idiom" : "universal",
14+ "scale" : "3x"
15+ }
16+ ],
17+ "info" : {
18+ "version" : 1,
19+ "author" : "xcode"
20+ }
21+}
GDproject/Supporting files/Assets.xcassets/share.imageset/share.pngnew file

Binary file not shown.