From 9e6ff4894be62e27d20b07d07d47a3dc4c5b76dd Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Tue, 19 Mar 2019 21:50:19 +0300 Subject: [PATCH 01/34] Trying to embed coordinators in project. Broke log in a little bit. Can be a lot of issues now. --- Cartfile | 1 + GDproject.xcodeproj/project.pbxproj | 83 ++++++-- GDproject/Base.lproj/Main.storyboard | 76 +------- GDproject/Constants.swift | 4 + .../ChannelsCoordinator.swift | 55 ------ .../Coordinators/ApplicationCoordinator.swift | 69 +++++++ .../Coordinators/BaseCoordinator.swift | 47 +++++ .../Coordinators/ChannelsCoordinator.swift | 28 +++ .../Coordinators/LogInCoordinator.swift | 41 ++++ .../Coordinators/ProfileCoordinator.swift | 52 +++++ .../Coordinators/TabbarController.swift | 45 +++++ .../Coordinators/TabbarCoordinator.swift | 57 ++++++ .../Controller/Log In/LogInCoordinator.swift | 48 ----- .../Controller/Log In/LogInCoordinator1.swift | 48 +++++ .../Log In/LogInViewController.swift | 181 ++++++++---------- .../Profile/ProfileViewController.swift | 46 ++--- GDproject/Controller/RepeatingTimer.swift | 59 ------ GDproject/DataStorage.swift | 10 +- GDproject/Supporting files/AppDelegate.swift | 52 +---- 19 files changed, 561 insertions(+), 441 deletions(-) delete mode 100644 GDproject/Controller/ News and channels/ChannelsCoordinator.swift create mode 100644 GDproject/Controller/Coordinators/ApplicationCoordinator.swift create mode 100644 GDproject/Controller/Coordinators/BaseCoordinator.swift create mode 100644 GDproject/Controller/Coordinators/ChannelsCoordinator.swift create mode 100644 GDproject/Controller/Coordinators/LogInCoordinator.swift create mode 100644 GDproject/Controller/Coordinators/ProfileCoordinator.swift create mode 100644 GDproject/Controller/Coordinators/TabbarController.swift create mode 100644 GDproject/Controller/Coordinators/TabbarCoordinator.swift delete mode 100644 GDproject/Controller/Log In/LogInCoordinator.swift create mode 100644 GDproject/Controller/Log In/LogInCoordinator1.swift delete mode 100644 GDproject/Controller/RepeatingTimer.swift diff --git a/Cartfile b/Cartfile index 72919cd..cc276a7 100644 --- a/Cartfile +++ b/Cartfile @@ -5,3 +5,4 @@ github "macteo/Marklight" github "ElaWorkshop/TagListView" github "Alamofire/Alamofire" "5.0.0-beta.2" github "whitesmith/WSTagsField" +github "ReactiveCocoa/ReactiveCocoa" ~> 8.0 \ No newline at end of file diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index 8079f0c..e0c4785 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -15,14 +15,16 @@ 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; }; 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; }; 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 1253867D2223DC9F00CC5EA7 /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */; }; 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; }; 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; }; 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; }; 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1288B5CD221F1158002BE6B1 /* DataStorage.swift */; }; 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; }; 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; }; - 1291BE3622218DBF009D3F23 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator.swift */; }; + 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */; }; + 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */; }; + 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */; }; + 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */; }; 12D7D133221C321600B35452 /* ChannelListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D132221C321600B35452 /* ChannelListController.swift */; }; 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D134221C42B700B35452 /* ChannelController.swift */; }; 12D7D137221D78E800B35452 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D136221D78E800B35452 /* Channel.swift */; }; @@ -50,9 +52,17 @@ 12E36DCE22144756006FCDD7 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DCD22144756006FCDD7 /* Constants.swift */; }; 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DD022148122006FCDD7 /* FullPostController.swift */; }; 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DD422156559006FCDD7 /* MyStackView.swift */; }; - 12E36DD8221594A9006FCDD7 /* TagListView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DD622159493006FCDD7 /* TagListView.framework */; }; - 12E36DD9221594A9006FCDD7 /* TagListView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DD622159493006FCDD7 /* TagListView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12E36DDB221594B1006FCDD7 /* MarkdownKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DB022143D47006FCDD7 /* MarkdownKit.framework */; }; + 12F3D6B0224106F700A69214 /* TabbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F3D6AF224106F700A69214 /* TabbarController.swift */; }; + 12F3D6B22241097B00A69214 /* ProfileCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F3D6B12241097B00A69214 /* ProfileCoordinator.swift */; }; + 12F3D6B4224112D600A69214 /* ReactiveMapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B3224112D600A69214 /* ReactiveMapKit.framework */; }; + 12F3D6B6224112E100A69214 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */; }; + 12F3D6B8224112E900A69214 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B7224112E800A69214 /* Result.framework */; }; + 12F3D6BB224112FD00A69214 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */; }; + 12F3D6BC224112FD00A69214 /* ReactiveCocoa.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12F3D6BE2241130000A69214 /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B7224112E800A69214 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12F3D6C02241130200A69214 /* ReactiveSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12F3D6C22241130400A69214 /* ReactiveMapKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B3224112D600A69214 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -63,10 +73,13 @@ dstSubfolderSpec = 10; files = ( 12E36DC0221441DB006FCDD7 /* Marklight.framework in Embed Frameworks */, + 12F3D6C22241130400A69214 /* ReactiveMapKit.framework in Embed Frameworks */, 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */, 12E36DC4221441EA006FCDD7 /* Cartography.framework in Embed Frameworks */, - 12E36DD9221594A9006FCDD7 /* TagListView.framework in Embed Frameworks */, + 12F3D6BE2241130000A69214 /* Result.framework in Embed Frameworks */, 12E36DBD221441D5006FCDD7 /* TinyConstraints.framework in Embed Frameworks */, + 12F3D6C02241130200A69214 /* ReactiveSwift.framework in Embed Frameworks */, + 12F3D6BC224112FD00A69214 /* ReactiveCocoa.framework in Embed Frameworks */, 123AD0E5223C1C4300326173 /* WSTagsField.framework in Embed Frameworks */, 12E36DC2221441E1006FCDD7 /* MarkdownKit.framework in Embed Frameworks */, ); @@ -82,14 +95,16 @@ 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = ""; }; 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; 124CC7042221C2BA009DF531 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; }; - 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatingTimer.swift; sourceTree = ""; }; 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; }; 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = ""; }; 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = ""; }; 1288B5CD221F1158002BE6B1 /* DataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStorage.swift; sourceTree = ""; }; 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = ""; }; 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = ""; }; - 1291BE3522218DBF009D3F23 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = ""; }; + 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator1.swift; sourceTree = ""; }; + 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; + 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; }; + 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = ""; }; 12D7D132221C321600B35452 /* ChannelListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelListController.swift; sourceTree = ""; }; 12D7D134221C42B700B35452 /* ChannelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelController.swift; sourceTree = ""; }; 12D7D136221D78E800B35452 /* Channel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channel.swift; sourceTree = ""; }; @@ -116,7 +131,12 @@ 12E36DCD22144756006FCDD7 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 12E36DD022148122006FCDD7 /* FullPostController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullPostController.swift; sourceTree = ""; }; 12E36DD422156559006FCDD7 /* MyStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyStackView.swift; sourceTree = ""; }; - 12E36DD622159493006FCDD7 /* TagListView.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TagListView.framework; path = Carthage/Build/iOS/TagListView.framework; sourceTree = ""; }; + 12F3D6AF224106F700A69214 /* TabbarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarController.swift; sourceTree = ""; }; + 12F3D6B12241097B00A69214 /* ProfileCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCoordinator.swift; sourceTree = ""; }; + 12F3D6B3224112D600A69214 /* ReactiveMapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveMapKit.framework; path = Carthage/Build/iOS/ReactiveMapKit.framework; sourceTree = ""; }; + 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/iOS/ReactiveSwift.framework; sourceTree = ""; }; + 12F3D6B7224112E800A69214 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = ""; }; + 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = Carthage/Build/iOS/ReactiveCocoa.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -125,9 +145,12 @@ buildActionMask = 2147483647; files = ( 12E36DB522143D54006FCDD7 /* TinyConstraints.framework in Frameworks */, + 12F3D6B4224112D600A69214 /* ReactiveMapKit.framework in Frameworks */, 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */, + 12F3D6BB224112FD00A69214 /* ReactiveCocoa.framework in Frameworks */, + 12F3D6B8224112E900A69214 /* Result.framework in Frameworks */, + 12F3D6B6224112E100A69214 /* ReactiveSwift.framework in Frameworks */, 12E36DDB221594B1006FCDD7 /* MarkdownKit.framework in Frameworks */, - 12E36DD8221594A9006FCDD7 /* TagListView.framework in Frameworks */, 12E36DAF22143D40006FCDD7 /* Cartography.framework in Frameworks */, 123AD0E3223C1C3900326173 /* WSTagsField.framework in Frameworks */, 12E36DB322143D4E006FCDD7 /* Marklight.framework in Frameworks */, @@ -150,19 +173,25 @@ path = Profile; sourceTree = ""; }; - 1291BE32222154C3009D3F23 /* Coordinator tab bar? */ = { + 1291BE32222154C3009D3F23 /* Coordinators */ = { isa = PBXGroup; children = ( + 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */, + 12F3D6AF224106F700A69214 /* TabbarController.swift */, + 12F3D6B12241097B00A69214 /* ProfileCoordinator.swift */, + 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */, + 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */, + 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */, 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */, ); - path = "Coordinator tab bar?"; + path = Coordinators; sourceTree = ""; }; 1291BE3722218DC4009D3F23 /* Log In */ = { isa = PBXGroup; children = ( 123E37A4221F1B3200F6E42A /* LogInViewController.swift */, - 1291BE3522218DBF009D3F23 /* LogInCoordinator.swift */, + 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */, ); path = "Log In"; sourceTree = ""; @@ -170,9 +199,12 @@ 12E36D8F221424EA006FCDD7 = { isa = PBXGroup; children = ( + 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */, + 12F3D6B7224112E800A69214 /* Result.framework */, + 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */, + 12F3D6B3224112D600A69214 /* ReactiveMapKit.framework */, 123AD0E2223C1C3900326173 /* WSTagsField.framework */, 124CC7042221C2BA009DF531 /* Alamofire.framework */, - 12E36DD622159493006FCDD7 /* TagListView.framework */, 12E36DB422143D54006FCDD7 /* TinyConstraints.framework */, 12E36DB222143D4E006FCDD7 /* Marklight.framework */, 12E36DB022143D47006FCDD7 /* MarkdownKit.framework */, @@ -232,8 +264,7 @@ 12E36DCF22145923006FCDD7 /* Controller */ = { isa = PBXGroup; children = ( - 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */, - 1291BE32222154C3009D3F23 /* Coordinator tab bar? */, + 1291BE32222154C3009D3F23 /* Coordinators */, 1291BE3722218DC4009D3F23 /* Log In */, 1288B5CC221F0EFE002BE6B1 /* Profile */, 12E36DD322156519006FCDD7 /* News and channels */, @@ -250,7 +281,6 @@ 12E36DC922144635006FCDD7 /* NewPostViewController.swift */, 12E36DD022148122006FCDD7 /* FullPostController.swift */, 12E36D9D221424EA006FCDD7 /* NewsController.swift */, - 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */, 125BD5802217314A008A3575 /* NewsVC.swift */, 12E36DCB22144725006FCDD7 /* PostViewCell.swift */, ); @@ -297,6 +327,11 @@ TargetAttributes = { 12E36D97221424EA006FCDD7 = { CreatedOnToolsVersion = 10.1; + SystemCapabilities = { + com.apple.Keychain = { + enabled = 0; + }; + }; }; }; }; @@ -337,9 +372,10 @@ buildActionMask = 2147483647; files = ( 124CC7032221C00C009DF531 /* Model.swift in Sources */, + 12F3D6B0224106F700A69214 /* TabbarController.swift in Sources */, 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */, 12D7D137221D78E800B35452 /* Channel.swift in Sources */, - 1291BE3622218DBF009D3F23 /* LogInCoordinator.swift in Sources */, + 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */, 12E36DB922144016006FCDD7 /* User.swift in Sources */, 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */, 125BD5812217314A008A3575 /* NewsVC.swift in Sources */, @@ -352,15 +388,18 @@ 125BD57F22171D73008A3575 /* Extentions.swift in Sources */, 12D7D133221C321600B35452 /* ChannelListController.swift in Sources */, 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */, + 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */, 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */, 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */, 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */, + 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */, + 12F3D6B22241097B00A69214 /* ProfileCoordinator.swift in Sources */, 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */, - 1253867D2223DC9F00CC5EA7 /* RepeatingTimer.swift in Sources */, 12DB7FDB2218590C0096878E /* InfoCell.swift in Sources */, 12DB7FD922181E660096878E /* BasicInfoCell.swift in Sources */, 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */, 12E36D9C221424EA006FCDD7 /* AppDelegate.swift in Sources */, + 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */, 12DB7FD722181CD20096878E /* BasicInfoController.swift in Sources */, 12E36DCE22144756006FCDD7 /* Constants.swift in Sources */, 12DB7FDD221872F80096878E /* SettingsViewController.swift in Sources */, @@ -510,6 +549,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 9Z6LFGKA9G; FRAMEWORK_SEARCH_PATHS = ( @@ -521,8 +561,9 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = rednikina.com.drHSE.GDproject; + PRODUCT_BUNDLE_IDENTIFIER = rednikina.com.drHSE.GDproject.sos; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -532,6 +573,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 9Z6LFGKA9G; FRAMEWORK_SEARCH_PATHS = ( @@ -543,8 +585,9 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = rednikina.com.drHSE.GDproject; + PRODUCT_BUNDLE_IDENTIFIER = rednikina.com.drHSE.GDproject.sos; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index ca4d690..f828dc1 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -20,7 +20,7 @@ - + @@ -416,55 +416,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -476,9 +427,6 @@ - - - @@ -515,31 +463,15 @@ - - - - - - - - - - - - - - - - - + - + diff --git a/GDproject/Constants.swift b/GDproject/Constants.swift index 1a937f7..eba0588 100644 --- a/GDproject/Constants.swift +++ b/GDproject/Constants.swift @@ -6,6 +6,7 @@ // Copyright © 2019 drHSE. All rights reserved. // +import UIKit /// constant for cell in posts NewsController.swift let postCellId = "PostCell" @@ -46,3 +47,6 @@ let logInController = "LogInController" let profileViewController = "ProfileViewController" let simplifiedChannelsList = "SimplifiedChannelsList" + +let blueSystemColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5) + diff --git a/GDproject/Controller/ News and channels/ChannelsCoordinator.swift b/GDproject/Controller/ News and channels/ChannelsCoordinator.swift deleted file mode 100644 index 6d8ab72..0000000 --- a/GDproject/Controller/ News and channels/ChannelsCoordinator.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// ChannelsCoordinator.swift -// GDproject -// -// Created by cstore on 23/02/2019. -// Copyright © 2019 drHSE. All rights reserved. -// - -import Foundation -import UIKit - -final class ChannelsCoordinator{ - - // MARK: - Properties - private var channel: Model.Channels { didSet { updateInterfaces() } } - private weak var navigationController: UINavigationController? - - // MARK:- Init - init(currentChannel: Model.Channels, navigationController: UINavigationController) { - self.channel = currentChannel - self.navigationController = navigationController - } - - func start(){ - showCurrentChannel() - } - - // MARK: - Private implementation - private func showListOfChannels(){ - let controller = UIStoryboard.makeChannelsListController() - controller.onChannelSelected = { [weak self] channel - in - self?.channel = channel - _ = self?.navigationController?.popViewController(animated: true) - } - navigationController?.pushViewController(controller, animated: true) - } - - private func showCurrentChannel(){ - let controller = UIStoryboard.makeNewsController() - controller.channel = channel - controller.onSelectChannel = { [weak self] in - self?.showListOfChannels() - } - navigationController?.setViewControllers([controller], animated: false) - //navigationController?.pushViewController(controller, animated: false) - } - - // MARK:- update only viewControllers which are depandable on chosen channel - private func updateInterfaces() { - navigationController?.viewControllers.forEach { - ($0 as? UpdateableWithChannel)?.channel = channel - } - } -} diff --git a/GDproject/Controller/Coordinators/ApplicationCoordinator.swift b/GDproject/Controller/Coordinators/ApplicationCoordinator.swift new file mode 100644 index 0000000..fa3c122 --- /dev/null +++ b/GDproject/Controller/Coordinators/ApplicationCoordinator.swift @@ -0,0 +1,69 @@ +// +// ApplicationCoordinator.swift +// RxSwift +// +// Created by cstore on 04/03/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import Foundation +import UIKit + + + +fileprivate enum LaunchInstructor { + case main, auth + + static func configure( + isAutorized: Bool = DataStorage.standard.isLoggedIn) -> LaunchInstructor { + + if isAutorized{ + return .main + } else { + return .auth + } + } +} + +final class ApplicationCoordinator: BaseCoordinator{ + private var window: UIWindow! + + init(window: UIWindow) { + self.window = window + } + + private var instructor: LaunchInstructor { + return LaunchInstructor.configure() + } + + override func start() { + switch instructor { + case .auth: runAuthFlow() + case .main: runMainFlow() + } + } + + private func runAuthFlow() { + let coordinator = LogInCoordinator(window: window) + coordinator.didEndFlow = { [weak self, weak coordinator] in + self?.start() + self?.removeDependency(coordinator) + } + addDependency(coordinator) + coordinator.start() + } + + private func runMainFlow() { + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let tabBar = storyboard.instantiateViewController(withIdentifier: "TabbarController") as! TabbarController + let coordinator = TabBarCoordinator(tabbarView: tabBar, window: window!) + + coordinator.didEndFlow = { [weak self, weak coordinator] in + self?.start() + self?.removeDependency(coordinator) + } + + addDependency(coordinator) + coordinator.start() + } +} diff --git a/GDproject/Controller/Coordinators/BaseCoordinator.swift b/GDproject/Controller/Coordinators/BaseCoordinator.swift new file mode 100644 index 0000000..2a01245 --- /dev/null +++ b/GDproject/Controller/Coordinators/BaseCoordinator.swift @@ -0,0 +1,47 @@ +// +// BaseCoordinator.swift +// RxSwift +// +// Created by cstore on 02/03/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import Foundation +import UIKit + +protocol Coordinator: class { + func start() +} + +class BaseCoordinator: Coordinator { + + var childCoordinators: [Coordinator] = [] + + func start() { + + } + + // add only unique object + func addDependency(_ coordinator: Coordinator) { + guard !childCoordinators.contains(where: { $0 === coordinator }) else { return } + childCoordinators.append(coordinator) + } + + func removeDependency(_ coordinator: Coordinator?) { + guard + childCoordinators.isEmpty == false, + let coordinator = coordinator + else { return } + + // Clear child-coordinators recursively + if let coordinator = coordinator as? BaseCoordinator, !coordinator.childCoordinators.isEmpty { + coordinator.childCoordinators + .filter({ $0 !== coordinator }) + .forEach({ coordinator.removeDependency($0) }) + } + for (index, element) in childCoordinators.enumerated() where element === coordinator { + childCoordinators.remove(at: index) + break + } + } +} diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift new file mode 100644 index 0000000..ad5b114 --- /dev/null +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -0,0 +1,28 @@ +// +// ChannelsCoordinator.swift +// RxSwift +// +// Created by cstore on 03/03/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import Foundation +import UIKit + +class ChannelsCoordinator: BaseCoordinator{ + private weak var navigationController: UINavigationController? + + init(nc: UINavigationController) { + self.navigationController = nc + } + + override func start() { + show() + } + + private func show() { + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let channels = storyboard.instantiateViewController(withIdentifier: channelListControllerId) as! ChannelListController + navigationController?.setViewControllers([channels], animated: false) + } +} diff --git a/GDproject/Controller/Coordinators/LogInCoordinator.swift b/GDproject/Controller/Coordinators/LogInCoordinator.swift new file mode 100644 index 0000000..f603f3b --- /dev/null +++ b/GDproject/Controller/Coordinators/LogInCoordinator.swift @@ -0,0 +1,41 @@ +// +// LogInCoordinator.swift +// RxSwift +// +// Created by cstore on 01/03/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class LogInCoordinator: BaseCoordinator { + + var didEndFlow: (()->())? + var window: UIWindow! + var navigationController: UINavigationController? + + init(window: UIWindow) { + self.window = window + self.window?.rootViewController = UINavigationController() + self.navigationController = window.rootViewController as? UINavigationController + } + + override func start(){ + let logInVC = LogInViewController() + logInVC.authenticate = { [weak self] (id) in + + Model.authenticate(with: id) { + (res) in + + if (res) { + DataStorage.standard.setUserKey(with: id) + self?.didEndFlow?() + } + else { + DataStorage.standard.setUserKey(with: 0) + } + } + } + navigationController?.pushViewController(logInVC, animated: false) + } +} diff --git a/GDproject/Controller/Coordinators/ProfileCoordinator.swift b/GDproject/Controller/Coordinators/ProfileCoordinator.swift new file mode 100644 index 0000000..0577612 --- /dev/null +++ b/GDproject/Controller/Coordinators/ProfileCoordinator.swift @@ -0,0 +1,52 @@ +// +// ProfileCoordinator.swift +// RxSwift +// +// Created by cstore on 02/03/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import Foundation +import UIKit + +class ProfileCoordinator: BaseCoordinator { + + var didEndSession: (()->())? + + private weak var navigationController: UINavigationController? + + init(nc: UINavigationController) { + self.navigationController = nc + } + + override func start() { + show() + } + + private func show() { + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let profile = storyboard.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController + profile.logOut = { [weak self] in + DataStorage.standard.setIsLoggedIn(value: false, with: 0) + self?.didEndSession?() + } + profile.onSettings = { [weak self, weak storyboard] in + let vc = storyboard?.instantiateViewController(withIdentifier: "SettingsViewController") as! SettingsViewController + self?.navigationController?.pushViewController(vc, animated: true) + } + + profile.onChannelsListToAddAPerson = { [weak self, weak storyboard] (user) in + let vc = storyboard?.instantiateViewController(withIdentifier: simplifiedChannelsList) as! SimplifiedChannelsList + vc.user = user + + let transition = CATransition() + transition.duration = 0.5 + transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) + transition.type = CATransitionType.moveIn + transition.subtype = CATransitionSubtype.fromTop + self?.navigationController?.view.layer.add(transition, forKey: nil) + self?.navigationController?.pushViewController(vc, animated: false) + } + navigationController?.setViewControllers([profile], animated: false) + } +} diff --git a/GDproject/Controller/Coordinators/TabbarController.swift b/GDproject/Controller/Coordinators/TabbarController.swift new file mode 100644 index 0000000..6c78575 --- /dev/null +++ b/GDproject/Controller/Coordinators/TabbarController.swift @@ -0,0 +1,45 @@ +// +// TabbarController.swift +// RxSwift +// +// Created by cstore on 03/03/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import Foundation +import UIKit + +protocol TabbarView: class { + var onChannelsFlowSelect: ((UINavigationController) -> ())? { get set } + var onProfileFlowSelect: ((UINavigationController) -> ())? { get set } + var onViewDidLoad: ((UINavigationController) -> ())? { get set } +} + +final class TabbarController: UITabBarController, UITabBarControllerDelegate, TabbarView { + + + var onChannelsFlowSelect: ((UINavigationController) -> ())? + var onProfileFlowSelect: ((UINavigationController) -> ())? + var onViewDidLoad: ((UINavigationController) -> ())? + + override func viewDidLoad() { + super.viewDidLoad() + + delegate = self + if let controller = customizableViewControllers?.first as? UINavigationController { + onViewDidLoad?(controller) + } + } + + func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) + { + + guard let controller = viewControllers?[selectedIndex] as? UINavigationController else { return } + + if selectedIndex == 0 { + onChannelsFlowSelect?(controller) + } else { + onProfileFlowSelect?(controller) + } + } +} diff --git a/GDproject/Controller/Coordinators/TabbarCoordinator.swift b/GDproject/Controller/Coordinators/TabbarCoordinator.swift new file mode 100644 index 0000000..de42348 --- /dev/null +++ b/GDproject/Controller/Coordinators/TabbarCoordinator.swift @@ -0,0 +1,57 @@ +// +// TabBarCoordinator.swift +// RxSwift +// +// Created by cstore on 02/03/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import Foundation +import UIKit + + +class TabBarCoordinator: BaseCoordinator { + + var didEndFlow: (()->())? + + private let tabbarView: TabbarView + private let window: UIWindow? + + init(tabbarView: TabbarView, window: UIWindow) { + self.tabbarView = tabbarView + self.window = window + } + + override func start() { + tabbarView.onViewDidLoad = runChannelsFlow() + tabbarView.onChannelsFlowSelect = runChannelsFlow() + tabbarView.onProfileFlowSelect = runProfileFlow() + window?.rootViewController = tabbarView as! TabbarController + } + + private func runChannelsFlow() -> ((UINavigationController) -> ()) + { + return { [unowned self] navController in + if navController.viewControllers.isEmpty == true { + let channelCoordinator = ChannelsCoordinator(nc: navController) + self.addDependency(channelCoordinator) + channelCoordinator.start() + } + } + } + + private func runProfileFlow() -> ((UINavigationController) -> ()) + { + return { [unowned self] navController in + if navController.viewControllers.isEmpty == true { + let profileCoordinator = ProfileCoordinator(nc: navController) + profileCoordinator.didEndSession = { [weak self, weak profileCoordinator] in + self?.removeDependency(profileCoordinator) + self?.didEndFlow?() + } + self.addDependency(profileCoordinator) + profileCoordinator.start() + } + } + } +} diff --git a/GDproject/Controller/Log In/LogInCoordinator.swift b/GDproject/Controller/Log In/LogInCoordinator.swift deleted file mode 100644 index 90c7266..0000000 --- a/GDproject/Controller/Log In/LogInCoordinator.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// LogInCoordinator.swift -// GDproject -// -// Created by cstore on 23/02/2019. -// Copyright © 2019 drHSE. All rights reserved. -// - -import UIKit - -class LogInCoordinator{ - - private weak var navigationController: UINavigationController? - private var window: UIWindow! - // MARK:- Init - init(navigationController: UINavigationController, window: UIWindow) { - self.navigationController = navigationController - self.window = window - } - - func start(){ - showLogInPage() - } - - // MARK: - Private implementation -// private func showStatusPage(){ -// let controller = TabbarCoordinator(window: window) -// controller.start() -// } - - private func showLogInPage(){ - let controller = UIStoryboard.makeLogIn() - - controller.onLogIn = { - (id) in Model.authenticate(with: id) { - (res) in - - if (res) { DataStorage.standard.setUserKey(with: id) } - else { DataStorage.standard.setUserKey(with: 0) } - - controller.authenticateSucceeded = res - } - } - - navigationController?.pushViewController(controller, animated: false) - } - -} diff --git a/GDproject/Controller/Log In/LogInCoordinator1.swift b/GDproject/Controller/Log In/LogInCoordinator1.swift new file mode 100644 index 0000000..f47f836 --- /dev/null +++ b/GDproject/Controller/Log In/LogInCoordinator1.swift @@ -0,0 +1,48 @@ +//// +//// LogInCoordinator.swift +//// GDproject +//// +//// Created by cstore on 23/02/2019. +//// Copyright © 2019 drHSE. All rights reserved. +//// +// +//import UIKit +// +//class LogInCoordinator{ +// +// private weak var navigationController: UINavigationController? +// private var window: UIWindow! +// // MARK:- Init +// init(navigationController: UINavigationController, window: UIWindow) { +// self.navigationController = navigationController +// self.window = window +// } +// +// func start(){ +// showLogInPage() +// } +// +// // MARK: - Private implementation +//// private func showStatusPage(){ +//// let controller = TabbarCoordinator(window: window) +//// controller.start() +//// } +// +// private func showLogInPage(){ +// let controller = UIStoryboard.makeLogIn() +// +// controller.onLogIn = { +// (id) in Model.authenticate(with: id) { +// (res) in +// +// if (res) { DataStorage.standard.setUserKey(with: id) } +// else { DataStorage.standard.setUserKey(with: 0) } +// +// controller.authenticateSucceeded = res +// } +// } +// +// navigationController?.pushViewController(controller, animated: false) +// } +// +//} diff --git a/GDproject/Controller/Log In/LogInViewController.swift b/GDproject/Controller/Log In/LogInViewController.swift index a7130c4..d62c48d 100644 --- a/GDproject/Controller/Log In/LogInViewController.swift +++ b/GDproject/Controller/Log In/LogInViewController.swift @@ -1,128 +1,110 @@ // // LogInViewController.swift -// NewsFeed +// RxSwift // -// Created by cstore on 20/01/2019. +// Created by cstore on 01/03/2019. // Copyright © 2019 drHSE. All rights reserved. // import UIKit +import TinyConstraints +import ReactiveSwift +import ReactiveCocoa +import Result class LogInViewController: UIViewController { - @IBOutlet weak var mailTextField: UITextField! + var authenticate: ((Int)->())? - @IBOutlet weak var indicatorView: UIActivityIndicatorView! + let logInLabel: UILabel = { + let label = UILabel() + label.text = "Log In" + label.textColor = .black + label.font = UIFont.boldSystemFont(ofSize: 34) + return label + }() - var onLogIn: ((Int)->())? - - var authenticateSucceeded: Bool? { - didSet { - if !authenticateSucceeded! { - indicatorView.stopAnimating() - indicatorView.isHidden = true - } - } - } - - static let titleColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5) - - var bottomConstraint: NSLayoutConstraint? + let mailTextField: UITextField = { + let textField = UITextField() + textField.backgroundColor = #colorLiteral(red: 0.937254902, green: 0.937254902, blue: 0.9568627451, alpha: 1) + textField.placeholder = "Mail" + textField.borderStyle = .roundedRect + textField.textColor = .black + textField.clearButtonMode = .always + return textField + }() let logInButton: UIButton = { let button = UIButton(type: .system) button.setTitle("Log In", for: .normal) - button.setTitleColor(titleColor, for: .normal) + button.setTitleColor(blueSystemColor, for: .normal) button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16) + button.addTarget(self, action: #selector(handleTap), for: .touchUpInside) button.isEnabled = false - button.addTarget(self, action: #selector(activateLogInProcess), for: .touchUpInside) return button }() - let keyboardBar: UIView = { - let view = UIView() - view.backgroundColor = .white - return view - }() + @objc func handleTap(){ + authenticate?(Int(mailTextField.text!)!) + } + + private lazy var keyboardBar = UIView() + private lazy var contentView = UIView() + private var bottomConstraint: NSLayoutConstraint? + + func setUpView(){ + // logIn stack with textField and label + view.addSubview(contentView) + contentView.edgesToSuperview(excluding: .bottom, insets: .left(16) + .right(16) + .top(80), usingSafeArea: true) + let views = [logInLabel, mailTextField] + contentView.stack(views, axis: .vertical, spacing: 10) + } + + func configureKeyboard(){ + + // configure keyboardBar setUp + view.addSubview(keyboardBar) + keyboardBar.height(50) + keyboardBar.edgesToSuperview(excluding: [.top,.bottom]) + bottomConstraint = NSLayoutConstraint(item: keyboardBar, attribute: .bottom, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0) + view.addConstraint(bottomConstraint!) + + // configure keyboardBar components + keyboardBar.addSubview(logInButton) + logInButton.height(50) + logInButton.leftToSuperview(view.leftAnchor, offset: 16, relation: .equal, isActive: true) + + } + + func logicOfLogInInputValidation() -> ((String?)->()) { + + let logic: ((String?)->()) = { [weak self] (someText) in + if let text = someText, !text.isEmpty, let id = Int(text) { + self?.logInButton.isEnabled = true + } else { + self?.logInButton.isEnabled = false + } + } + + return logic + } override func viewDidLoad() { super.viewDidLoad() + self.view.backgroundColor = .white setUpView() - configureTapgesture() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - } - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - } - - private func configureTapgesture(){ - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) - view.addGestureRecognizer(tapGesture) - } - - @objc func handleTap(){ - view.endEditing(true) - } - - @objc func activateLogInProcess(){ - if logInButton.isEnabled { - // MARK:- when log in is succeeded do I need to go there? - if let id = Int(mailTextField.text!){ - indicatorView.isHidden = false - indicatorView.startAnimating() - onLogIn?(id) - view.endEditing(true) - } else { - logInButton.isEnabled = false - logInButton.setTitleColor(LogInViewController.titleColor.withAlphaComponent(0.5), for: .normal) - } - } - } - - func setUpView(){ - indicatorView.isHidden = true - mailTextField.delegate = self - view.addSubview(keyboardBar) + configureKeyboard() - view.addConstraintsWithFormat(format: "H:|[v0]|", views: keyboardBar) - view.addConstraintsWithFormat(format: "V:[v0(50)]", views: keyboardBar) + let mailFieldValuesSignal: Signal = mailTextField.reactive.continuousTextValues - setUpBarComponents() - - configureKeyboardBehavior() - } - - func configureKeyboardBehavior(){ - bottomConstraint = NSLayoutConstraint(item: keyboardBar, attribute: .bottom, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0) - view.addConstraint(bottomConstraint!) + mailFieldValuesSignal.observeValues(logicOfLogInInputValidation()) // for keyboard notifications NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillHideNotification, object: nil) - - // for log in button notifications - NotificationCenter.default.addObserver(self, selector: #selector(inputDidChanged), name: UITextField.textDidChangeNotification, object: mailTextField) - - NotificationCenter.default.addObserver(self, selector: #selector(inputDidChanged), name: UITextField.textDidBeginEditingNotification, object: mailTextField) } - @objc func inputDidChanged(notification: NSNotification){ - if mailTextField.text?.isEmpty ?? true - { - logInButton.isEnabled = false - logInButton.setTitleColor(LogInViewController.titleColor.withAlphaComponent(0.5), for: .normal) - } - else - { - logInButton.isEnabled = true - logInButton.setTitleColor(LogInViewController.titleColor.withAlphaComponent(1), for: .normal) - } - } @objc func handleKeyboardNotifications(notification: NSNotification){ if let userInfo = notification.userInfo{ @@ -136,18 +118,13 @@ class LogInViewController: UIViewController { } } - func setUpBarComponents(){ - keyboardBar.addSubview(logInButton) - keyboardBar.addConstraintsWithFormat(format: "H:[v0(60)]-16-|", views: logInButton) - keyboardBar.addConstraintsWithFormat(format: "V:|[v0]|", views: logInButton) - } } - -extension LogInViewController: UITextFieldDelegate{ - func textFieldShouldReturn(_ textField: UITextField) -> Bool - { - textField.resignFirstResponder() - return true +extension UIButton { + override open var isEnabled: Bool { + didSet { + let color = isEnabled ? self.titleLabel?.textColor.withAlphaComponent(1) : self.titleLabel?.textColor.withAlphaComponent(0.5) + self.setTitleColor(color, for: .normal) + } } } diff --git a/GDproject/Controller/Profile/ProfileViewController.swift b/GDproject/Controller/Profile/ProfileViewController.swift index 57a0e12..560d098 100644 --- a/GDproject/Controller/Profile/ProfileViewController.swift +++ b/GDproject/Controller/Profile/ProfileViewController.swift @@ -30,6 +30,12 @@ class ProfileViewController: UIViewController @IBOutlet weak var newMessageButton: UIButton! + var logOut: (()->())? + + var onSettings: (()->())? + + var onChannelsListToAddAPerson: ((Model.Users)->())? + var protoDictionary: [Int: UIImage] = [9: #imageLiteral(resourceName: "9"), 5051: #imageLiteral(resourceName: "5051"), 69: #imageLiteral(resourceName: "69"), 42: #imageLiteral(resourceName: "42")] func fill(with user: Model.Users){ @@ -76,8 +82,6 @@ class ProfileViewController: UIViewController { super.viewDidLoad() - - posts.viewController = self posts.type = .NONE @@ -99,6 +103,7 @@ class ProfileViewController: UIViewController var idProfile: Int? override func viewWillAppear(_ animated: Bool) { + if idProfile == nil { idProfile = DataStorage.standard.getUserId() } @@ -111,8 +116,6 @@ class ProfileViewController: UIViewController self?.user = dic[id] } } - - // requst for postsforuser was here. moved because of concrr } setUpNavigarionBar() @@ -120,56 +123,35 @@ class ProfileViewController: UIViewController func setUpNavigarionBar(){ navigationController?.navigationBar.prefersLargeTitles = true - //navigationItem.title = "\(user?.id ?? 0)" let uibarbutton = UIBarButtonItem(title: "More", style: .plain, target: self, action: #selector(showInformation)) navigationItem.rightBarButtonItems = [uibarbutton] navigationItem.largeTitleDisplayMode = .always } - - let copyLink: UIAlertAction = { - let b = UIAlertAction(title: "Copy link", style: .default) - return b - }() - @objc func showInformation(){ - // drafts - // saved - let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let channelAction = UIAlertAction(title: "Add to a channel", style: .default){ [weak self] (_) in - - let vc = self?.storyboard?.instantiateViewController(withIdentifier: simplifiedChannelsList) as! SimplifiedChannelsList - - vc.user = self?.user! - - let transition = CATransition() - transition.duration = 0.5 - transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) - transition.type = CATransitionType.moveIn - transition.subtype = CATransitionSubtype.fromTop - self?.navigationController?.view.layer.add(transition, forKey: nil) - self?.navigationController?.pushViewController(vc, animated: false) + self?.onChannelsListToAddAPerson?(self!.user!) } + let settingsAction = UIAlertAction(title: "Setting", style: .default) { [weak self] (_) in - - let vc = self?.storyboard?.instantiateViewController(withIdentifier: "SettingsViewController") as! SettingsViewController - self?.navigationController?.pushViewController(vc, animated: true) + self?.onSettings?() } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) + let logoutAction = UIAlertAction(title: "Log out", style: .destructive) - { + { [weak self] (_) in - DataStorage.standard.setIsLoggedIn(value: false, with: 0) + self?.logOut?() } optionMenu.addAction(channelAction) optionMenu.addAction(settingsAction) - optionMenu.addAction(copyLink) optionMenu.addAction(logoutAction) optionMenu.addAction(cancelAction) diff --git a/GDproject/Controller/RepeatingTimer.swift b/GDproject/Controller/RepeatingTimer.swift deleted file mode 100644 index 19e02c6..0000000 --- a/GDproject/Controller/RepeatingTimer.swift +++ /dev/null @@ -1,59 +0,0 @@ -/// RepeatingTimer mimics the API of DispatchSourceTimer but in a way that prevents -/// crashes that occur from calling resume multiple times on a timer that is -/// already resumed (noted by https://github.com/SiftScience/sift-ios/issues/52 - -import Foundation - -class RepeatingTimer { - - let timeInterval: TimeInterval - - init(timeInterval: TimeInterval) { - self.timeInterval = timeInterval - } - - private lazy var timer: DispatchSourceTimer = { - let t = DispatchSource.makeTimerSource() - t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval) - t.setEventHandler(handler: { [weak self] in - self?.eventHandler?() - }) - return t - }() - - var eventHandler: (() -> Void)? - - private enum State { - case suspended - case resumed - } - - private var state: State = .suspended - - deinit { - timer.setEventHandler {} - timer.cancel() - /* - If the timer is suspended, calling cancel without resuming - triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902 - */ - resume() - eventHandler = nil - } - - func resume() { - if state == .resumed { - return - } - state = .resumed - timer.resume() - } - - func suspend() { - if state == .suspended { - return - } - state = .suspended - timer.suspend() - } -} diff --git a/GDproject/DataStorage.swift b/GDproject/DataStorage.swift index 26a4e26..98a1bb8 100644 --- a/GDproject/DataStorage.swift +++ b/GDproject/DataStorage.swift @@ -40,15 +40,7 @@ class DataStorage{ /** Function to determine is user logged in already or not */ - var isLoggedIn: Bool = UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue) { - didSet{ - if isLoggedIn && getUserId() != 0 { - (UIApplication.shared.delegate as? AppDelegate)?.tabCoordinator.start() - } else { - (UIApplication.shared.delegate as? AppDelegate)?.logInAgain() - } - } - } + var isLoggedIn: Bool = UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue) } /** diff --git a/GDproject/Supporting files/AppDelegate.swift b/GDproject/Supporting files/AppDelegate.swift index 18b89c3..0ebbd26 100644 --- a/GDproject/Supporting files/AppDelegate.swift +++ b/GDproject/Supporting files/AppDelegate.swift @@ -12,57 +12,21 @@ import UIKit class AppDelegate: UIResponder, UIApplicationDelegate { - var coordinator: LogInCoordinator! - var tabCoordinator: TabbarCoordinator! - var window: UIWindow? - var rootController: UINavigationController? { - return self.window!.rootViewController as? UINavigationController - } - - func logInAgain(){ - let root = UIStoryboard.navRoot() - window?.rootViewController = root - window?.makeKeyAndVisible() - coordinator = LogInCoordinator(navigationController: root, window: UIApplication.shared.keyWindow!) - coordinator!.start() - } + var appCoordinator: ApplicationCoordinator! func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - coordinator = LogInCoordinator(navigationController: rootController!, window: window!) - tabCoordinator = TabbarCoordinator(window: window!) - // TODO:- determine what to do here: log in or tabbar - if DataStorage.standard.isLoggedIn { - tabCoordinator.start() - } else { - coordinator.start() - } + window = UIWindow(frame: UIScreen.main.bounds) + window?.makeKeyAndVisible() + + // window?.rootViewController = UINavigationController() + + appCoordinator = ApplicationCoordinator(window: window!) + appCoordinator.start() return true } - - func applicationWillResignActive(_ application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. - } - - func applicationDidEnterBackground(_ application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - } - - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. - } - - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } - - func applicationWillTerminate(_ application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } } From 32e1d267d1f9053136a2b4d0288d5488eda01e5d Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Wed, 20 Mar 2019 15:31:14 +0300 Subject: [PATCH 02/34] a lot of changes: still trying to embed coordinators. slowly modfiying channels. added empty card for editing channel --- Cartfile | 4 +- GDproject.xcodeproj/project.pbxproj | 6 ++ GDproject/Base.lproj/Main.storyboard | 102 ++++++++++++++---- .../ News and channels/NewsController.swift | 12 ++- .../TabbarCoordinator.swift | 14 --- .../Coordinators/ChannelsCoordinator.swift | 16 ++- 6 files changed, 113 insertions(+), 41 deletions(-) delete mode 100644 GDproject/Controller/Coordinator tab bar?/TabbarCoordinator.swift diff --git a/Cartfile b/Cartfile index cc276a7..dd68276 100644 --- a/Cartfile +++ b/Cartfile @@ -2,7 +2,7 @@ github "robb/Cartography" github "ivanbruel/MarkdownKit" github "roberthein/TinyConstraints" github "macteo/Marklight" -github "ElaWorkshop/TagListView" github "Alamofire/Alamofire" "5.0.0-beta.2" github "whitesmith/WSTagsField" -github "ReactiveCocoa/ReactiveCocoa" ~> 8.0 \ No newline at end of file +github "ReactiveCocoa/ReactiveCocoa" ~> 8.0 +github "52inc/Pulley" \ No newline at end of file diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index e0c4785..f4ab99e 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -63,6 +63,8 @@ 12F3D6BE2241130000A69214 /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B7224112E800A69214 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12F3D6C02241130200A69214 /* ReactiveSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12F3D6C22241130400A69214 /* ReactiveMapKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B3224112D600A69214 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12F3D6C92242645800A69214 /* Pulley.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6C72242644D00A69214 /* Pulley.framework */; }; + 12F3D6CA2242645800A69214 /* Pulley.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6C72242644D00A69214 /* Pulley.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -81,6 +83,7 @@ 12F3D6C02241130200A69214 /* ReactiveSwift.framework in Embed Frameworks */, 12F3D6BC224112FD00A69214 /* ReactiveCocoa.framework in Embed Frameworks */, 123AD0E5223C1C4300326173 /* WSTagsField.framework in Embed Frameworks */, + 12F3D6CA2242645800A69214 /* Pulley.framework in Embed Frameworks */, 12E36DC2221441E1006FCDD7 /* MarkdownKit.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -137,6 +140,7 @@ 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/iOS/ReactiveSwift.framework; sourceTree = ""; }; 12F3D6B7224112E800A69214 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = ""; }; 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = Carthage/Build/iOS/ReactiveCocoa.framework; sourceTree = ""; }; + 12F3D6C72242644D00A69214 /* Pulley.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pulley.framework; path = Carthage/Build/iOS/Pulley.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -147,6 +151,7 @@ 12E36DB522143D54006FCDD7 /* TinyConstraints.framework in Frameworks */, 12F3D6B4224112D600A69214 /* ReactiveMapKit.framework in Frameworks */, 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */, + 12F3D6C92242645800A69214 /* Pulley.framework in Frameworks */, 12F3D6BB224112FD00A69214 /* ReactiveCocoa.framework in Frameworks */, 12F3D6B8224112E900A69214 /* Result.framework in Frameworks */, 12F3D6B6224112E100A69214 /* ReactiveSwift.framework in Frameworks */, @@ -199,6 +204,7 @@ 12E36D8F221424EA006FCDD7 = { isa = PBXGroup; children = ( + 12F3D6C72242644D00A69214 /* Pulley.framework */, 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */, 12F3D6B7224112E800A69214 /* Result.framework */, 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index f828dc1..ff8cd15 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -416,6 +416,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -432,26 +471,6 @@ - - - - - - - - - - - - - - - - - - - - @@ -487,6 +506,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index f829a78..9863f95 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -21,8 +21,11 @@ protocol UpdateableWithUser: class { } // MARK:- Controller with posts and channels availiable. // Search is availiable within every table (posts and channels). Has button-functionality for boths post and chnnels -class NewsController: UITableViewController, UISearchControllerDelegate, NewPostDelegate, UpdateableWithChannel, DataDelegate +class NewsController: UIViewController, UISearchControllerDelegate, NewPostDelegate, UpdateableWithChannel, DataDelegate { + + var changedChannelName: ((String)->())? + func passData(for row: Int, channel: Model.Channels) { if channel.id == -1{ self.channel = nil @@ -31,6 +34,8 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost } } + @IBOutlet weak var tableView: UITableView! + var dictionary: [Int: Model.Users]? { didSet { @@ -147,9 +152,8 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost self?.posts = $0.posts self?.dictionary = $0.users self?.refreshContr.endRefreshing() - self?.navigationItem.title = "General" + self?.changedChannelName?("General") } - } } @@ -245,7 +249,7 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost var isBannerVisible: Bool = false - override func scrollViewDidScroll(_ scrollView: UIScrollView) { + func scrollViewDidScroll(_ scrollView: UIScrollView) { print(scrollView.contentOffset.y) if scrollView.contentOffset.y >= 50 && !isBannerVisible{ isBannerVisible = true diff --git a/GDproject/Controller/Coordinator tab bar?/TabbarCoordinator.swift b/GDproject/Controller/Coordinator tab bar?/TabbarCoordinator.swift deleted file mode 100644 index c4b7dba..0000000 --- a/GDproject/Controller/Coordinator tab bar?/TabbarCoordinator.swift +++ /dev/null @@ -1,14 +0,0 @@ -import UIKit - -class TabbarCoordinator{ - var window: UIWindow! - - func start(){ - let tabbar = UIStoryboard.tabBarController() - window.rootViewController = tabbar - } - - init(window: UIWindow) { - self.window = window - } -} diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift index ad5b114..710cc3b 100644 --- a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -8,6 +8,7 @@ import Foundation import UIKit +import Pulley class ChannelsCoordinator: BaseCoordinator{ private weak var navigationController: UINavigationController? @@ -22,7 +23,20 @@ class ChannelsCoordinator: BaseCoordinator{ private func show() { let storyboard = UIStoryboard(name: "Main", bundle: nil) + let channels = storyboard.instantiateViewController(withIdentifier: channelListControllerId) as! ChannelListController - navigationController?.setViewControllers([channels], animated: false) + + let mainContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: newsController) as! NewsController + + let drawerContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DrawerContentViewController") + + let pulleyDrawerVC = PulleyViewController(contentViewController: mainContentVC, drawerViewController: drawerContentVC) + + pulleyDrawerVC.initialDrawerPosition = .collapsed + mainContentVC.changedChannelName = { + [weak pulleyDrawerVC] (title) in pulleyDrawerVC?.navigationItem.title = title + } + + navigationController?.setViewControllers([channels,pulleyDrawerVC], animated: false) } } From e8b7ede59391935337fa521952a8c4856f5d22ee Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Wed, 20 Mar 2019 17:19:13 +0300 Subject: [PATCH 03/34] still working on channels. coordination is improved. need to design channel card and get rid of previous editing mode --- .../ChannelListController.swift | 20 ++------ .../NewPostViewController.swift | 20 ++------ .../ News and channels/NewsController.swift | 34 +------------ .../Coordinators/ChannelsCoordinator.swift | 48 ++++++++++++++++++- .../Coordinators/ProfileCoordinator.swift | 2 + 5 files changed, 58 insertions(+), 66 deletions(-) diff --git a/GDproject/Controller/ News and channels/ChannelListController.swift b/GDproject/Controller/ News and channels/ChannelListController.swift index 0e0a65c..3a28796 100644 --- a/GDproject/Controller/ News and channels/ChannelListController.swift +++ b/GDproject/Controller/ News and channels/ChannelListController.swift @@ -14,7 +14,7 @@ struct ChannelData{ } // TODO: make search controller availiable -class ChannelListController: UITableViewController, DataDelegate { +class ChannelListController: UITableViewController { // MARK: - Output - var onChannelSelected: ((Model.Channels) -> Void)? @@ -22,7 +22,6 @@ class ChannelListController: UITableViewController, DataDelegate { // MARK: - filter search controller var filteredDataSource = [Model.Channels]() - var myProtocol: DataDelegate? var displayingChannel: Model.Channels? var toReload: Bool = false { @@ -97,7 +96,6 @@ class ChannelListController: UITableViewController, DataDelegate { // editing mode is on automatically let vc = storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController vc.index = 1 - vc.myProtocol = self vc.channel = Model.Channels(people: [], name: "Untitled", tags: []) navigationController?.pushViewController(vc, animated: true) } @@ -147,7 +145,6 @@ class ChannelListController: UITableViewController, DataDelegate { let editButton = UITableViewRowAction(style: .normal, title: "Edit") { [weak self] (action, indexPath) in let vc = self?.storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController vc.index = indexPath.row - vc.myProtocol = self! vc.channel = self?.dataSource[indexPath.row] self?.navigationController?.pushViewController(vc, animated: true) } @@ -159,11 +156,7 @@ class ChannelListController: UITableViewController, DataDelegate { Model.channelsDelete(by: self!.dataSource[indexPath.row].id!, completion: { print("хз что тут делать при успехе") }) - - if (self!.dataSource[indexPath.row].id == self!.displayingChannel?.id ?? -1) - { - self?.myProtocol?.passData(for: 0, channel: self!.dataSource[0]) - } + self?.tableView.beginUpdates() self?.dataSource.remove(at: indexPath.row) self?.tableView.deleteRows(at: [indexPath], with: .none) @@ -175,17 +168,12 @@ class ChannelListController: UITableViewController, DataDelegate { return [editButton, deleteButton] } - // TODO: remove popping override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if isFiltering { - //onChannelSelected?(filteredDataSource[indexPath.row]) - myProtocol?.passData(for: 0, channel: filteredDataSource[indexPath.row]) - navigationController?.popViewController(animated: true) + onChannelSelected?(filteredDataSource[indexPath.row]) } else { - myProtocol?.passData(for: 0, channel: dataSource[indexPath.row]) - navigationController?.popViewController(animated: true) - //onChannelSelected?(ChannelListController.dataSource[indexPath.row]) + onChannelSelected?(dataSource[indexPath.row]) } } } diff --git a/GDproject/Controller/ News and channels/NewPostViewController.swift b/GDproject/Controller/ News and channels/NewPostViewController.swift index 53a7a46..d1268c9 100644 --- a/GDproject/Controller/ News and channels/NewPostViewController.swift +++ b/GDproject/Controller/ News and channels/NewPostViewController.swift @@ -22,9 +22,6 @@ class NewPostViewController: UIViewController, UITextViewDelegate { // Keep strong instance of the `NSTextStorage` subclass let textStorage = MarklightTextStorage() - weak var parentVC: NewsController? - var myProtocol: NewPostDelegate? - static var draft: String = "" static var hashTagsDraft: [String] = [] @@ -219,7 +216,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: tagsField.tags.map { $0.text }) // adding row to uiTableView after adding new post // myProtocol?.addPost(post: p) - moveBackToParentVC() + moveBackToParentVC?() // somewhere here i will be sending server notifications about new post arrival } @@ -232,7 +229,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { _ in NewPostViewController.draft = self?.textView.text ?? "" NewPostViewController.hashTagsDraft = self?.tagsField.tags.map { $0.text } ?? [] - self?.moveBackToParentVC() + self?.moveBackToParentVC?() } let deleteAction = UIAlertAction(title: "Delete draft", style: .destructive) @@ -241,7 +238,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { in NewPostViewController.draft = "" NewPostViewController.hashTagsDraft = [] - self?.moveBackToParentVC() + self?.moveBackToParentVC?() } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) @@ -258,16 +255,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { actionSaveDraft() } - func moveBackToParentVC(){ - let transition = CATransition() - transition.duration = 0.5 - transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) - transition.type = CATransitionType.reveal - transition.subtype = CATransitionSubtype.fromBottom - navigationController?.view.layer.add(transition, forKey: nil) - navigationController?.popViewController(animated: false) - textView!.resignFirstResponder() - } + var moveBackToParentVC: (()->())? func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index 9863f95..257ba2f 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -61,11 +61,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg } } - var channel: Model.Channels? { - didSet { - navigationItem.title = channel?.name ?? "" - } - } + var channel: Model.Channels? // MARK: - Output - var onSelectChannel: (() -> Void)? @@ -144,6 +140,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg self?.posts = $0.posts self?.dictionary = $0.users self?.refreshContr.endRefreshing() + self?.changedChannelName?(channel.name) } } else { @@ -158,38 +155,11 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg } func setUpNavigationItemsforPosts(){ - navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(self.writePost(_:))),UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(self.showChannels(_:))) - ] tableView.delegate = news tableView.dataSource = news tableView.reloadData() } - // MARK:- attempt with coordinator - @objc func showChannels(_ barItem: UIBarButtonItem){ - let vc = UIStoryboard.makeChannelsListController() - vc.myProtocol = self - vc.displayingChannel = channel - self.navigationController?.pushViewController(vc, animated: true) - } - - @objc func writePost(_ barItem: UIBarButtonItem) - { - let vc = storyboard?.instantiateViewController(withIdentifier: "NewPostViewController") as! NewPostViewController - - vc.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Post", style: .plain, target: vc, action: #selector(vc.newPost)) - vc.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Close", style: .plain, target: vc, action: #selector(vc.closeView)) - - vc.myProtocol = self - - let transition = CATransition() - transition.duration = 0.5 - transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) - transition.type = CATransitionType.moveIn - transition.subtype = CATransitionSubtype.fromTop - navigationController?.view.layer.add(transition, forKey: nil) - navigationController?.pushViewController(vc, animated: false) - } // for animating the banner var topConstraint: NSLayoutConstraint? diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift index 710cc3b..d079ff4 100644 --- a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -11,10 +11,12 @@ import UIKit import Pulley class ChannelsCoordinator: BaseCoordinator{ + let storyboard = UIStoryboard(name: "Main", bundle: nil) private weak var navigationController: UINavigationController? init(nc: UINavigationController) { self.navigationController = nc + navigationController?.navigationItem.largeTitleDisplayMode = .always } override func start() { @@ -22,21 +24,63 @@ class ChannelsCoordinator: BaseCoordinator{ } private func show() { - let storyboard = UIStoryboard(name: "Main", bundle: nil) let channels = storyboard.instantiateViewController(withIdentifier: channelListControllerId) as! ChannelListController + channels.onChannelSelected = { [weak self] + (channel) in + self?.navigationController?.pushViewController(self!.presentNewsController(with: channel), animated: true) + } + navigationController?.setViewControllers([channels,presentNewsController()], animated: false) + } + + private func presentNewsController(with channel: Model.Channels? = nil) -> PulleyViewController { + let mainContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: newsController) as! NewsController + mainContentVC.channel = channel let drawerContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DrawerContentViewController") let pulleyDrawerVC = PulleyViewController(contentViewController: mainContentVC, drawerViewController: drawerContentVC) pulleyDrawerVC.initialDrawerPosition = .collapsed + + mainContentVC.changedChannelName = { [weak pulleyDrawerVC] (title) in pulleyDrawerVC?.navigationItem.title = title } - navigationController?.setViewControllers([channels,pulleyDrawerVC], animated: false) + pulleyDrawerVC.navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(writePost(_:))) + ] + + return pulleyDrawerVC + } + + @objc private func writePost(_ barItem: UIBarButtonItem) + { + let vc = storyboard.instantiateViewController(withIdentifier: "NewPostViewController") as! NewPostViewController + + vc.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Post", style: .plain, target: vc, action: #selector(vc.newPost)) + vc.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Close", style: .plain, target: vc, action: #selector(vc.closeView)) + + let transition = CATransition() + transition.duration = 0.5 + transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) + transition.type = CATransitionType.moveIn + transition.subtype = CATransitionSubtype.fromTop + navigationController?.view.layer.add(transition, forKey: nil) + navigationController?.pushViewController(vc, animated: false) + + vc.moveBackToParentVC = { + [weak self] in + + let transition = CATransition() + transition.duration = 0.5 + transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) + transition.type = CATransitionType.reveal + transition.subtype = CATransitionSubtype.fromBottom + self?.navigationController?.view.layer.add(transition, forKey: nil) + self?.navigationController?.popViewController(animated: false) + } } } diff --git a/GDproject/Controller/Coordinators/ProfileCoordinator.swift b/GDproject/Controller/Coordinators/ProfileCoordinator.swift index 0577612..fff48a0 100644 --- a/GDproject/Controller/Coordinators/ProfileCoordinator.swift +++ b/GDproject/Controller/Coordinators/ProfileCoordinator.swift @@ -26,10 +26,12 @@ class ProfileCoordinator: BaseCoordinator { private func show() { let storyboard = UIStoryboard(name: "Main", bundle: nil) let profile = storyboard.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController + profile.logOut = { [weak self] in DataStorage.standard.setIsLoggedIn(value: false, with: 0) self?.didEndSession?() } + profile.onSettings = { [weak self, weak storyboard] in let vc = storyboard?.instantiateViewController(withIdentifier: "SettingsViewController") as! SettingsViewController self?.navigationController?.pushViewController(vc, animated: true) From e5bb705035639020507cd0e2b347c1c6b71c1a1e Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Wed, 20 Mar 2019 22:22:57 +0300 Subject: [PATCH 04/34] working on coordination between anonymous channels. in close future: design and implement channels' card --- GDproject/Base.lproj/Main.storyboard | 2 + .../ChannelListController.swift | 2 +- .../ News and channels/NewsController.swift | 62 +++++++------- .../ News and channels/NewsVC.swift | 20 +++-- .../ News and channels/PostViewCell.swift | 11 ++- .../Coordinators/ChannelsCoordinator.swift | 30 +++++-- GDproject/Simple model/Model.swift | 82 +++++++++++++++++++ 7 files changed, 162 insertions(+), 47 deletions(-) diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index ff8cd15..6e0307e 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -526,6 +526,7 @@ + @@ -543,6 +544,7 @@ + diff --git a/GDproject/Controller/ News and channels/ChannelListController.swift b/GDproject/Controller/ News and channels/ChannelListController.swift index 3a28796..2859446 100644 --- a/GDproject/Controller/ News and channels/ChannelListController.swift +++ b/GDproject/Controller/ News and channels/ChannelListController.swift @@ -100,7 +100,7 @@ class ChannelListController: UITableViewController { navigationController?.pushViewController(vc, animated: true) } - static let generalChannel = Model.Channels(people: [], name: "General", id: -1, tags: []) + static let generalChannel = Model.Channels(people: [], name: "General", tags: []) var dataSource : [Model.Channels] = [ ChannelListController.generalChannel ] diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index 257ba2f..97a21b8 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -21,19 +21,10 @@ protocol UpdateableWithUser: class { } // MARK:- Controller with posts and channels availiable. // Search is availiable within every table (posts and channels). Has button-functionality for boths post and chnnels -class NewsController: UIViewController, UISearchControllerDelegate, NewPostDelegate, UpdateableWithChannel, DataDelegate +class NewsController: UIViewController, UISearchControllerDelegate, NewPostDelegate, UpdateableWithChannel { - var changedChannelName: ((String)->())? - func passData(for row: Int, channel: Model.Channels) { - if channel.id == -1{ - self.channel = nil - } else { - self.channel = channel - } - } - @IBOutlet weak var tableView: UITableView! var dictionary: [Int: Model.Users]? { @@ -49,19 +40,15 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg }) - news.dataSourse = newPosts tableView.reloadData() } } - var posts: [Model.Posts]? { - didSet{ - - } - } + var posts: [Model.Posts]? var channel: Model.Channels? + var anonymousChannel: (users: [Int: Model.Users],posts: [Model.Posts])? // MARK: - Output - var onSelectChannel: (() -> Void)? @@ -73,6 +60,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg var searchController = UISearchController(searchResultsController: nil) var news = NewsVC() + var type: HeaderType? = .NEWS var refreshContr = UIRefreshControl() @@ -90,7 +78,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg // setUpSearchContr() news.viewController = self - news.type = .NEWS + news.type = type == .NEWS ? .NEWS : type! setUpNavigationItemsforPosts() @@ -134,22 +122,32 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg } func decideWhatChannelDisplay(){ - if let channel = channel, let id = channel.id { - - Model.getChannel(with: id) { [weak self] in - self?.posts = $0.posts - self?.dictionary = $0.users - self?.refreshContr.endRefreshing() - self?.changedChannelName?(channel.name) + switch type! { + case .NEWS, .NONE: + if let channel = channel, let id = channel.id { + + Model.getChannel(with: id) { [weak self] in + self?.posts = $0.posts + self?.dictionary = $0.users + self?.refreshContr.endRefreshing() + self?.changedChannelName?(channel.name) + } + + } else if let _ = posts { + print("here!") + } else { + Model.getLast { [weak self] in + self?.posts = $0.posts + self?.dictionary = $0.users + self?.refreshContr.endRefreshing() + self?.changedChannelName?("General") + } } - - } else { - - Model.getLast { [weak self] in - self?.posts = $0.posts - self?.dictionary = $0.users - self?.refreshContr.endRefreshing() - self?.changedChannelName?("General") + default: + if let anonChannel = anonymousChannel { + posts = anonChannel.posts + dictionary = anonChannel.users + changedChannelName?("Anonymous") } } } diff --git a/GDproject/Controller/ News and channels/NewsVC.swift b/GDproject/Controller/ News and channels/NewsVC.swift index 5997c0a..78a7050 100644 --- a/GDproject/Controller/ News and channels/NewsVC.swift +++ b/GDproject/Controller/ News and channels/NewsVC.swift @@ -27,6 +27,10 @@ struct PostCellData{ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{ + var onChannelDidChange: ((([Int: Model.Users],[Model.Posts]))->())? + + var onFullPost: ((HeaderType, Model.Posts)->())? + var dataSourse: [Model.Posts] = [] { didSet { cellDataSourse = [] @@ -48,11 +52,7 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) - let vc = viewController!.storyboard!.instantiateViewController(withIdentifier: fullPostControllerId) as! FullPostController - vc.type = type - vc.post = dataSourse[indexPath.row] - viewController!.navigationController!.pushViewController(vc, animated: true) + onFullPost?(type, dataSourse[indexPath.row]) } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { @@ -72,12 +72,19 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{ vc.idProfile = id self?.viewController!.navigationController!.pushViewController(vc, animated: true) } - case .NONE: + default: cell.onUserDisplay = { (id) in print("tapped when profile is open already \(id)") } } + cell.onAnonymousChannelDisplay = { + [weak self] (tag) in + Model.getAnonymousChannel(by: Model.AnonymousChannel(people: [], tags: [tag]), + completion: { (tuple) in self?.onChannelDidChange?(tuple) } + ) + } + cell.selectionStyle = .none return cell } @@ -95,4 +102,5 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{ enum HeaderType { case NONE case NEWS + case ANONYMOUS } diff --git a/GDproject/Controller/ News and channels/PostViewCell.swift b/GDproject/Controller/ News and channels/PostViewCell.swift index 672cade..7adab89 100644 --- a/GDproject/Controller/ News and channels/PostViewCell.swift +++ b/GDproject/Controller/ News and channels/PostViewCell.swift @@ -15,6 +15,8 @@ class PostViewCell: UITableViewCell { var onUserDisplay: ((Int)->())? + var onAnonymousChannelDisplay: ((String)->())? + let nameLabel: UIButton = { let button = UIButton() button.setTitleColor(.black, for: .normal) @@ -120,8 +122,7 @@ class PostViewCell: UITableViewCell for hash in hashtags { let button = UIButton() button.setTitle("#" + hash, for: .normal) - //button.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) - //button.layer.cornerRadius = 10 + button.addTarget(self, action: #selector(setAnonymousChannel(on:)), for: .touchUpInside) button.titleLabel?.font = UIFont.monospacedDigitSystemFont(ofSize: 15, weight: .semibold) button.setTitleColor(#colorLiteral(red: 0, green: 0.4784313725, blue: 1, alpha: 1), for: .normal) buttons.append(button) @@ -179,7 +180,11 @@ class PostViewCell: UITableViewCell } @objc func displayProfile(){ - print("buttonTapped") onUserDisplay?(post!.authorId) } + + @objc func setAnonymousChannel(on button: UIButton){ + print("\(button.titleLabel?.text ?? "nothing")") + onAnonymousChannelDisplay?(String(button.titleLabel!.text!.dropFirst())) + } } diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift index d079ff4..218ca84 100644 --- a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -34,18 +34,38 @@ class ChannelsCoordinator: BaseCoordinator{ navigationController?.setViewControllers([channels,presentNewsController()], animated: false) } - private func presentNewsController(with channel: Model.Channels? = nil) -> PulleyViewController { + func presentNewsController(with channel: Model.Channels? = nil) -> PulleyViewController { let mainContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: newsController) as! NewsController - mainContentVC.channel = channel - let drawerContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DrawerContentViewController") + mainContentVC.channel = channel + mainContentVC.news.onFullPost = { + [weak self] (type,post) in + + let vc = self?.storyboard.instantiateViewController(withIdentifier: fullPostControllerId) as! FullPostController + vc.type = type + vc.post = post + self?.navigationController!.pushViewController(vc, animated: true) + } + + mainContentVC.news.onChannelDidChange = { + [weak self, weak mainContentVC] (tuple) in print("hep") + switch mainContentVC!.news.type { + case .ANONYMOUS: + mainContentVC!.posts = tuple.1 + mainContentVC!.dictionary = tuple.0 + default: + let vc = self!.presentNewsController() + (vc.primaryContentViewController as! NewsController).anonymousChannel = tuple + (vc.primaryContentViewController as! NewsController).type = .ANONYMOUS + self?.navigationController?.pushViewController(vc, animated: true) + } + } + let pulleyDrawerVC = PulleyViewController(contentViewController: mainContentVC, drawerViewController: drawerContentVC) pulleyDrawerVC.initialDrawerPosition = .collapsed - - mainContentVC.changedChannelName = { [weak pulleyDrawerVC] (title) in pulleyDrawerVC?.navigationItem.title = title } diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 6362d6c..853b50b 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -14,6 +14,7 @@ class Model{ static let invalidTocken = 498 private static var isValidTocken: ((Int)->())? = { responce in + print(responce) if responce == invalidTocken { DataStorage.standard.setIsLoggedIn(value: false, with: 0) } @@ -33,6 +34,7 @@ class Model{ static let channelsListURL = URL(string: "\(baseUrl)/channels")! static let channelsCreateURL = URL(string: "\(baseUrl)/channels/create")! static let channelsDeleteURL = URL(string: "\(baseUrl)/channels/delete")! + static let channelsGetAnonURL = URL(string: "\(baseUrl)/channels/getAnonymous")! struct QueryPosts: Codable{ @@ -417,4 +419,84 @@ class Model{ isValidTocken?(response.response?.statusCode ?? 498) } } + + struct AnonymousChannel: Codable { + + var limit = 10 + var request: RequestPeopleTags + + enum CodingKeys: String, CodingKey { + case limit + case request + } + + init(people: [Int], tags: [String]) { + self.request = RequestPeopleTags(people: people, tags: tags) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(limit, forKey: .limit) + try container.encode(request, forKey: .request) + } + + struct RequestPeopleTags: Codable { + + var people: [Int] + var tags: [String] + + init(people: [Int], tags: [String]) { + self.people = people + self.tags = tags + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(people, forKey: .people) + try container.encode(tags, forKey: .tags) + } + + enum CodingKeys: String, CodingKey { + case people + case tags + } + + } + } + /* + { + "limit": 11, + "request": { + "people": [ + 2, + 7, + 8 + ], + "tags": [ + "thisIsHashTag", + "thisIsAlsoHashTag" + ] + } + } + */ + static func getAnonymousChannel(by anonymousChannel: Model.AnonymousChannel, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){ + + var request = URLRequest(url: channelsGetAnonURL) + request.httpMethod = "POST" + request.httpBody = try? JSONEncoder().encode(anonymousChannel) + + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + AF.request(request).response { + (response) in + + isValidTocken?(response.response?.statusCode ?? 498) + + guard let json = response.data else { return } + + guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return } + + // idUser = newQueery.users + completion((newQueery.users, newQueery.posts)) + } + } } From 1520ef7904db515f166af730da42173d615377ae Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Sat, 20 Apr 2019 10:55:49 +0300 Subject: [PATCH 05/34] previous changes. coordinator improved. good version with card in news. will be removed soon... --- Cartfile | 3 ++- GDproject.xcodeproj/project.pbxproj | 6 ++++++ .../xcshareddata/xcschemes/GDproject.xcscheme | 4 ++-- GDproject/Controller/ News and channels/NewsVC.swift | 9 +++++++++ GDproject/Controller/Coordinators/BaseCoordinator.swift | 2 +- .../Controller/Coordinators/TabbarCoordinator.swift | 2 +- GDproject/Controller/Profile/ProfileViewController.swift | 2 +- GDproject/Simple model/Model.swift | 2 ++ GDproject/Supporting files/AppDelegate.swift | 4 ++++ 9 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Cartfile b/Cartfile index dd68276..f17ffce 100644 --- a/Cartfile +++ b/Cartfile @@ -5,4 +5,5 @@ github "macteo/Marklight" github "Alamofire/Alamofire" "5.0.0-beta.2" github "whitesmith/WSTagsField" github "ReactiveCocoa/ReactiveCocoa" ~> 8.0 -github "52inc/Pulley" \ No newline at end of file +github "52inc/Pulley" +github "HeroTransitions/Hero" \ No newline at end of file diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index f4ab99e..ae3ec31 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; }; 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; }; 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */; }; + 12924BD6224401D4003813B9 /* Hero.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12924BD5224401D4003813B9 /* Hero.framework */; }; + 12924BD8224401DC003813B9 /* Hero.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12924BD5224401D4003813B9 /* Hero.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */; }; 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */; }; 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */; }; @@ -82,6 +84,7 @@ 12E36DBD221441D5006FCDD7 /* TinyConstraints.framework in Embed Frameworks */, 12F3D6C02241130200A69214 /* ReactiveSwift.framework in Embed Frameworks */, 12F3D6BC224112FD00A69214 /* ReactiveCocoa.framework in Embed Frameworks */, + 12924BD8224401DC003813B9 /* Hero.framework in Embed Frameworks */, 123AD0E5223C1C4300326173 /* WSTagsField.framework in Embed Frameworks */, 12F3D6CA2242645800A69214 /* Pulley.framework in Embed Frameworks */, 12E36DC2221441E1006FCDD7 /* MarkdownKit.framework in Embed Frameworks */, @@ -105,6 +108,7 @@ 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = ""; }; 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = ""; }; 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator1.swift; sourceTree = ""; }; + 12924BD5224401D4003813B9 /* Hero.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Hero.framework; path = Carthage/Build/iOS/Hero.framework; sourceTree = ""; }; 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; }; 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = ""; }; @@ -156,6 +160,7 @@ 12F3D6B8224112E900A69214 /* Result.framework in Frameworks */, 12F3D6B6224112E100A69214 /* ReactiveSwift.framework in Frameworks */, 12E36DDB221594B1006FCDD7 /* MarkdownKit.framework in Frameworks */, + 12924BD6224401D4003813B9 /* Hero.framework in Frameworks */, 12E36DAF22143D40006FCDD7 /* Cartography.framework in Frameworks */, 123AD0E3223C1C3900326173 /* WSTagsField.framework in Frameworks */, 12E36DB322143D4E006FCDD7 /* Marklight.framework in Frameworks */, @@ -204,6 +209,7 @@ 12E36D8F221424EA006FCDD7 = { isa = PBXGroup; children = ( + 12924BD5224401D4003813B9 /* Hero.framework */, 12F3D6C72242644D00A69214 /* Pulley.framework */, 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */, 12F3D6B7224112E800A69214 /* Result.framework */, diff --git a/GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcscheme b/GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcscheme index 970dcc2..436c347 100644 --- a/GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcscheme +++ b/GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcscheme @@ -43,8 +43,8 @@ ())? private let tabbarView: TabbarView - private let window: UIWindow? + private weak var window: UIWindow? init(tabbarView: TabbarView, window: UIWindow) { self.tabbarView = tabbarView diff --git a/GDproject/Controller/Profile/ProfileViewController.swift b/GDproject/Controller/Profile/ProfileViewController.swift index 560d098..d930c41 100644 --- a/GDproject/Controller/Profile/ProfileViewController.swift +++ b/GDproject/Controller/Profile/ProfileViewController.swift @@ -81,7 +81,7 @@ class ProfileViewController: UIViewController override func viewDidLoad() { super.viewDidLoad() - + print("hell") posts.viewController = self posts.type = .NONE diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 853b50b..acbf367 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -7,6 +7,7 @@ // import Foundation +import UIKit import Alamofire class Model{ @@ -17,6 +18,7 @@ class Model{ print(responce) if responce == invalidTocken { DataStorage.standard.setIsLoggedIn(value: false, with: 0) + (UIApplication.shared.delegate as! AppDelegate).relaunch() } } diff --git a/GDproject/Supporting files/AppDelegate.swift b/GDproject/Supporting files/AppDelegate.swift index 0ebbd26..5ecf3ad 100644 --- a/GDproject/Supporting files/AppDelegate.swift +++ b/GDproject/Supporting files/AppDelegate.swift @@ -28,5 +28,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + + func relaunch(){ + appCoordinator.start() + } } From b9768d66c5e77a0697b638118238c146c803c192 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Tue, 23 Apr 2019 06:58:25 +0300 Subject: [PATCH 06/34] improved navigation using coordinators. improved card in channels. needs modifications --- Cartfile | 7 +- GDproject.xcodeproj/project.pbxproj | 144 +++++++++--------- .../xcshareddata/xcschemes/GDproject.xcscheme | 4 +- GDproject/Base.lproj/Main.storyboard | 98 +++++++++++- .../AnonimousChannelController.swift | 18 +++ .../ChannelListController.swift | 23 ++- .../NewPostViewController.swift | 24 +-- .../Coordinators/ChannelsCoordinator.swift | 27 +++- .../Log In/LogInViewController.swift | 4 +- GDproject/Simple model/Model.swift | 17 ++- 10 files changed, 247 insertions(+), 119 deletions(-) create mode 100644 GDproject/Controller/ News and channels/AnonimousChannelController.swift diff --git a/Cartfile b/Cartfile index f17ffce..f8cd73e 100644 --- a/Cartfile +++ b/Cartfile @@ -2,8 +2,7 @@ github "robb/Cartography" github "ivanbruel/MarkdownKit" github "roberthein/TinyConstraints" github "macteo/Marklight" -github "Alamofire/Alamofire" "5.0.0-beta.2" -github "whitesmith/WSTagsField" -github "ReactiveCocoa/ReactiveCocoa" ~> 8.0 +github "Alamofire/Alamofire" "5.0.0-beta.5" github "52inc/Pulley" -github "HeroTransitions/Hero" \ No newline at end of file +github "HeroTransitions/Hero" +github "ReactiveCocoa/ReactiveCocoa" "1c089a8" \ No newline at end of file diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index ae3ec31..be8ff67 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -8,13 +8,10 @@ /* Begin PBXBuildFile section */ 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */; }; - 123AD0E3223C1C3900326173 /* WSTagsField.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 123AD0E2223C1C3900326173 /* WSTagsField.framework */; }; - 123AD0E5223C1C4300326173 /* WSTagsField.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 123AD0E2223C1C3900326173 /* WSTagsField.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 121A8972226B1EAC005EE599 /* AnonimousChannelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */; }; 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A4221F1B3200F6E42A /* LogInViewController.swift */; }; 123E37A7221F1DD700F6E42A /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A6221F1DD700F6E42A /* MainController.swift */; }; 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; }; - 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; }; - 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; }; 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; }; 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; }; @@ -22,8 +19,6 @@ 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; }; 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; }; 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */; }; - 12924BD6224401D4003813B9 /* Hero.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12924BD5224401D4003813B9 /* Hero.framework */; }; - 12924BD8224401DC003813B9 /* Hero.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12924BD5224401D4003813B9 /* Hero.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */; }; 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */; }; 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */; }; @@ -40,54 +35,54 @@ 12E36DA1221424EA006FCDD7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12E36D9F221424EA006FCDD7 /* Main.storyboard */; }; 12E36DA3221424ED006FCDD7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 12E36DA2221424ED006FCDD7 /* Assets.xcassets */; }; 12E36DA6221424ED006FCDD7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12E36DA4221424ED006FCDD7 /* LaunchScreen.storyboard */; }; - 12E36DAF22143D40006FCDD7 /* Cartography.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DAE22143D40006FCDD7 /* Cartography.framework */; }; - 12E36DB322143D4E006FCDD7 /* Marklight.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DB222143D4E006FCDD7 /* Marklight.framework */; }; - 12E36DB522143D54006FCDD7 /* TinyConstraints.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DB422143D54006FCDD7 /* TinyConstraints.framework */; }; 12E36DB72214400A006FCDD7 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DB62214400A006FCDD7 /* Post.swift */; }; 12E36DB922144016006FCDD7 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DB822144016006FCDD7 /* User.swift */; }; - 12E36DBD221441D5006FCDD7 /* TinyConstraints.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DB422143D54006FCDD7 /* TinyConstraints.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 12E36DC0221441DB006FCDD7 /* Marklight.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DB222143D4E006FCDD7 /* Marklight.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 12E36DC2221441E1006FCDD7 /* MarkdownKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DB022143D47006FCDD7 /* MarkdownKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 12E36DC4221441EA006FCDD7 /* Cartography.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DAE22143D40006FCDD7 /* Cartography.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12E36DCA22144635006FCDD7 /* NewPostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DC922144635006FCDD7 /* NewPostViewController.swift */; }; 12E36DCC22144725006FCDD7 /* PostViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DCB22144725006FCDD7 /* PostViewCell.swift */; }; 12E36DCE22144756006FCDD7 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DCD22144756006FCDD7 /* Constants.swift */; }; 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DD022148122006FCDD7 /* FullPostController.swift */; }; 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DD422156559006FCDD7 /* MyStackView.swift */; }; - 12E36DDB221594B1006FCDD7 /* MarkdownKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12E36DB022143D47006FCDD7 /* MarkdownKit.framework */; }; 12F3D6B0224106F700A69214 /* TabbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F3D6AF224106F700A69214 /* TabbarController.swift */; }; 12F3D6B22241097B00A69214 /* ProfileCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F3D6B12241097B00A69214 /* ProfileCoordinator.swift */; }; - 12F3D6B4224112D600A69214 /* ReactiveMapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B3224112D600A69214 /* ReactiveMapKit.framework */; }; - 12F3D6B6224112E100A69214 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */; }; - 12F3D6B8224112E900A69214 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B7224112E800A69214 /* Result.framework */; }; - 12F3D6BB224112FD00A69214 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */; }; - 12F3D6BC224112FD00A69214 /* ReactiveCocoa.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 12F3D6BE2241130000A69214 /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B7224112E800A69214 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 12F3D6C02241130200A69214 /* ReactiveSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 12F3D6C22241130400A69214 /* ReactiveMapKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6B3224112D600A69214 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 12F3D6C92242645800A69214 /* Pulley.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6C72242644D00A69214 /* Pulley.framework */; }; - 12F3D6CA2242645800A69214 /* Pulley.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12F3D6C72242644D00A69214 /* Pulley.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC7C226B5E2900995B4E /* Marklight.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC75226B5E2800995B4E /* Marklight.framework */; }; + 12FDAC7D226B5E2900995B4E /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC76226B5E2800995B4E /* Result.framework */; }; + 12FDAC7E226B5E2900995B4E /* Cartography.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC77226B5E2900995B4E /* Cartography.framework */; }; + 12FDAC81226B5E2900995B4E /* MarkdownKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC7A226B5E2900995B4E /* MarkdownKit.framework */; }; + 12FDAC84226B5E3700995B4E /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC79226B5E2900995B4E /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC88226B5E4800995B4E /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC79226B5E2900995B4E /* Alamofire.framework */; }; + 12FDAC8A226B5E4B00995B4E /* Cartography.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC77226B5E2900995B4E /* Cartography.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC8B226B5E4E00995B4E /* Hero.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC78226B5E2900995B4E /* Hero.framework */; }; + 12FDAC8C226B5E4E00995B4E /* Hero.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC78226B5E2900995B4E /* Hero.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC8E226B5E5000995B4E /* MarkdownKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC7A226B5E2900995B4E /* MarkdownKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC90226B5E5200995B4E /* Marklight.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC75226B5E2800995B4E /* Marklight.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC91226B5E5400995B4E /* Pulley.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC7B226B5E2900995B4E /* Pulley.framework */; }; + 12FDAC92226B5E5400995B4E /* Pulley.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC7B226B5E2900995B4E /* Pulley.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC94226B5E5600995B4E /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC76226B5E2800995B4E /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC96226B61C400995B4E /* TinyConstraints.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC95226B61C400995B4E /* TinyConstraints.framework */; }; + 12FDAC98226B61CB00995B4E /* TinyConstraints.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC95226B61C400995B4E /* TinyConstraints.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC9B226B652C00995B4E /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC99226B652400995B4E /* ReactiveSwift.framework */; }; + 12FDAC9C226B652C00995B4E /* ReactiveSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC99226B652400995B4E /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 12FDAC9F226B9E7C00995B4E /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC9D226B9E7600995B4E /* ReactiveCocoa.framework */; }; + 12FDACA0226B9E7C00995B4E /* ReactiveCocoa.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12FDAC9D226B9E7600995B4E /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ - 12E36DBE221441D5006FCDD7 /* Embed Frameworks */ = { + 12FDAC85226B5E3700995B4E /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 12E36DC0221441DB006FCDD7 /* Marklight.framework in Embed Frameworks */, - 12F3D6C22241130400A69214 /* ReactiveMapKit.framework in Embed Frameworks */, - 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */, - 12E36DC4221441EA006FCDD7 /* Cartography.framework in Embed Frameworks */, - 12F3D6BE2241130000A69214 /* Result.framework in Embed Frameworks */, - 12E36DBD221441D5006FCDD7 /* TinyConstraints.framework in Embed Frameworks */, - 12F3D6C02241130200A69214 /* ReactiveSwift.framework in Embed Frameworks */, - 12F3D6BC224112FD00A69214 /* ReactiveCocoa.framework in Embed Frameworks */, - 12924BD8224401DC003813B9 /* Hero.framework in Embed Frameworks */, - 123AD0E5223C1C4300326173 /* WSTagsField.framework in Embed Frameworks */, - 12F3D6CA2242645800A69214 /* Pulley.framework in Embed Frameworks */, - 12E36DC2221441E1006FCDD7 /* MarkdownKit.framework in Embed Frameworks */, + 12FDAC8E226B5E5000995B4E /* MarkdownKit.framework in Embed Frameworks */, + 12FDAC98226B61CB00995B4E /* TinyConstraints.framework in Embed Frameworks */, + 12FDAC94226B5E5600995B4E /* Result.framework in Embed Frameworks */, + 12FDAC84226B5E3700995B4E /* Alamofire.framework in Embed Frameworks */, + 12FDAC8A226B5E4B00995B4E /* Cartography.framework in Embed Frameworks */, + 12FDAC9C226B652C00995B4E /* ReactiveSwift.framework in Embed Frameworks */, + 12FDAC92226B5E5400995B4E /* Pulley.framework in Embed Frameworks */, + 12FDAC8C226B5E4E00995B4E /* Hero.framework in Embed Frameworks */, + 12FDAC90226B5E5200995B4E /* Marklight.framework in Embed Frameworks */, + 12FDACA0226B9E7C00995B4E /* ReactiveCocoa.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -96,11 +91,10 @@ /* Begin PBXFileReference section */ 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplifiedChannelsList.swift; sourceTree = ""; }; - 123AD0E2223C1C3900326173 /* WSTagsField.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WSTagsField.framework; path = Carthage/Build/iOS/WSTagsField.framework; sourceTree = ""; }; + 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnonimousChannelController.swift; sourceTree = ""; }; 123E37A4221F1B3200F6E42A /* LogInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInViewController.swift; sourceTree = ""; }; 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = ""; }; 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; - 124CC7042221C2BA009DF531 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; }; 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; }; 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = ""; }; 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = ""; }; @@ -108,7 +102,6 @@ 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = ""; }; 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = ""; }; 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator1.swift; sourceTree = ""; }; - 12924BD5224401D4003813B9 /* Hero.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Hero.framework; path = Carthage/Build/iOS/Hero.framework; sourceTree = ""; }; 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; }; 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = ""; }; @@ -127,10 +120,6 @@ 12E36DA2221424ED006FCDD7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 12E36DA5221424ED006FCDD7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 12E36DA7221424ED006FCDD7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 12E36DAE22143D40006FCDD7 /* Cartography.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cartography.framework; path = Carthage/Build/iOS/Cartography.framework; sourceTree = ""; }; - 12E36DB022143D47006FCDD7 /* MarkdownKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MarkdownKit.framework; path = Carthage/Build/iOS/MarkdownKit.framework; sourceTree = ""; }; - 12E36DB222143D4E006FCDD7 /* Marklight.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Marklight.framework; path = Carthage/Build/iOS/Marklight.framework; sourceTree = ""; }; - 12E36DB422143D54006FCDD7 /* TinyConstraints.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TinyConstraints.framework; path = Carthage/Build/iOS/TinyConstraints.framework; sourceTree = ""; }; 12E36DB62214400A006FCDD7 /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = ""; }; 12E36DB822144016006FCDD7 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 12E36DC922144635006FCDD7 /* NewPostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPostViewController.swift; sourceTree = ""; }; @@ -140,11 +129,16 @@ 12E36DD422156559006FCDD7 /* MyStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyStackView.swift; sourceTree = ""; }; 12F3D6AF224106F700A69214 /* TabbarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarController.swift; sourceTree = ""; }; 12F3D6B12241097B00A69214 /* ProfileCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCoordinator.swift; sourceTree = ""; }; - 12F3D6B3224112D600A69214 /* ReactiveMapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveMapKit.framework; path = Carthage/Build/iOS/ReactiveMapKit.framework; sourceTree = ""; }; - 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/iOS/ReactiveSwift.framework; sourceTree = ""; }; - 12F3D6B7224112E800A69214 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = ""; }; - 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = Carthage/Build/iOS/ReactiveCocoa.framework; sourceTree = ""; }; - 12F3D6C72242644D00A69214 /* Pulley.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pulley.framework; path = Carthage/Build/iOS/Pulley.framework; sourceTree = ""; }; + 12FDAC75226B5E2800995B4E /* Marklight.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Marklight.framework; path = Carthage/Build/iOS/Marklight.framework; sourceTree = ""; }; + 12FDAC76226B5E2800995B4E /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = ""; }; + 12FDAC77226B5E2900995B4E /* Cartography.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cartography.framework; path = Carthage/Build/iOS/Cartography.framework; sourceTree = ""; }; + 12FDAC78226B5E2900995B4E /* Hero.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Hero.framework; path = Carthage/Build/iOS/Hero.framework; sourceTree = ""; }; + 12FDAC79226B5E2900995B4E /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; }; + 12FDAC7A226B5E2900995B4E /* MarkdownKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MarkdownKit.framework; path = Carthage/Build/iOS/MarkdownKit.framework; sourceTree = ""; }; + 12FDAC7B226B5E2900995B4E /* Pulley.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pulley.framework; path = Carthage/Build/iOS/Pulley.framework; sourceTree = ""; }; + 12FDAC95226B61C400995B4E /* TinyConstraints.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TinyConstraints.framework; path = Carthage/Build/iOS/TinyConstraints.framework; sourceTree = ""; }; + 12FDAC99226B652400995B4E /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/iOS/ReactiveSwift.framework; sourceTree = ""; }; + 12FDAC9D226B9E7600995B4E /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = Carthage/Build/iOS/ReactiveCocoa.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -152,18 +146,16 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 12E36DB522143D54006FCDD7 /* TinyConstraints.framework in Frameworks */, - 12F3D6B4224112D600A69214 /* ReactiveMapKit.framework in Frameworks */, - 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */, - 12F3D6C92242645800A69214 /* Pulley.framework in Frameworks */, - 12F3D6BB224112FD00A69214 /* ReactiveCocoa.framework in Frameworks */, - 12F3D6B8224112E900A69214 /* Result.framework in Frameworks */, - 12F3D6B6224112E100A69214 /* ReactiveSwift.framework in Frameworks */, - 12E36DDB221594B1006FCDD7 /* MarkdownKit.framework in Frameworks */, - 12924BD6224401D4003813B9 /* Hero.framework in Frameworks */, - 12E36DAF22143D40006FCDD7 /* Cartography.framework in Frameworks */, - 123AD0E3223C1C3900326173 /* WSTagsField.framework in Frameworks */, - 12E36DB322143D4E006FCDD7 /* Marklight.framework in Frameworks */, + 12FDAC7C226B5E2900995B4E /* Marklight.framework in Frameworks */, + 12FDAC96226B61C400995B4E /* TinyConstraints.framework in Frameworks */, + 12FDAC91226B5E5400995B4E /* Pulley.framework in Frameworks */, + 12FDAC9F226B9E7C00995B4E /* ReactiveCocoa.framework in Frameworks */, + 12FDAC88226B5E4800995B4E /* Alamofire.framework in Frameworks */, + 12FDAC81226B5E2900995B4E /* MarkdownKit.framework in Frameworks */, + 12FDAC7D226B5E2900995B4E /* Result.framework in Frameworks */, + 12FDAC9B226B652C00995B4E /* ReactiveSwift.framework in Frameworks */, + 12FDAC7E226B5E2900995B4E /* Cartography.framework in Frameworks */, + 12FDAC8B226B5E4E00995B4E /* Hero.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -209,18 +201,16 @@ 12E36D8F221424EA006FCDD7 = { isa = PBXGroup; children = ( - 12924BD5224401D4003813B9 /* Hero.framework */, - 12F3D6C72242644D00A69214 /* Pulley.framework */, - 12F3D6B9224112F500A69214 /* ReactiveCocoa.framework */, - 12F3D6B7224112E800A69214 /* Result.framework */, - 12F3D6B5224112E000A69214 /* ReactiveSwift.framework */, - 12F3D6B3224112D600A69214 /* ReactiveMapKit.framework */, - 123AD0E2223C1C3900326173 /* WSTagsField.framework */, - 124CC7042221C2BA009DF531 /* Alamofire.framework */, - 12E36DB422143D54006FCDD7 /* TinyConstraints.framework */, - 12E36DB222143D4E006FCDD7 /* Marklight.framework */, - 12E36DB022143D47006FCDD7 /* MarkdownKit.framework */, - 12E36DAE22143D40006FCDD7 /* Cartography.framework */, + 12FDAC9D226B9E7600995B4E /* ReactiveCocoa.framework */, + 12FDAC99226B652400995B4E /* ReactiveSwift.framework */, + 12FDAC95226B61C400995B4E /* TinyConstraints.framework */, + 12FDAC79226B5E2900995B4E /* Alamofire.framework */, + 12FDAC77226B5E2900995B4E /* Cartography.framework */, + 12FDAC78226B5E2900995B4E /* Hero.framework */, + 12FDAC7A226B5E2900995B4E /* MarkdownKit.framework */, + 12FDAC75226B5E2800995B4E /* Marklight.framework */, + 12FDAC7B226B5E2900995B4E /* Pulley.framework */, + 12FDAC76226B5E2800995B4E /* Result.framework */, 12E36D9A221424EA006FCDD7 /* GDproject */, 12E36D99221424EA006FCDD7 /* Products */, 12E36DDA221594B1006FCDD7 /* Frameworks */, @@ -288,6 +278,7 @@ isa = PBXGroup; children = ( 12D7D134221C42B700B35452 /* ChannelController.swift */, + 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */, 12D7D132221C321600B35452 /* ChannelListController.swift */, 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */, 12E36DC922144635006FCDD7 /* NewPostViewController.swift */, @@ -316,7 +307,7 @@ 12E36D94221424EA006FCDD7 /* Sources */, 12E36D95221424EA006FCDD7 /* Frameworks */, 12E36D96221424EA006FCDD7 /* Resources */, - 12E36DBE221441D5006FCDD7 /* Embed Frameworks */, + 12FDAC85226B5E3700995B4E /* Embed Frameworks */, ); buildRules = ( ); @@ -393,6 +384,7 @@ 125BD5812217314A008A3575 /* NewsVC.swift in Sources */, 12E36DB72214400A006FCDD7 /* Post.swift in Sources */, 12DB7FDF221877160096878E /* InviteViewController.swift in Sources */, + 121A8972226B1EAC005EE599 /* AnonimousChannelController.swift in Sources */, 12E36DCA22144635006FCDD7 /* NewPostViewController.swift in Sources */, 12E36D9E221424EA006FCDD7 /* NewsController.swift in Sources */, 12E36DCC22144725006FCDD7 /* PostViewCell.swift in Sources */, @@ -576,7 +568,7 @@ PRODUCT_BUNDLE_IDENTIFIER = rednikina.com.drHSE.GDproject.sos; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -600,7 +592,7 @@ PRODUCT_BUNDLE_IDENTIFIER = rednikina.com.drHSE.GDproject.sos; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; diff --git a/GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcscheme b/GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcscheme index 436c347..970dcc2 100644 --- a/GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcscheme +++ b/GDproject.xcodeproj/xcshareddata/xcschemes/GDproject.xcscheme @@ -43,8 +43,8 @@ - + @@ -506,6 +506,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -551,6 +590,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GDproject/Controller/ News and channels/AnonimousChannelController.swift b/GDproject/Controller/ News and channels/AnonimousChannelController.swift new file mode 100644 index 0000000..a841946 --- /dev/null +++ b/GDproject/Controller/ News and channels/AnonimousChannelController.swift @@ -0,0 +1,18 @@ +// +// AnonimousChannelController.swift +// GDproject +// +// Created by cstore on 20/04/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class AnonimousChannelController: UITableViewController { + + override func viewDidLoad() { + super.viewDidLoad() + } + + +} diff --git a/GDproject/Controller/ News and channels/ChannelListController.swift b/GDproject/Controller/ News and channels/ChannelListController.swift index 2859446..4bb04da 100644 --- a/GDproject/Controller/ News and channels/ChannelListController.swift +++ b/GDproject/Controller/ News and channels/ChannelListController.swift @@ -13,11 +13,16 @@ struct ChannelData{ var subtitle = String() } +protocol ChannelListData: class { + func reloadData (with channels: [Model.Channels]) +} + // TODO: make search controller availiable class ChannelListController: UITableViewController { // MARK: - Output - var onChannelSelected: ((Model.Channels) -> Void)? + var onEditingModeBegins: ((Model.Channels, IndexPath)->Void)? // MARK: - filter search controller var filteredDataSource = [Model.Channels]() @@ -88,16 +93,14 @@ class ChannelListController: UITableViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(true) - askForUpdates() + //askForUpdates() + tabBarController?.tabBar.isHidden = false } @objc func addChannel() { // editing mode is on automatically - let vc = storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController - vc.index = 1 - vc.channel = Model.Channels(people: [], name: "Untitled", tags: []) - navigationController?.pushViewController(vc, animated: true) + onEditingModeBegins?(Model.Channels(people: [], name: "Untitled", tags: []),IndexPath(row: 1, section: 0)) } static let generalChannel = Model.Channels(people: [], name: "General", tags: []) @@ -141,15 +144,11 @@ class ChannelListController: UITableViewController { override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { - - let editButton = UITableViewRowAction(style: .normal, title: "Edit") { [weak self] (action, indexPath) in - let vc = self?.storyboard?.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController - vc.index = indexPath.row - vc.channel = self?.dataSource[indexPath.row] - self?.navigationController?.pushViewController(vc, animated: true) + let editButton = UITableViewRowAction(style: .normal, title: "Edit") { [unowned self] (rowAction, indexPath) in + self.onEditingModeBegins?(self.dataSource[indexPath.row], indexPath) } - editButton.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) + editButton.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) let deleteButton = UITableViewRowAction(style: .normal, title: "Delete") { [weak self] (action, indexPath) in diff --git a/GDproject/Controller/ News and channels/NewPostViewController.swift b/GDproject/Controller/ News and channels/NewPostViewController.swift index d1268c9..1c90e38 100644 --- a/GDproject/Controller/ News and channels/NewPostViewController.swift +++ b/GDproject/Controller/ News and channels/NewPostViewController.swift @@ -10,7 +10,7 @@ import UIKit import Cartography import Marklight import TinyConstraints -import WSTagsField +// import WSTagsField class NewPostViewController: UIViewController, UITextViewDelegate { @@ -18,7 +18,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { @IBOutlet weak var viewForTags: UIView! - fileprivate let tagsField = WSTagsField() + // fileprivate let tagsField = WSTagsField() // Keep strong instance of the `NSTextStorage` subclass let textStorage = MarklightTextStorage() @@ -55,10 +55,10 @@ class NewPostViewController: UIViewController, UITextViewDelegate { setUpMD() setUpTextView() setUpAccessoryView() - setUpTagsView() + //setUpTagsView() } - func setUpTagsView(){ + /* func setUpTagsView(){ tagsField.frame = viewForTags.bounds viewForTags.addSubview(tagsField) @@ -86,7 +86,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { tagsField.acceptTagOption = .space textFieldEvents() - } + }*/ override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -94,7 +94,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { navigationController?.navigationBar.prefersLargeTitles = false navigationItem.title = "New post" textView.text = NewPostViewController.draft - tagsField.addTags(NewPostViewController.hashTagsDraft) + // tagsField.addTags(NewPostViewController.hashTagsDraft) } @@ -213,7 +213,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { var indexOfPost = 0 // MARK:- new post @objc func newPost(){ - Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: tagsField.tags.map { $0.text }) + Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: /*tagsField.tags.map { $0.text }*/ []) // adding row to uiTableView after adding new post // myProtocol?.addPost(post: p) moveBackToParentVC?() @@ -228,7 +228,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { [weak self] _ in NewPostViewController.draft = self?.textView.text ?? "" - NewPostViewController.hashTagsDraft = self?.tagsField.tags.map { $0.text } ?? [] + NewPostViewController.hashTagsDraft = /*self?.tagsField.tags.map { $0.text } ??*/ [] self?.moveBackToParentVC?() } @@ -269,7 +269,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { textView.scrollRectToVisible(rect, animated: animated) } - fileprivate func textFieldEvents() { + /*fileprivate func textFieldEvents() { tagsField.onDidAddTag = { _, _ in print("onDidAddTag") } @@ -293,16 +293,16 @@ class NewPostViewController: UIViewController, UITextViewDelegate { tagsField.onDidUnselectTagView = { _, tagView in print("Unselect \(tagView)") } - } + }*/ } extension NewPostViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { - if textField == tagsField { + /*if textField == tagsField { textView.becomeFirstResponder() - } + }*/ return true } diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift index 218ca84..6ed99b9 100644 --- a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -31,13 +31,34 @@ class ChannelsCoordinator: BaseCoordinator{ (channel) in self?.navigationController?.pushViewController(self!.presentNewsController(with: channel), animated: true) } - navigationController?.setViewControllers([channels,presentNewsController()], animated: false) + + channels.onEditingModeBegins = { [unowned self] (channel, indexPath) in + let vc = self.presentEditingChannelController(with: channel) + self.navigationController?.pushViewController(vc, animated: true) + vc.tabBarController?.tabBar.isHidden = true + } + + let nc = presentNewsController() + navigationController?.setViewControllers([channels, nc], animated: false) + } + + func presentEditingChannelController(with channel: Model.Channels? = nil) -> PulleyViewController { + + let mainContentVC = storyboard.instantiateViewController(withIdentifier: newsController+"1") as! NewsController + let drawerContentVC = storyboard.instantiateViewController(withIdentifier: "DrawerContentViewController1") + + mainContentVC.channel = channel + let pulleyDrawerVC = PulleyViewController(contentViewController: mainContentVC, drawerViewController: drawerContentVC) + + pulleyDrawerVC.initialDrawerPosition = .collapsed + + return pulleyDrawerVC } func presentNewsController(with channel: Model.Channels? = nil) -> PulleyViewController { - let mainContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: newsController) as! NewsController - let drawerContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DrawerContentViewController") + let mainContentVC = storyboard.instantiateViewController(withIdentifier: newsController) as! NewsController + let drawerContentVC = storyboard.instantiateViewController(withIdentifier: "DrawerContentViewController") mainContentVC.channel = channel mainContentVC.news.onFullPost = { diff --git a/GDproject/Controller/Log In/LogInViewController.swift b/GDproject/Controller/Log In/LogInViewController.swift index d62c48d..bdce50e 100644 --- a/GDproject/Controller/Log In/LogInViewController.swift +++ b/GDproject/Controller/Log In/LogInViewController.swift @@ -79,7 +79,7 @@ class LogInViewController: UIViewController { func logicOfLogInInputValidation() -> ((String?)->()) { let logic: ((String?)->()) = { [weak self] (someText) in - if let text = someText, !text.isEmpty, let id = Int(text) { + if let text = someText, !text.isEmpty, let _ = Int(text) { self?.logInButton.isEnabled = true } else { self?.logInButton.isEnabled = false @@ -95,7 +95,7 @@ class LogInViewController: UIViewController { setUpView() configureKeyboard() - let mailFieldValuesSignal: Signal = mailTextField.reactive.continuousTextValues + let mailFieldValuesSignal: Signal = mailTextField.reactive.continuousTextValues mailFieldValuesSignal.observeValues(logicOfLogInInputValidation()) diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index acbf367..7f48bd5 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -37,11 +37,11 @@ class Model{ static let channelsCreateURL = URL(string: "\(baseUrl)/channels/create")! static let channelsDeleteURL = URL(string: "\(baseUrl)/channels/delete")! static let channelsGetAnonURL = URL(string: "\(baseUrl)/channels/getAnonymous")! + static let complexURL = URL(string: "\(baseUrl)/complex")! - - struct QueryPosts: Codable{ + struct QueryPosts: Codable { var users: [Int: Users] - var posts: [Posts] + var response: [Posts] } struct Posts: Codable { @@ -227,7 +227,7 @@ class Model{ return } idUser = newQueery.users - completion((newQueery.users, newQueery.posts)) + completion((newQueery.users, newQueery.response)) } } @@ -274,7 +274,7 @@ class Model{ guard let newPost = try? decoder.decode(QueryPosts.self, from: json) else { return } - completion(newPost.posts) + completion(newPost.response) } } @@ -336,7 +336,7 @@ class Model{ guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return } idUser = newQueery.users - completion((newQueery.users, newQueery.posts)) + completion((newQueery.users, newQueery.response)) } } @@ -498,7 +498,10 @@ class Model{ guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return } // idUser = newQueery.users - completion((newQueery.users, newQueery.posts)) + completion((newQueery.users, newQueery.response)) } } + + + } From bce7fa115e57387d77e08cb1d2fd3b8330b4ff29 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Mon, 29 Apr 2019 23:41:37 +0300 Subject: [PATCH 07/34] Working on pagination. Still something left. Removed a lot of unnecessary information! --- GDproject.xcodeproj/project.pbxproj | 4 - .../ News and channels/NewsController.swift | 129 ++---------------- .../ News and channels/NewsVC.swift | 44 ++++-- .../Coordinators/ChannelsCoordinator.swift | 5 +- GDproject/Simple model/Model.swift | 30 +++- GDproject/Simple model/Post.swift | 26 ---- GDproject/Simple model/User.swift | 41 ------ 7 files changed, 66 insertions(+), 213 deletions(-) delete mode 100644 GDproject/Simple model/User.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index be8ff67..de42752 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -36,7 +36,6 @@ 12E36DA3221424ED006FCDD7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 12E36DA2221424ED006FCDD7 /* Assets.xcassets */; }; 12E36DA6221424ED006FCDD7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12E36DA4221424ED006FCDD7 /* LaunchScreen.storyboard */; }; 12E36DB72214400A006FCDD7 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DB62214400A006FCDD7 /* Post.swift */; }; - 12E36DB922144016006FCDD7 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DB822144016006FCDD7 /* User.swift */; }; 12E36DCA22144635006FCDD7 /* NewPostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DC922144635006FCDD7 /* NewPostViewController.swift */; }; 12E36DCC22144725006FCDD7 /* PostViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DCB22144725006FCDD7 /* PostViewCell.swift */; }; 12E36DCE22144756006FCDD7 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E36DCD22144756006FCDD7 /* Constants.swift */; }; @@ -121,7 +120,6 @@ 12E36DA5221424ED006FCDD7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 12E36DA7221424ED006FCDD7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 12E36DB62214400A006FCDD7 /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = ""; }; - 12E36DB822144016006FCDD7 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 12E36DC922144635006FCDD7 /* NewPostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPostViewController.swift; sourceTree = ""; }; 12E36DCB22144725006FCDD7 /* PostViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewCell.swift; sourceTree = ""; }; 12E36DCD22144756006FCDD7 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; @@ -248,7 +246,6 @@ 124CC7022221C00C009DF531 /* Model.swift */, 12E36DB62214400A006FCDD7 /* Post.swift */, 12D7D136221D78E800B35452 /* Channel.swift */, - 12E36DB822144016006FCDD7 /* User.swift */, ); path = "Simple model"; sourceTree = ""; @@ -379,7 +376,6 @@ 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */, 12D7D137221D78E800B35452 /* Channel.swift in Sources */, 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */, - 12E36DB922144016006FCDD7 /* User.swift in Sources */, 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */, 125BD5812217314A008A3575 /* NewsVC.swift in Sources */, 12E36DB72214400A006FCDD7 /* Post.swift in Sources */, diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index 97a21b8..0a70991 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -13,49 +13,23 @@ import Cartography protocol UpdateableWithChannel: class { var channel: Model.Channels? { get set } } -protocol NewPostDelegate { - func addPost(post: Post) -} + protocol UpdateableWithUser: class { var user: Model.Users? { get set } } // MARK:- Controller with posts and channels availiable. // Search is availiable within every table (posts and channels). Has button-functionality for boths post and chnnels -class NewsController: UIViewController, UISearchControllerDelegate, NewPostDelegate, UpdateableWithChannel +class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWithChannel { var changedChannelName: ((String)->())? @IBOutlet weak var tableView: UITableView! - var dictionary: [Int: Model.Users]? { - didSet { - - var newPosts: [Model.Posts] = [] - - posts!.forEach({ (post) in - - newPosts.append(Model.Posts(body: post.body, authorId: post.authorId, id: post.id, user: dictionary![post.authorId]!, date: post.updated, tags: post.tags)) - - post.tags.forEach { Model.Channels.fullTags.insert($0) } - - }) - - news.dataSourse = newPosts - tableView.reloadData() - } - } - - var posts: [Model.Posts]? - var channel: Model.Channels? - var anonymousChannel: (users: [Int: Model.Users],posts: [Model.Posts])? + var anonymousChannel: (users: [Int: Model.Users], posts: [Model.Posts])? // MARK: - Output - var onSelectChannel: (() -> Void)? - - func addPost(post: Post) { - //news.dataSourse.insert(post, at: 0) - } var searchController = UISearchController(searchResultsController: nil) @@ -101,16 +75,6 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg print("news clear") } - let label : UILabel = { - let label = UILabel() - label.text = "No posts to display yet!" - label.font = UIFont.systemFont(ofSize: 18) - label.textColor = .black - label.isHidden = true - label.numberOfLines = 0 - return label - }() - override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) searchController.isActive = false @@ -127,26 +91,23 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg if let channel = channel, let id = channel.id { Model.getChannel(with: id) { [weak self] in - self?.posts = $0.posts - self?.dictionary = $0.users + self?.news.dataSourse = $0.posts + self?.news.dictionary = $0.users self?.refreshContr.endRefreshing() self?.changedChannelName?(channel.name) } - - } else if let _ = posts { - print("here!") } else { Model.getLast { [weak self] in - self?.posts = $0.posts - self?.dictionary = $0.users + self?.news.dataSourse = $0.posts + self?.news.dictionary = $0.users self?.refreshContr.endRefreshing() self?.changedChannelName?("General") } } default: if let anonChannel = anonymousChannel { - posts = anonChannel.posts - dictionary = anonChannel.users + news.dataSourse = anonChannel.posts + news.dictionary = anonChannel.users changedChannelName?("Anonymous") } } @@ -157,76 +118,4 @@ class NewsController: UIViewController, UISearchControllerDelegate, NewPostDeleg tableView.dataSource = news tableView.reloadData() } - - - // for animating the banner - var topConstraint: NSLayoutConstraint? - - let bannerView: UIView = { - let view = UIView() - view.backgroundColor = .blue - view.layer.cornerRadius = 25.0 - view.clipsToBounds = true - return view - }() - - let statusLabel: UILabel = { - let label = UILabel() - label.text = "-" - label.textColor = .white - label.textAlignment = .center - return label - }() - - // MARK:- banner - private func setUpBanner() - { - view.addSubview(bannerView) - bannerView.addSubview(statusLabel) - statusLabel.edgesToSuperview() - - let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:))) - - bannerView.addGestureRecognizer(tap) - topConstraint = NSLayoutConstraint(item: bannerView, attribute: .top, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .top, multiplier: 1, constant: -300) - view.addConstraint(topConstraint!) - - bannerView.edgesToSuperview(excluding: [.bottom, .top, .left], insets: .right(20), usingSafeArea: true) - bannerView.height(50) - bannerView.width(50) - } - - // when table is scrolling no deletion is availiable - @objc func handleTap(sender: UITapGestureRecognizer? = nil) { - let indexPath = IndexPath(row: 0, section: 0) - self.tableView.scrollToRow(at: indexPath, at: .top, animated: true) -// isBannerVisible = false -// changeConstraint(isVisible: false) - } - - // animation for banner - func changeConstraint(isVisible: Bool){ - topConstraint?.constant = isVisible ? 50 : -300 - - UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { - self.view.layoutIfNeeded() - }) { (completed) in - - } - } - - var isBannerVisible: Bool = false - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - print(scrollView.contentOffset.y) - if scrollView.contentOffset.y >= 50 && !isBannerVisible{ - isBannerVisible = true - changeConstraint(isVisible: true) - } - - if isBannerVisible && scrollView.contentOffset.y == 0{ - isBannerVisible = false - changeConstraint(isVisible: false) - } - } } diff --git a/GDproject/Controller/ News and channels/NewsVC.swift b/GDproject/Controller/ News and channels/NewsVC.swift index 6f7ba0e..583991c 100644 --- a/GDproject/Controller/ News and channels/NewsVC.swift +++ b/GDproject/Controller/ News and channels/NewsVC.swift @@ -25,18 +25,29 @@ struct PostCellData{ } } -class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{ +class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { + var onChannelDidChange: ((([Int: Model.Users],[Model.Posts]))->())? var onFullPost: ((HeaderType, Model.Posts)->())? + var dictionary: [Int: Model.Users] = [:] { + didSet { + + dataSourse = dataSourse.map { + var copy = $0 + copy.user = dictionary[$0.authorId] + return copy + } + + (viewController as? NewsController)?.tableView.reloadData() + } + } + var dataSourse: [Model.Posts] = [] { didSet { - cellDataSourse = [] - dataSourse.forEach { (item) in - cellDataSourse.append(PostCellData(attributedData: PostCellData.create(with: item.body))) - } + cellDataSourse = dataSourse.map { PostCellData(attributedData: PostCellData.create(with: $0.body)) } } } @@ -96,18 +107,21 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource{ func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return 100.0 } + + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) + { + if indexPath.row == cellDataSourse.count - 1 + { + // check this! + Model.getLast(on: 10, from: dataSourse.last?.id ?? 0) + { [weak self] in + self?.dataSourse.append(contentsOf: $0.posts) + $0.users.forEach { self?.dictionary[$0.key] = $0.value } + } + } + } } -//extension NewsVC { -// override func prepare(for segue: UIStoryboardSegue, sender: Any?) -// { -// if let currentCell = sender as? PostViewCell, -// let vc = segue.destination as? FullPostController { -// -// } -// } -//} - enum HeaderType { case NONE case NEWS diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift index 6ed99b9..86223f3 100644 --- a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -74,8 +74,9 @@ class ChannelsCoordinator: BaseCoordinator{ [weak self, weak mainContentVC] (tuple) in print("hep") switch mainContentVC!.news.type { case .ANONYMOUS: - mainContentVC!.posts = tuple.1 - mainContentVC!.dictionary = tuple.0 + print("nothing to diplay") + // mainContentVC!.posts = tuple.1 + //mainContentVC!.dictionary = tuple.0 default: let vc = self!.presentNewsController() (vc.primaryContentViewController as! NewsController).anonymousChannel = tuple diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 7f48bd5..9e2ee5e 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -45,6 +45,7 @@ class Model{ } struct Posts: Codable { + var body: [Attachments] var authorId: Int var id: Int @@ -205,15 +206,34 @@ class Model{ } } - - static func getLast(completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){ - let jsonString = "30" - var request = URLRequest(url: postsLastURL) + struct PostsLastRequest: Codable { + var limit: Int + var exclusiveFrom: Int? + var request: [Int] = [] + enum CodingKeys: String, CodingKey { + case limit + case exclusiveFrom + case request + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(limit, forKey: .limit) + try container.encode(exclusiveFrom, forKey: .exclusiveFrom) + try container.encode(request, forKey: .request) + } + } + + static func getLast(on limit: Int = 10, from pointInTime: Int? = nil, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())) + { + let postRequest = PostsLastRequest(limit: limit, exclusiveFrom: pointInTime, request: []) + + var request = URLRequest(url: postsLastURL) + request.httpBody = try? JSONEncoder().encode(postRequest) request.httpMethod = "POST" // insert json data to the request - request.httpBody = jsonString.data(using: .utf8) request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") AF.request(request).responseJSON { diff --git a/GDproject/Simple model/Post.swift b/GDproject/Simple model/Post.swift index a15e7ca..101f94e 100644 --- a/GDproject/Simple model/Post.swift +++ b/GDproject/Simple model/Post.swift @@ -9,31 +9,6 @@ import Foundation import UIKit -struct Post { - var dataArray: [Media] - var fromUser: User - var atDate: String - var comments: [Comment] = [] - - var hashtags: [String] = ["ФКН", "Подбельский", "НИУВШЭ", "Шершаков", "ПАД", "Интересное","Мемы","Забавное","Учеба","Наука"] - - init(dataArray: [Media], from: User, date: String, comments: [Comment] = []) - { - self.dataArray = dataArray - fromUser = from - atDate = date - self.comments = comments - } - - init(dataArray: [Media]) { - self.dataArray = dataArray - fromUser = User(name: "vbogomazova", id: 2, fullName: "Богомазова Вероника Львовна") - atDate = "23.03.19 в 23:33" - } - -} - - enum Media{ case text(String) case image([UIImage]) @@ -41,7 +16,6 @@ enum Media{ } struct Comment { - var from: User var atTime: String var withData: String } diff --git a/GDproject/Simple model/User.swift b/GDproject/Simple model/User.swift deleted file mode 100644 index f4e2011..0000000 --- a/GDproject/Simple model/User.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// User.swift -// NewsFeed -// -// Created by cstore on 12/01/2019. -// Copyright © 2019 drHSE. All rights reserved. -// - -import Foundation -import UIKit - -struct User { - //var photo: UIImage? = #imageLiteral(resourceName: "image") - var login: String - var id: Int - var fullName: String - var initials: (surname: String, name: String, optional: String?) = ("Богомазова","Вероника","Львовна") - var placeOfWork: String? - var faculty: String? - let posts: [Post] = [] - - - init(name: String, id: Int, fullName: String) { - self.login = name - self.id = id - self.fullName = fullName - } - - init(surname: String, name: String, optional: String?, emailName: String, id: Int, place: String?, faculty: String?) - { - self.id = id - self.login = emailName - self.initials = (surname, name, optional) - - self.fullName = "\(surname) \(name) \(optional ?? "")" - self.placeOfWork = place - if let f = faculty { - self.faculty = f - } - } -} From d78240516072416644655c50f6cf769f8fb28787 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Wed, 1 May 2019 11:24:35 +0300 Subject: [PATCH 08/34] Finished pagination. Deleted pulley. --- GDproject/Base.lproj/Main.storyboard | 182 ++---------------- .../ChannelListController.swift | 2 +- .../ News and channels/NewsController.swift | 2 +- .../ News and channels/NewsVC.swift | 11 +- .../Coordinators/ChannelsCoordinator.swift | 77 +++----- 5 files changed, 51 insertions(+), 223 deletions(-) diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 757db7f..813ba1d 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ - + - + @@ -416,45 +416,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -506,146 +467,35 @@ - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/GDproject/Controller/ News and channels/ChannelListController.swift b/GDproject/Controller/ News and channels/ChannelListController.swift index 4bb04da..a267a61 100644 --- a/GDproject/Controller/ News and channels/ChannelListController.swift +++ b/GDproject/Controller/ News and channels/ChannelListController.swift @@ -103,7 +103,7 @@ class ChannelListController: UITableViewController { onEditingModeBegins?(Model.Channels(people: [], name: "Untitled", tags: []),IndexPath(row: 1, section: 0)) } - static let generalChannel = Model.Channels(people: [], name: "General", tags: []) + static let generalChannel = Model.Channels(people: [], name: "General", id: -1, tags: []) var dataSource : [Model.Channels] = [ ChannelListController.generalChannel ] diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index 0a70991..35bd452 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -88,7 +88,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi func decideWhatChannelDisplay(){ switch type! { case .NEWS, .NONE: - if let channel = channel, let id = channel.id { + if let channel = channel, let id = channel.id, id != -1 { Model.getChannel(with: id) { [weak self] in self?.news.dataSourse = $0.posts diff --git a/GDproject/Controller/ News and channels/NewsVC.swift b/GDproject/Controller/ News and channels/NewsVC.swift index 583991c..9293aa0 100644 --- a/GDproject/Controller/ News and channels/NewsVC.swift +++ b/GDproject/Controller/ News and channels/NewsVC.swift @@ -85,7 +85,7 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { } default: cell.onUserDisplay = { (id) in - print("tapped when profile is open already \(id)") + print("tapped when profile is opened already \(id)") } } @@ -108,16 +108,21 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { return 100.0 } + var prevLast = -1 + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - if indexPath.row == cellDataSourse.count - 1 + // pagination + if indexPath.row == cellDataSourse.count - 1 && prevLast != indexPath.row { // check this! - Model.getLast(on: 10, from: dataSourse.last?.id ?? 0) + Model.getLast(on: 10, from: dataSourse.last?.id ) { [weak self] in self?.dataSourse.append(contentsOf: $0.posts) $0.users.forEach { self?.dictionary[$0.key] = $0.value } } + + prevLast = indexPath.row } } } diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift index 86223f3..45c9379 100644 --- a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -27,13 +27,13 @@ class ChannelsCoordinator: BaseCoordinator{ let channels = storyboard.instantiateViewController(withIdentifier: channelListControllerId) as! ChannelListController - channels.onChannelSelected = { [weak self] + channels.onChannelSelected = { [unowned self] (channel) in - self?.navigationController?.pushViewController(self!.presentNewsController(with: channel), animated: true) + self.navigationController?.pushViewController(self.presentNewsController(with: channel), animated: true) } channels.onEditingModeBegins = { [unowned self] (channel, indexPath) in - let vc = self.presentEditingChannelController(with: channel) + let vc = self.presentNewsController(with: channel) self.navigationController?.pushViewController(vc, animated: true) vc.tabBarController?.tabBar.isHidden = true } @@ -42,60 +42,33 @@ class ChannelsCoordinator: BaseCoordinator{ navigationController?.setViewControllers([channels, nc], animated: false) } - func presentEditingChannelController(with channel: Model.Channels? = nil) -> PulleyViewController { - - let mainContentVC = storyboard.instantiateViewController(withIdentifier: newsController+"1") as! NewsController - let drawerContentVC = storyboard.instantiateViewController(withIdentifier: "DrawerContentViewController1") - + func presentNewsController(with channel: Model.Channels? = nil) -> NewsController { + let mainContentVC = storyboard.instantiateViewController(withIdentifier: newsController) as! NewsController mainContentVC.channel = channel - let pulleyDrawerVC = PulleyViewController(contentViewController: mainContentVC, drawerViewController: drawerContentVC) - pulleyDrawerVC.initialDrawerPosition = .collapsed - - return pulleyDrawerVC - } - - func presentNewsController(with channel: Model.Channels? = nil) -> PulleyViewController { - - let mainContentVC = storyboard.instantiateViewController(withIdentifier: newsController) as! NewsController - let drawerContentVC = storyboard.instantiateViewController(withIdentifier: "DrawerContentViewController") - - mainContentVC.channel = channel - mainContentVC.news.onFullPost = { - [weak self] (type,post) in - - let vc = self?.storyboard.instantiateViewController(withIdentifier: fullPostControllerId) as! FullPostController - vc.type = type - vc.post = post - self?.navigationController!.pushViewController(vc, animated: true) - } - - mainContentVC.news.onChannelDidChange = { - [weak self, weak mainContentVC] (tuple) in print("hep") - switch mainContentVC!.news.type { - case .ANONYMOUS: - print("nothing to diplay") - // mainContentVC!.posts = tuple.1 - //mainContentVC!.dictionary = tuple.0 - default: - let vc = self!.presentNewsController() - (vc.primaryContentViewController as! NewsController).anonymousChannel = tuple - (vc.primaryContentViewController as! NewsController).type = .ANONYMOUS - self?.navigationController?.pushViewController(vc, animated: true) + if channel == nil || channel?.id == -1 { + mainContentVC.news.onFullPost = { + [weak self] (type,post) in + + let vc = self?.storyboard.instantiateViewController(withIdentifier: fullPostControllerId) as! FullPostController + vc.type = type + vc.post = post + self?.navigationController!.pushViewController(vc, animated: true) } + + mainContentVC.news.onChannelDidChange = { + print("anon with \($0.0.count) users") + } + + mainContentVC.changedChannelName = { + [weak mainContentVC] (title) in mainContentVC?.navigationItem.title = title + } + + mainContentVC.navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(writePost(_:))) + ] } - let pulleyDrawerVC = PulleyViewController(contentViewController: mainContentVC, drawerViewController: drawerContentVC) - - pulleyDrawerVC.initialDrawerPosition = .collapsed - mainContentVC.changedChannelName = { - [weak pulleyDrawerVC] (title) in pulleyDrawerVC?.navigationItem.title = title - } - - pulleyDrawerVC.navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(writePost(_:))) - ] - - return pulleyDrawerVC + return mainContentVC } @objc private func writePost(_ barItem: UIBarButtonItem) From 2c1cb56a149c1647970efd8d50ad4001f0d121b5 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Wed, 1 May 2019 14:29:32 +0300 Subject: [PATCH 09/34] Changed preview concept (new vc) and added dialogs bar. Is correctly working in coordinators system. Btw wish General were a channel... --- GDproject.xcodeproj/project.pbxproj | 16 ++++ GDproject/Base.lproj/Main.storyboard | 55 ++++++++++-- GDproject/Constants.swift | 3 + .../ChannelController.swift | 30 +++++-- .../Coordinators/ChannelsCoordinator.swift | 21 ++++- .../Coordinators/MessagesCoordinator.swift | 32 +++++++ .../Coordinators/TabbarController.swift | 7 +- .../Coordinators/TabbarCoordinator.swift | 11 +++ .../Messages/MessagesViewController.swift | 90 +++++++++++++++++++ 9 files changed, 250 insertions(+), 15 deletions(-) create mode 100644 GDproject/Controller/Coordinators/MessagesCoordinator.swift create mode 100644 GDproject/Controller/Messages/MessagesViewController.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index de42752..0659027 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -19,6 +19,8 @@ 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; }; 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; }; 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */; }; + 129320042279B4270035C7B3 /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320032279B4270035C7B3 /* MessagesViewController.swift */; }; + 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320062279B5690035C7B3 /* MessagesCoordinator.swift */; }; 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */; }; 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */; }; 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */; }; @@ -101,6 +103,8 @@ 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = ""; }; 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = ""; }; 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator1.swift; sourceTree = ""; }; + 129320032279B4270035C7B3 /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; + 129320062279B5690035C7B3 /* MessagesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesCoordinator.swift; sourceTree = ""; }; 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; }; 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = ""; }; @@ -177,6 +181,7 @@ isa = PBXGroup; children = ( 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */, + 129320062279B5690035C7B3 /* MessagesCoordinator.swift */, 12F3D6AF224106F700A69214 /* TabbarController.swift */, 12F3D6B12241097B00A69214 /* ProfileCoordinator.swift */, 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */, @@ -196,6 +201,14 @@ path = "Log In"; sourceTree = ""; }; + 129320052279B4300035C7B3 /* Messages */ = { + isa = PBXGroup; + children = ( + 129320032279B4270035C7B3 /* MessagesViewController.swift */, + ); + path = Messages; + sourceTree = ""; + }; 12E36D8F221424EA006FCDD7 = { isa = PBXGroup; children = ( @@ -264,6 +277,7 @@ isa = PBXGroup; children = ( 1291BE32222154C3009D3F23 /* Coordinators */, + 129320052279B4300035C7B3 /* Messages */, 1291BE3722218DC4009D3F23 /* Log In */, 1288B5CC221F0EFE002BE6B1 /* Profile */, 12E36DD322156519006FCDD7 /* News and channels */, @@ -371,6 +385,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */, 124CC7032221C00C009DF531 /* Model.swift in Sources */, 12F3D6B0224106F700A69214 /* TabbarController.swift in Sources */, 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */, @@ -391,6 +406,7 @@ 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */, 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */, 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */, + 129320042279B4270035C7B3 /* MessagesViewController.swift in Sources */, 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */, 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */, 12F3D6B22241097B00A69214 /* ProfileCoordinator.swift in Sources */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 813ba1d..a3ed7d4 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -11,6 +11,20 @@ + + + + + + + + + + + + + + @@ -131,7 +145,7 @@ - + @@ -168,7 +182,7 @@ - + @@ -212,7 +226,7 @@ - + @@ -416,9 +430,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -428,9 +471,8 @@ - - + @@ -446,7 +488,7 @@ - + @@ -460,6 +502,7 @@ + diff --git a/GDproject/Constants.swift b/GDproject/Constants.swift index eba0588..819bfc3 100644 --- a/GDproject/Constants.swift +++ b/GDproject/Constants.swift @@ -43,6 +43,9 @@ let newsController = "NewsController" /// constant for switching to view controller for log in app let logInController = "LogInController" +// constant for getting to messages +let messagesViewControllerId = "MessagesViewController" + let profileViewController = "ProfileViewController" diff --git a/GDproject/Controller/ News and channels/ChannelController.swift b/GDproject/Controller/ News and channels/ChannelController.swift index d81153c..24a06ac 100644 --- a/GDproject/Controller/ News and channels/ChannelController.swift +++ b/GDproject/Controller/ News and channels/ChannelController.swift @@ -33,6 +33,9 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS var activeDataSource: ActiveTable = .people + // func to show preview of the current channel + var onShowingPreview: ((Model.Channels)->())? + @IBOutlet weak var viewww: UIView! @IBOutlet weak var textField: UITextField! @@ -61,16 +64,33 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS // TODO: update channel override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - if let _ = channel?.id { print("update") - Model.updateChannel(with: channel!) - } else { print("create") - Model.createChannel(with: channel!) + + defer { + super.viewWillDisappear(animated) + } + + guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else { + if let _ = channel?.id { print("update") + Model.updateChannel(with: channel!) + } else { print("create") + Model.createChannel(with: channel!) + } + return + } + // nou + } + + @objc func showPreview(){ + if let channel = channel { + onShowingPreview?(channel) } } func setUpController(){ + // setting up the preview button for the editing or created channel + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Preview", style: .done, target: self, action: #selector(showPreview)) + tableView.delegate = self tableView.dataSource = self tableView.register(UITableViewCell.self, forCellReuseIdentifier: itemCellId) diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift index 45c9379..f16dfee 100644 --- a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -33,7 +33,7 @@ class ChannelsCoordinator: BaseCoordinator{ } channels.onEditingModeBegins = { [unowned self] (channel, indexPath) in - let vc = self.presentNewsController(with: channel) + let vc = self.presentChannelController(with: channel) self.navigationController?.pushViewController(vc, animated: true) vc.tabBarController?.tabBar.isHidden = true } @@ -42,11 +42,26 @@ class ChannelsCoordinator: BaseCoordinator{ navigationController?.setViewControllers([channels, nc], animated: false) } - func presentNewsController(with channel: Model.Channels? = nil) -> NewsController { + /// Function that presents channel controller + /// + /// - Parameter channel: channel that needs to be displayed + func presentChannelController(with channel: Model.Channels? = nil) -> ChannelController { + let mainContentVC = storyboard.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController + + // to show preview we need to instantiate newsController but with different functionality + mainContentVC.onShowingPreview = { [weak mainContentVC, unowned self] ch in + let vc = self.presentNewsController(with: ch, previewMode: true) + mainContentVC?.navigationController?.pushViewController(vc, animated: true) + } + mainContentVC.channel = channel + return mainContentVC + } + + func presentNewsController(with channel: Model.Channels? = nil, previewMode: Bool = false) -> NewsController { let mainContentVC = storyboard.instantiateViewController(withIdentifier: newsController) as! NewsController mainContentVC.channel = channel - if channel == nil || channel?.id == -1 { + if !previewMode { mainContentVC.news.onFullPost = { [weak self] (type,post) in diff --git a/GDproject/Controller/Coordinators/MessagesCoordinator.swift b/GDproject/Controller/Coordinators/MessagesCoordinator.swift new file mode 100644 index 0000000..83e1b0d --- /dev/null +++ b/GDproject/Controller/Coordinators/MessagesCoordinator.swift @@ -0,0 +1,32 @@ +// +// MessagesCoordinator.swift +// GDproject +// +// Created by cstore on 01/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + + +import Foundation +import UIKit + +class MessagesCoordinator: BaseCoordinator { + + var didEndSession: (()->())? + + let storyboard = UIStoryboard(name: "Main", bundle: nil) + private weak var navigationController: UINavigationController? + + init(nc: UINavigationController) { + self.navigationController = nc + } + + override func start() { + show() + } + + private func show(){ + let vc = storyboard.instantiateViewController(withIdentifier: messagesViewControllerId) as! MessagesViewController + navigationController?.viewControllers = [vc] + } +} diff --git a/GDproject/Controller/Coordinators/TabbarController.swift b/GDproject/Controller/Coordinators/TabbarController.swift index 6c78575..b02b09c 100644 --- a/GDproject/Controller/Coordinators/TabbarController.swift +++ b/GDproject/Controller/Coordinators/TabbarController.swift @@ -12,6 +12,7 @@ import UIKit protocol TabbarView: class { var onChannelsFlowSelect: ((UINavigationController) -> ())? { get set } var onProfileFlowSelect: ((UINavigationController) -> ())? { get set } + var onMessagesFlowSelect: ((UINavigationController) -> ())? { get set } var onViewDidLoad: ((UINavigationController) -> ())? { get set } } @@ -20,6 +21,8 @@ final class TabbarController: UITabBarController, UITabBarControllerDelegate, Ta var onChannelsFlowSelect: ((UINavigationController) -> ())? var onProfileFlowSelect: ((UINavigationController) -> ())? + var onMessagesFlowSelect: ((UINavigationController) -> ())? + var onViewDidLoad: ((UINavigationController) -> ())? override func viewDidLoad() { @@ -38,8 +41,10 @@ final class TabbarController: UITabBarController, UITabBarControllerDelegate, Ta if selectedIndex == 0 { onChannelsFlowSelect?(controller) - } else { + } else if selectedIndex == 2 { onProfileFlowSelect?(controller) + } else { + onMessagesFlowSelect?(controller) } } } diff --git a/GDproject/Controller/Coordinators/TabbarCoordinator.swift b/GDproject/Controller/Coordinators/TabbarCoordinator.swift index 561a9bb..8ce6c93 100644 --- a/GDproject/Controller/Coordinators/TabbarCoordinator.swift +++ b/GDproject/Controller/Coordinators/TabbarCoordinator.swift @@ -26,6 +26,7 @@ class TabBarCoordinator: BaseCoordinator { tabbarView.onViewDidLoad = runChannelsFlow() tabbarView.onChannelsFlowSelect = runChannelsFlow() tabbarView.onProfileFlowSelect = runProfileFlow() + tabbarView.onMessagesFlowSelect = runMessagesFlow() window?.rootViewController = tabbarView as! TabbarController } @@ -40,6 +41,16 @@ class TabBarCoordinator: BaseCoordinator { } } + private func runMessagesFlow() -> ((UINavigationController) -> ()){ + return { [unowned self] navController in + if navController.viewControllers.isEmpty == true { + let messagesCoordinator = MessagesCoordinator(nc: navController) + self.addDependency(messagesCoordinator) + messagesCoordinator.start() + } + } + } + private func runProfileFlow() -> ((UINavigationController) -> ()) { return { [unowned self] navController in diff --git a/GDproject/Controller/Messages/MessagesViewController.swift b/GDproject/Controller/Messages/MessagesViewController.swift new file mode 100644 index 0000000..f40db66 --- /dev/null +++ b/GDproject/Controller/Messages/MessagesViewController.swift @@ -0,0 +1,90 @@ +// +// MessagesViewController.swift +// GDproject +// +// Created by cstore on 01/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class MessagesViewController: UITableViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Uncomment the following line to preserve selection between presentations + // self.clearsSelectionOnViewWillAppear = false + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + // #warning Incomplete implementation, return the number of sections + return 0 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return 0 + } + + /* + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + + // Configure the cell... + + return cell + } + */ + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} From b47de10a3e119b10f4af134a721045628b7cae94 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Wed, 1 May 2019 16:19:05 +0300 Subject: [PATCH 10/34] Message flow is added. You can see list of dialogs, choose a person to write message to. Needs a lot of modification because everything with my [] for now. Need to add messages in dialog. --- GDproject.xcodeproj/project.pbxproj | 8 ++ GDproject/Base.lproj/Main.storyboard | 90 ++++++++++++++- GDproject/Constants.swift | 6 +- .../Coordinators/MessagesCoordinator.swift | 13 +++ .../Messages/DialogViewController.swift | 90 +++++++++++++++ .../Messages/MessagesViewController.swift | 53 ++++++--- .../PeopleToWriteViewController.swift | 106 ++++++++++++++++++ 7 files changed, 349 insertions(+), 17 deletions(-) create mode 100644 GDproject/Controller/Messages/DialogViewController.swift create mode 100644 GDproject/Controller/Messages/PeopleToWriteViewController.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index 0659027..6190f5e 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -21,6 +21,8 @@ 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */; }; 129320042279B4270035C7B3 /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320032279B4270035C7B3 /* MessagesViewController.swift */; }; 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320062279B5690035C7B3 /* MessagesCoordinator.swift */; }; + 129320092279C3B50035C7B3 /* PeopleToWriteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */; }; + 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1293200A2279D02D0035C7B3 /* DialogViewController.swift */; }; 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */; }; 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */; }; 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */; }; @@ -105,6 +107,8 @@ 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator1.swift; sourceTree = ""; }; 129320032279B4270035C7B3 /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; 129320062279B5690035C7B3 /* MessagesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesCoordinator.swift; sourceTree = ""; }; + 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PeopleToWriteViewController.swift; path = GDproject/Controller/Messages/PeopleToWriteViewController.swift; sourceTree = SOURCE_ROOT; }; + 1293200A2279D02D0035C7B3 /* DialogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DialogViewController.swift; path = GDproject/Controller/Messages/DialogViewController.swift; sourceTree = SOURCE_ROOT; }; 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; }; 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = ""; }; @@ -204,6 +208,8 @@ 129320052279B4300035C7B3 /* Messages */ = { isa = PBXGroup; children = ( + 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */, + 1293200A2279D02D0035C7B3 /* DialogViewController.swift */, 129320032279B4270035C7B3 /* MessagesViewController.swift */, ); path = Messages; @@ -396,6 +402,7 @@ 12E36DB72214400A006FCDD7 /* Post.swift in Sources */, 12DB7FDF221877160096878E /* InviteViewController.swift in Sources */, 121A8972226B1EAC005EE599 /* AnonimousChannelController.swift in Sources */, + 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */, 12E36DCA22144635006FCDD7 /* NewPostViewController.swift in Sources */, 12E36D9E221424EA006FCDD7 /* NewsController.swift in Sources */, 12E36DCC22144725006FCDD7 /* PostViewCell.swift in Sources */, @@ -403,6 +410,7 @@ 125BD57F22171D73008A3575 /* Extentions.swift in Sources */, 12D7D133221C321600B35452 /* ChannelListController.swift in Sources */, 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */, + 129320092279C3B50035C7B3 /* PeopleToWriteViewController.swift in Sources */, 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */, 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */, 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index a3ed7d4..2364d3d 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -439,12 +439,28 @@ - + + + + + @@ -458,6 +474,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GDproject/Constants.swift b/GDproject/Constants.swift index 819bfc3..9fe7bdd 100644 --- a/GDproject/Constants.swift +++ b/GDproject/Constants.swift @@ -43,9 +43,13 @@ let newsController = "NewsController" /// constant for switching to view controller for log in app let logInController = "LogInController" -// constant for getting to messages +/// constant for getting to messages let messagesViewControllerId = "MessagesViewController" +/// constant for displaying list of people to send message to +let peopleToWriteVC = "PeopleToWriteViewController" + +let dialogVC = "DialogViewController" let profileViewController = "ProfileViewController" diff --git a/GDproject/Controller/Coordinators/MessagesCoordinator.swift b/GDproject/Controller/Coordinators/MessagesCoordinator.swift index 83e1b0d..b6bb4ea 100644 --- a/GDproject/Controller/Coordinators/MessagesCoordinator.swift +++ b/GDproject/Controller/Coordinators/MessagesCoordinator.swift @@ -27,6 +27,19 @@ class MessagesCoordinator: BaseCoordinator { private func show(){ let vc = storyboard.instantiateViewController(withIdentifier: messagesViewControllerId) as! MessagesViewController + + // choose person to write message to + vc.onUserDisplayList = { [weak vc, unowned self] in + + let newVC = self.storyboard.instantiateViewController(withIdentifier: peopleToWriteVC) as! PeopleToWriteViewController + vc?.navigationController?.pushViewController(newVC, animated: true) + } + + vc.onDialogDisplay = { [weak vc, unowned self] in + let newVC = self.storyboard.instantiateViewController(withIdentifier: dialogVC) as! DialogViewController + newVC.currentDialog = $0 + vc?.navigationController?.pushViewController(newVC, animated: true) + } navigationController?.viewControllers = [vc] } } diff --git a/GDproject/Controller/Messages/DialogViewController.swift b/GDproject/Controller/Messages/DialogViewController.swift new file mode 100644 index 0000000..635a219 --- /dev/null +++ b/GDproject/Controller/Messages/DialogViewController.swift @@ -0,0 +1,90 @@ +// +// DialogViewController.swift +// GDproject +// +// Created by cstore on 01/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class DialogViewController: UITableViewController { + + var currentDialog: (id: Int, name: String)? + + override func viewDidLoad() { + super.viewDidLoad() + + if let person = currentDialog { + navigationItem.title = person.name + } + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + // #warning Incomplete implementation, return the number of sections + return 0 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return 0 + } + + /* + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + + // Configure the cell... + + return cell + } + */ + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/GDproject/Controller/Messages/MessagesViewController.swift b/GDproject/Controller/Messages/MessagesViewController.swift index f40db66..85b95ce 100644 --- a/GDproject/Controller/Messages/MessagesViewController.swift +++ b/GDproject/Controller/Messages/MessagesViewController.swift @@ -10,38 +10,61 @@ import UIKit class MessagesViewController: UITableViewController { + // curreent Active which can be displayed + var currentActiveDialogs: [(id: Int, name: String)] = [(id: Int, name: String)]() + + var onUserDisplayList: (()->())? + + var onDialogDisplay: (((id: Int, name: String))->())? + + let searchC = UISearchController(searchResultsController: nil) + override func viewDidLoad() { super.viewDidLoad() - // Uncomment the following line to preserve selection between presentations - // self.clearsSelectionOnViewWillAppear = false - // Uncomment the following line to display an Edit button in the navigation bar for this view controller. - // self.navigationItem.rightBarButtonItem = self.editButtonItem + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Write", style: .plain, target: self, action: #selector(choosePerson)) + + self.navigationItem.title = "Messages" + // self.navigationItem.largeTitleDisplayMode = .always + self.navigationItem.searchController = searchC + navigationController?.navigationBar.prefersLargeTitles = true + self.navigationItem.hidesSearchBarWhenScrolling = false } + @objc func choosePerson(){ + onUserDisplayList?() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + tableView.reloadData() + } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { - // #warning Incomplete implementation, return the number of sections - return 0 + return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - // #warning Incomplete implementation, return the number of rows - return 0 + return currentActiveDialogs.count } - /* - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) - - // Configure the cell... + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "MessagesCell", for: indexPath) + + cell.textLabel?.text = currentActiveDialogs[indexPath.row].name + cell.detailTextLabel?.text = "from \(currentActiveDialogs[indexPath.row].name) some message is displayed here..." return cell } - */ - + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + onDialogDisplay?(currentActiveDialogs[indexPath.row]) + } /* // Override to support conditional editing of the table view. override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { diff --git a/GDproject/Controller/Messages/PeopleToWriteViewController.swift b/GDproject/Controller/Messages/PeopleToWriteViewController.swift new file mode 100644 index 0000000..4e0bea8 --- /dev/null +++ b/GDproject/Controller/Messages/PeopleToWriteViewController.swift @@ -0,0 +1,106 @@ +// +// PeopleToWriteViewController.swift +// GDproject +// +// Created by cstore on 01/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class PeopleToWriteViewController: UITableViewController { + + let searchC = UISearchController(searchResultsController: nil) + + let users = [(id: 9,name: "Anna Mikhaleva")] + + override func viewDidLoad() { + super.viewDidLoad() + + self.navigationItem.title = "People" + self.navigationItem.largeTitleDisplayMode = .never + self.navigationItem.searchController = searchC + self.navigationItem.hidesSearchBarWhenScrolling = false + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + // #warning Incomplete implementation, return the number of sections + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return users.count + } + + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "peopleToWriteCell", for: indexPath) + + cell.textLabel?.text = users[indexPath.row].name + cell.detailTextLabel?.text = "\(users[indexPath.row].id)" + + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + navigationController?.popViewController(animated: true) + + guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else + { + (navigationController?.viewControllers.last as? MessagesViewController)?.currentActiveDialogs.append(users[indexPath.row]) + (navigationController?.viewControllers.last as? MessagesViewController)?.onDialogDisplay?(users[indexPath.row]) + return + } + } + + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} From f24ae333b62fb34488aa883c8c1e663fee8f81e6 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Wed, 1 May 2019 23:00:34 +0300 Subject: [PATCH 11/34] On my way to autocompleted hashtags. --- GDproject.xcodeproj/project.pbxproj | 4 ++ GDproject/Simple model/CompletionTree.swift | 43 ++++++++++++++++++++ GDproject/Simple model/Model.swift | 14 ++++++- GDproject/Supporting files/AppDelegate.swift | 3 ++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 GDproject/Simple model/CompletionTree.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index 6190f5e..13f7513 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320062279B5690035C7B3 /* MessagesCoordinator.swift */; }; 129320092279C3B50035C7B3 /* PeopleToWriteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */; }; 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1293200A2279D02D0035C7B3 /* DialogViewController.swift */; }; + 1293200D2279D7310035C7B3 /* CompletionTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1293200C2279D7310035C7B3 /* CompletionTree.swift */; }; 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */; }; 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */; }; 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */; }; @@ -109,6 +110,7 @@ 129320062279B5690035C7B3 /* MessagesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesCoordinator.swift; sourceTree = ""; }; 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PeopleToWriteViewController.swift; path = GDproject/Controller/Messages/PeopleToWriteViewController.swift; sourceTree = SOURCE_ROOT; }; 1293200A2279D02D0035C7B3 /* DialogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DialogViewController.swift; path = GDproject/Controller/Messages/DialogViewController.swift; sourceTree = SOURCE_ROOT; }; + 1293200C2279D7310035C7B3 /* CompletionTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionTree.swift; sourceTree = ""; }; 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; }; 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = ""; }; @@ -263,6 +265,7 @@ isa = PBXGroup; children = ( 124CC7022221C00C009DF531 /* Model.swift */, + 1293200C2279D7310035C7B3 /* CompletionTree.swift */, 12E36DB62214400A006FCDD7 /* Post.swift */, 12D7D136221D78E800B35452 /* Channel.swift */, ); @@ -402,6 +405,7 @@ 12E36DB72214400A006FCDD7 /* Post.swift in Sources */, 12DB7FDF221877160096878E /* InviteViewController.swift in Sources */, 121A8972226B1EAC005EE599 /* AnonimousChannelController.swift in Sources */, + 1293200D2279D7310035C7B3 /* CompletionTree.swift in Sources */, 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */, 12E36DCA22144635006FCDD7 /* NewPostViewController.swift in Sources */, 12E36D9E221424EA006FCDD7 /* NewsController.swift in Sources */, diff --git a/GDproject/Simple model/CompletionTree.swift b/GDproject/Simple model/CompletionTree.swift new file mode 100644 index 0000000..1e2795d --- /dev/null +++ b/GDproject/Simple model/CompletionTree.swift @@ -0,0 +1,43 @@ +// +// CompletionTree.swift +// GDproject +// +// Created by cstore on 01/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import Foundation + + +struct CompletionTree: Codable { + var value: String? + var subtree: [String : CompletionTree] + + static func getCompletion(tree: CompletionTree, word: String) -> [String] { + if word == "" { + return getValues(tree: tree) + } + + let character = String(word.first!) + + if tree.subtree[character] == nil { + return [] + } else { + return getCompletion(tree: tree.subtree[character]!, word: String(word.dropFirst())) + } + } + + static func getValues(tree: CompletionTree) -> [String] { + var out = [String]() + + if let treeVal = tree.value { + out.append(treeVal) + } + + for (_, subtree) in tree.subtree { + out += getValues(tree: subtree) + } + + return out + } +} diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 9e2ee5e..e6e550f 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -14,6 +14,8 @@ class Model{ static let invalidTocken = 498 + static var hashTagTree: CompletionTree? + private static var isValidTocken: ((Int)->())? = { responce in print(responce) if responce == invalidTocken { @@ -38,6 +40,7 @@ class Model{ static let channelsDeleteURL = URL(string: "\(baseUrl)/channels/delete")! static let channelsGetAnonURL = URL(string: "\(baseUrl)/channels/getAnonymous")! static let complexURL = URL(string: "\(baseUrl)/complex")! + static let hashTagTreeURL = URL(string: "\(baseUrl)/tagCompletions")! struct QueryPosts: Codable { var users: [Int: Users] @@ -522,6 +525,15 @@ class Model{ } } - + static func getCompl(completion: @escaping ((CompletionTree)->())) { + + AF.request(URLRequest(url: hashTagTreeURL)).responseJSON { + (response) in + + guard let json = response.data else { return } + guard let tree = try? decoder.decode(CompletionTree.self, from: json) else { return } + completion(tree) + } + } } diff --git a/GDproject/Supporting files/AppDelegate.swift b/GDproject/Supporting files/AppDelegate.swift index 5ecf3ad..9f0dc28 100644 --- a/GDproject/Supporting files/AppDelegate.swift +++ b/GDproject/Supporting files/AppDelegate.swift @@ -22,6 +22,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window?.makeKeyAndVisible() // window?.rootViewController = UINavigationController() + Model.getCompl { (complTree) in + Model.hashTagTree = complTree + } appCoordinator = ApplicationCoordinator(window: window!) appCoordinator.start() From 659507bde9929a88373d2070c355260a7d217d41 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Wed, 1 May 2019 23:58:04 +0300 Subject: [PATCH 12/34] for iko without correct pagination (works only for general channel). --- Cartfile | 2 +- .../Controller/ News and channels/ChannelController.swift | 6 ++---- GDproject/Controller/ News and channels/NewsVC.swift | 2 +- GDproject/Controller/Log In/LogInViewController.swift | 2 +- GDproject/Simple model/Model.swift | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Cartfile b/Cartfile index f8cd73e..a96d1ab 100644 --- a/Cartfile +++ b/Cartfile @@ -5,4 +5,4 @@ github "macteo/Marklight" github "Alamofire/Alamofire" "5.0.0-beta.5" github "52inc/Pulley" github "HeroTransitions/Hero" -github "ReactiveCocoa/ReactiveCocoa" "1c089a8" \ No newline at end of file +github "ReactiveCocoa/ReactiveCocoa" ~> 9.0 \ No newline at end of file diff --git a/GDproject/Controller/ News and channels/ChannelController.swift b/GDproject/Controller/ News and channels/ChannelController.swift index 24a06ac..66182c6 100644 --- a/GDproject/Controller/ News and channels/ChannelController.swift +++ b/GDproject/Controller/ News and channels/ChannelController.swift @@ -25,7 +25,7 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS var channel: Model.Channels? var myProtocol: DataDelegate? - var fullTags: [String] = Array(Model.Channels.fullTags) + var fullTags: [String] = CompletionTree.getCompletion(tree: Model.hashTagTree!, word: "") var dataSourcePeople: [Model.Users] = [] @@ -252,9 +252,7 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS } tableView.reloadData() case 1: - dataSourceTags = fullTags.filter({ (s) -> Bool in - s.lowercased().contains(text.lowercased()) - }) + dataSourceTags = CompletionTree.getCompletion(tree: Model.hashTagTree!, word: text) tableView.reloadData() default: break diff --git a/GDproject/Controller/ News and channels/NewsVC.swift b/GDproject/Controller/ News and channels/NewsVC.swift index 9293aa0..3bf44f2 100644 --- a/GDproject/Controller/ News and channels/NewsVC.swift +++ b/GDproject/Controller/ News and channels/NewsVC.swift @@ -121,7 +121,7 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { self?.dataSourse.append(contentsOf: $0.posts) $0.users.forEach { self?.dictionary[$0.key] = $0.value } } - + prevLast = indexPath.row } } diff --git a/GDproject/Controller/Log In/LogInViewController.swift b/GDproject/Controller/Log In/LogInViewController.swift index bdce50e..5375350 100644 --- a/GDproject/Controller/Log In/LogInViewController.swift +++ b/GDproject/Controller/Log In/LogInViewController.swift @@ -95,7 +95,7 @@ class LogInViewController: UIViewController { setUpView() configureKeyboard() - let mailFieldValuesSignal: Signal = mailTextField.reactive.continuousTextValues + let mailFieldValuesSignal: Signal = mailTextField.reactive.continuousTextValues mailFieldValuesSignal.observeValues(logicOfLogInInputValidation()) diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index e6e550f..9e0eec6 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -133,7 +133,7 @@ class Model{ struct Channels: Codable { - static var fullTags = Set() + // static var fullTags = Set() static var fullPeople = [Users]() static var fullPeopleDict = [Int:Users]() From caebb6177983b16897aca9f5e086833f661c8b8b Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Thu, 2 May 2019 21:28:10 +0300 Subject: [PATCH 13/34] Changed creation of new channels A LOT. But still search inside people and hashtags is left (and also preview - make get Anon channel). Fixed pagination and something else. --- GDproject.xcodeproj/project.pbxproj | 8 + GDproject/Base.lproj/Main.storyboard | 79 ++++++- GDproject/Constants.swift | 4 + .../ News and channels/AddToChannelVC.swift | 117 +++++++++++ .../ChannelListController.swift | 2 +- .../ChannelViewController.swift | 195 ++++++++++++++++++ .../ News and channels/NewsController.swift | 3 +- .../ News and channels/NewsVC.swift | 21 +- .../Coordinators/ChannelsCoordinator.swift | 17 +- GDproject/Simple model/Model.swift | 20 +- 10 files changed, 440 insertions(+), 26 deletions(-) create mode 100644 GDproject/Controller/ News and channels/AddToChannelVC.swift create mode 100644 GDproject/Controller/ News and channels/ChannelViewController.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index 13f7513..6181036 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; }; 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; }; 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; }; + 1261BB93227B364C003898CF /* ChannelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB92227B364C003898CF /* ChannelViewController.swift */; }; + 1261BB95227B3991003898CF /* AddToChannelVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB94227B3991003898CF /* AddToChannelVC.swift */; }; 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1288B5CD221F1158002BE6B1 /* DataStorage.swift */; }; 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; }; 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; }; @@ -102,6 +104,8 @@ 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; }; 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = ""; }; 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = ""; }; + 1261BB92227B364C003898CF /* ChannelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelViewController.swift; sourceTree = ""; }; + 1261BB94227B3991003898CF /* AddToChannelVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToChannelVC.swift; sourceTree = ""; }; 1288B5CD221F1158002BE6B1 /* DataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStorage.swift; sourceTree = ""; }; 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = ""; }; 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = ""; }; @@ -297,6 +301,8 @@ 12E36DD322156519006FCDD7 /* News and channels */ = { isa = PBXGroup; children = ( + 1261BB92227B364C003898CF /* ChannelViewController.swift */, + 1261BB94227B3991003898CF /* AddToChannelVC.swift */, 12D7D134221C42B700B35452 /* ChannelController.swift */, 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */, 12D7D132221C321600B35452 /* ChannelListController.swift */, @@ -423,8 +429,10 @@ 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */, 12F3D6B22241097B00A69214 /* ProfileCoordinator.swift in Sources */, 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */, + 1261BB95227B3991003898CF /* AddToChannelVC.swift in Sources */, 12DB7FDB2218590C0096878E /* InfoCell.swift in Sources */, 12DB7FD922181E660096878E /* BasicInfoCell.swift in Sources */, + 1261BB93227B364C003898CF /* ChannelViewController.swift in Sources */, 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */, 12E36D9C221424EA006FCDD7 /* AppDelegate.swift in Sources */, 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 2364d3d..90133d6 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -546,10 +546,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -559,6 +635,7 @@ + diff --git a/GDproject/Constants.swift b/GDproject/Constants.swift index 9fe7bdd..fc87350 100644 --- a/GDproject/Constants.swift +++ b/GDproject/Constants.swift @@ -55,5 +55,9 @@ let profileViewController = "ProfileViewController" let simplifiedChannelsList = "SimplifiedChannelsList" +let channelViewControllerId = "ChannelViewController" + +let addToChannelVCId = "AddToChannelVC" + let blueSystemColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5) diff --git a/GDproject/Controller/ News and channels/AddToChannelVC.swift b/GDproject/Controller/ News and channels/AddToChannelVC.swift new file mode 100644 index 0000000..fad589d --- /dev/null +++ b/GDproject/Controller/ News and channels/AddToChannelVC.swift @@ -0,0 +1,117 @@ +// +// AddToChannelVC.swift +// GDproject +// +// Created by cstore on 02/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +enum DataSourse{ + case people, tags +} + +class AddToChannelVC: UITableViewController { + + var channel: Model.Channels? + var dataSourse: DataSourse = .people + weak var update: UpdatableChannel? + + // data sources for people and hashtags + var dataSourcePeople: [Model.Users] = Model.Channels.fullPeople + var fullTags: [String] = CompletionTree.getCompletion(tree: Model.hashTagTree!, word: "") + + + var reloadtable: Bool = false { + didSet{ + tableView.reloadData() + } + } + + let searchC = UISearchController(searchResultsController: nil) + + override func viewDidLoad() { + super.viewDidLoad() + + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + self.navigationItem.searchController = searchC + navigationItem.largeTitleDisplayMode = .never + self.navigationItem.hidesSearchBarWhenScrolling = false + + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + update?.updateChannel(with: channel!) + } + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch dataSourse { + case .people: + return dataSourcePeople.count + default: + return fullTags.count + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + + switch dataSourse { + case .people: + cell.textLabel?.text = dataSourcePeople[indexPath.row].fullName() + if channel!.people.contains(dataSourcePeople[indexPath.row].id) { + cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) + } else { + cell.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) + } + default: + cell.textLabel?.text = "# \(fullTags[indexPath.row])" + if channel!.tags.contains(fullTags[indexPath.row]) { + cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) + } else { + cell.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) + } + } + + cell.selectionStyle = .none + + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if let cell = tableView.cellForRow(at: indexPath) { + switch dataSourse { + case .people: + if cell.backgroundColor == #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) { + let filtered = channel!.people.filter{ $0 != dataSourcePeople[indexPath.row].id } + channel?.people = filtered + cell.backgroundColor = #colorLiteral(red: 0.9999960065, green: 1, blue: 1, alpha: 1) + } else { + channel?.people.append(dataSourcePeople[indexPath.row].id) + cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) + } + default: //tags + if cell.backgroundColor == #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) { + let filtered = channel!.tags.filter{ + !fullTags[indexPath.row].contains($0) + } + channel?.tags = filtered + cell.backgroundColor = #colorLiteral(red: 0.9999960065, green: 1, blue: 1, alpha: 1) + } else { + channel?.tags.append(fullTags[indexPath.row]) + cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) + } + } + } + } + +} diff --git a/GDproject/Controller/ News and channels/ChannelListController.swift b/GDproject/Controller/ News and channels/ChannelListController.swift index a267a61..dcdd2a0 100644 --- a/GDproject/Controller/ News and channels/ChannelListController.swift +++ b/GDproject/Controller/ News and channels/ChannelListController.swift @@ -84,7 +84,7 @@ class ChannelListController: UITableViewController { askForUpdates() } - private func askForUpdates(){ + func askForUpdates(){ Model.channelsList { [weak self] (channels) in self?.dataSource = [ChannelListController.generalChannel] + channels self?.toReload = true diff --git a/GDproject/Controller/ News and channels/ChannelViewController.swift b/GDproject/Controller/ News and channels/ChannelViewController.swift new file mode 100644 index 0000000..ee5ec3e --- /dev/null +++ b/GDproject/Controller/ News and channels/ChannelViewController.swift @@ -0,0 +1,195 @@ +// +// ChannelViewController.swift +// MessageApp +// +// Created by cstore on 02/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +protocol UpdatableName: class{ + func updateName(with name: String) +} + +protocol UpdatableChannel: class{ + func updateChannel(with channel: Model.Channels) +} + +class ChannelViewController: UITableViewController, UpdatableName, UpdatableChannel { + + func updateName(with name: String){ + channel?.name = name + } + + func updateChannel(with channel: Model.Channels) { + self.channel = channel + } + + var channel: Model.Channels? + + // func to show preview of the current channel + var onShowingPreview: ((Model.Channels)->())? + + override func viewDidLoad() + { + super.viewDidLoad() + + navigationItem.rightBarButtonItems = [ UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(showPreview)), self.editButtonItem] + navigationItem.largeTitleDisplayMode = .never + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "searchCell") + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + tableView.reloadData() + } + // TODO: update channel + override func viewWillDisappear(_ animated: Bool) { + + defer { + super.viewWillDisappear(animated) + } + + guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else { + if let _ = channel?.id + { + Model.updateChannel(with: channel!) + } else { + Model.createChannel(with: channel!) + } + return + } + // nou + } + + @objc func showPreview(){ + if let channel = channel { + onShowingPreview?(channel) + } + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + return 3 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch section { + case 1: + return (channel?.people.count ?? 0) + 1 + case 2: + return (channel?.tags.count ?? 0) + 1 + default: + return 1 + } + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + switch section { + case 1: + return "People" + case 2: + return "Tags" + default: + return "Title" + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + switch indexPath.section{ + case 1 , 2: + return whichCell(tableView, cellForRowAt: indexPath) + default: + let cell = tableView.dequeueReusableCell(withIdentifier: "titleCell") as! TitleCell + + cell.fill(title: channel?.name) + cell.update = self + + return cell + } + } + + private func whichCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let row = indexPath.row + let section = indexPath.section + + if row == 0 + { + let cell = tableView.dequeueReusableCell(withIdentifier: "searchCell")! + cell.textLabel?.text = "Add more" + cell.accessoryType = .disclosureIndicator + return cell + } + + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + + if section == 1 { + cell.textLabel?.text = Model.Channels.fullPeopleDict[channel?.people[row-1] ?? 0]?.fullName() + } else { + cell.textLabel?.text = "# \(channel!.tags[row-1])" + } + + return cell + } + + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) + { + if editingStyle == UITableViewCell.EditingStyle.delete { + if indexPath.section == 1 { + channel?.people.remove(at: indexPath.row-1) + } else { + channel?.tags.remove(at: indexPath.row-1) + } + } + + tableView.reloadData() + } + + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + if indexPath.row == 0 { + return false + } + + return true + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) + { + if indexPath.row == 0 && indexPath.section != 0 + { + let vc = storyboard?.instantiateViewController(withIdentifier: addToChannelVCId) as! AddToChannelVC + + vc.dataSourse = indexPath.section == 1 ? .people : .tags + vc.channel = channel + vc.update = self + + navigationController?.pushViewController(vc, animated: true) + } + } +} + + +class TitleCell: UITableViewCell, UITextFieldDelegate { + + @IBOutlet weak var titleLabel: UITextField! + + weak var update: UpdatableName? + + override func awakeFromNib() { + super.awakeFromNib() + } + + func fill(title: String?){ + titleLabel.text = title + titleLabel.addTarget(self, action: #selector(changedText(_:)), for: .editingChanged) + } + + @objc func changedText(_ textField: UITextField){ + update?.updateName(with: textField.text!) + } +} diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index 35bd452..374df7d 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -53,10 +53,9 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi news.viewController = self news.type = type == .NEWS ? .NEWS : type! + news.currChannel = channel setUpNavigationItemsforPosts() - - //setUpBanner() } func setUpSearchContr(){ diff --git a/GDproject/Controller/ News and channels/NewsVC.swift b/GDproject/Controller/ News and channels/NewsVC.swift index 3bf44f2..d68c59c 100644 --- a/GDproject/Controller/ News and channels/NewsVC.swift +++ b/GDproject/Controller/ News and channels/NewsVC.swift @@ -45,6 +45,8 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { } } + var currChannel : Model.Channels? + var dataSourse: [Model.Posts] = [] { didSet { cellDataSourse = dataSourse.map { PostCellData(attributedData: PostCellData.create(with: $0.body)) } @@ -115,11 +117,20 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { // pagination if indexPath.row == cellDataSourse.count - 1 && prevLast != indexPath.row { - // check this! - Model.getLast(on: 10, from: dataSourse.last?.id ) - { [weak self] in - self?.dataSourse.append(contentsOf: $0.posts) - $0.users.forEach { self?.dictionary[$0.key] = $0.value } + if let ch = currChannel, let id = ch.id, ch.id != -1{ + // check this! + Model.getChannel(with: id, on: 10, from: dataSourse.last?.id ) + { [weak self] in + self?.dataSourse.append(contentsOf: $0.posts) + $0.users.forEach { self?.dictionary[$0.key] = $0.value } + } + } else { + // check this! + Model.getLast(on: 10, from: dataSourse.last?.id ) + { [weak self] in + self?.dataSourse.append(contentsOf: $0.posts) + $0.users.forEach { self?.dictionary[$0.key] = $0.value } + } } prevLast = indexPath.row diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift index f16dfee..1602d47 100644 --- a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -32,6 +32,8 @@ class ChannelsCoordinator: BaseCoordinator{ self.navigationController?.pushViewController(self.presentNewsController(with: channel), animated: true) } + channels.askForUpdates() + channels.onEditingModeBegins = { [unowned self] (channel, indexPath) in let vc = self.presentChannelController(with: channel) self.navigationController?.pushViewController(vc, animated: true) @@ -45,8 +47,8 @@ class ChannelsCoordinator: BaseCoordinator{ /// Function that presents channel controller /// /// - Parameter channel: channel that needs to be displayed - func presentChannelController(with channel: Model.Channels? = nil) -> ChannelController { - let mainContentVC = storyboard.instantiateViewController(withIdentifier: channelControllerId) as! ChannelController + func presentChannelController(with channel: Model.Channels? = nil) -> ChannelViewController { + let mainContentVC = storyboard.instantiateViewController(withIdentifier: channelViewControllerId) as! ChannelViewController // to show preview we need to instantiate newsController but with different functionality mainContentVC.onShowingPreview = { [weak mainContentVC, unowned self] ch in @@ -57,7 +59,8 @@ class ChannelsCoordinator: BaseCoordinator{ return mainContentVC } - func presentNewsController(with channel: Model.Channels? = nil, previewMode: Bool = false) -> NewsController { + func presentNewsController(with channel: Model.Channels? = nil, previewMode: Bool = false) -> NewsController + { let mainContentVC = storyboard.instantiateViewController(withIdentifier: newsController) as! NewsController mainContentVC.channel = channel @@ -75,14 +78,14 @@ class ChannelsCoordinator: BaseCoordinator{ print("anon with \($0.0.count) users") } - mainContentVC.changedChannelName = { - [weak mainContentVC] (title) in mainContentVC?.navigationItem.title = title - } - mainContentVC.navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(writePost(_:))) ] } + mainContentVC.changedChannelName = { + [weak mainContentVC] (title) in mainContentVC?.navigationItem.title = title + } + return mainContentVC } diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 9e0eec6..6814db5 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -112,6 +112,9 @@ class Model{ var firstName: String var id: Int + func fullName() -> String { + return "\(firstName) \(lastName)" + } } struct Attachments: Codable { @@ -209,10 +212,10 @@ class Model{ } } - struct PostsLastRequest: Codable { + struct PostsLastRequest: Codable { var limit: Int var exclusiveFrom: Int? - var request: [Int] = [] + var request: T enum CodingKeys: String, CodingKey { case limit @@ -230,7 +233,7 @@ class Model{ static func getLast(on limit: Int = 10, from pointInTime: Int? = nil, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())) { - let postRequest = PostsLastRequest(limit: limit, exclusiveFrom: pointInTime, request: []) + let postRequest = PostsLastRequest<[Int]>(limit: limit, exclusiveFrom: pointInTime, request: []) var request = URLRequest(url: postsLastURL) request.httpBody = try? JSONEncoder().encode(postRequest) @@ -333,13 +336,10 @@ class Model{ } // get channel (with id): in responce -- PostQuery - static func getChannel(with channelId: Int, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){ - let json = [ - "request" : channelId, - "limit": 10 - ] - - let jsonData = try? JSONSerialization.data(withJSONObject: json) + static func getChannel(with channelId: Int, on limit: Int = 10, from pointInTime: Int? = nil, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())) + { + let postRequest = PostsLastRequest(limit: limit, exclusiveFrom: pointInTime, request: channelId) + let jsonData = try? JSONEncoder().encode(postRequest) var request = URLRequest(url: channelsGetURL) request.httpMethod = "POST" From 24023c790a91e1d09989e957dc1a7a5e61aeebb6 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Fri, 3 May 2019 00:13:23 +0300 Subject: [PATCH 14/34] Added chat info xml. Some additions to channels creation. Added new framework for hashtags --- Cartfile | 3 +- GDproject.xcodeproj/project.pbxproj | 10 ++ GDproject/Base.lproj/Main.storyboard | 100 +++++++++++++++++- .../ChannelViewController.swift | 2 +- .../Messages/ChatInfoViewController.swift | 94 ++++++++++++++++ .../Messages/DialogViewController.swift | 2 + .../PeopleToWriteViewController.swift | 2 + .../chat.imageset/Contents.json | 21 ++++ .../Assets.xcassets/chat.imageset/chat.pdf | Bin 0 -> 70136 bytes 9 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 GDproject/Controller/Messages/ChatInfoViewController.swift create mode 100644 GDproject/Supporting files/Assets.xcassets/chat.imageset/Contents.json create mode 100644 GDproject/Supporting files/Assets.xcassets/chat.imageset/chat.pdf diff --git a/Cartfile b/Cartfile index a96d1ab..fdc304e 100644 --- a/Cartfile +++ b/Cartfile @@ -5,4 +5,5 @@ github "macteo/Marklight" github "Alamofire/Alamofire" "5.0.0-beta.5" github "52inc/Pulley" github "HeroTransitions/Hero" -github "ReactiveCocoa/ReactiveCocoa" ~> 9.0 \ No newline at end of file +github "ReactiveCocoa/ReactiveCocoa" ~> 9.0 +github "ZaidSA/TaggerKit" "bb826a7" \ No newline at end of file diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index 6181036..ea07cbc 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; }; 1261BB93227B364C003898CF /* ChannelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB92227B364C003898CF /* ChannelViewController.swift */; }; 1261BB95227B3991003898CF /* AddToChannelVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB94227B3991003898CF /* AddToChannelVC.swift */; }; + 1261BB9E227B793D003898CF /* ChatInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB9D227B793D003898CF /* ChatInfoViewController.swift */; }; 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1288B5CD221F1158002BE6B1 /* DataStorage.swift */; }; 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; }; 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; }; @@ -29,6 +30,8 @@ 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */; }; 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */; }; 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */; }; + 12CE7D6F227B94B10024B6E8 /* TaggerKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12CE7D6E227B94B00024B6E8 /* TaggerKit.framework */; }; + 12CE7D71227B94B50024B6E8 /* TaggerKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12CE7D6E227B94B00024B6E8 /* TaggerKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12D7D133221C321600B35452 /* ChannelListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D132221C321600B35452 /* ChannelListController.swift */; }; 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D134221C42B700B35452 /* ChannelController.swift */; }; 12D7D137221D78E800B35452 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D7D136221D78E800B35452 /* Channel.swift */; }; @@ -88,6 +91,7 @@ 12FDAC92226B5E5400995B4E /* Pulley.framework in Embed Frameworks */, 12FDAC8C226B5E4E00995B4E /* Hero.framework in Embed Frameworks */, 12FDAC90226B5E5200995B4E /* Marklight.framework in Embed Frameworks */, + 12CE7D71227B94B50024B6E8 /* TaggerKit.framework in Embed Frameworks */, 12FDACA0226B9E7C00995B4E /* ReactiveCocoa.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -106,6 +110,7 @@ 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = ""; }; 1261BB92227B364C003898CF /* ChannelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelViewController.swift; sourceTree = ""; }; 1261BB94227B3991003898CF /* AddToChannelVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToChannelVC.swift; sourceTree = ""; }; + 1261BB9D227B793D003898CF /* ChatInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoViewController.swift; sourceTree = ""; }; 1288B5CD221F1158002BE6B1 /* DataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStorage.swift; sourceTree = ""; }; 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = ""; }; 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = ""; }; @@ -118,6 +123,7 @@ 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; }; 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = ""; }; + 12CE7D6E227B94B00024B6E8 /* TaggerKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TaggerKit.framework; path = Carthage/Build/iOS/TaggerKit.framework; sourceTree = ""; }; 12D7D132221C321600B35452 /* ChannelListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelListController.swift; sourceTree = ""; }; 12D7D134221C42B700B35452 /* ChannelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelController.swift; sourceTree = ""; }; 12D7D136221D78E800B35452 /* Channel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channel.swift; sourceTree = ""; }; @@ -167,6 +173,7 @@ 12FDAC7D226B5E2900995B4E /* Result.framework in Frameworks */, 12FDAC9B226B652C00995B4E /* ReactiveSwift.framework in Frameworks */, 12FDAC7E226B5E2900995B4E /* Cartography.framework in Frameworks */, + 12CE7D6F227B94B10024B6E8 /* TaggerKit.framework in Frameworks */, 12FDAC8B226B5E4E00995B4E /* Hero.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -214,6 +221,7 @@ 129320052279B4300035C7B3 /* Messages */ = { isa = PBXGroup; children = ( + 1261BB9D227B793D003898CF /* ChatInfoViewController.swift */, 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */, 1293200A2279D02D0035C7B3 /* DialogViewController.swift */, 129320032279B4270035C7B3 /* MessagesViewController.swift */, @@ -224,6 +232,7 @@ 12E36D8F221424EA006FCDD7 = { isa = PBXGroup; children = ( + 12CE7D6E227B94B00024B6E8 /* TaggerKit.framework */, 12FDAC9D226B9E7600995B4E /* ReactiveCocoa.framework */, 12FDAC99226B652400995B4E /* ReactiveSwift.framework */, 12FDAC95226B61C400995B4E /* TinyConstraints.framework */, @@ -414,6 +423,7 @@ 1293200D2279D7310035C7B3 /* CompletionTree.swift in Sources */, 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */, 12E36DCA22144635006FCDD7 /* NewPostViewController.swift in Sources */, + 1261BB9E227B793D003898CF /* ChatInfoViewController.swift in Sources */, 12E36D9E221424EA006FCDD7 /* NewsController.swift in Sources */, 12E36DCC22144725006FCDD7 /* PostViewCell.swift in Sources */, 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 90133d6..e8348ef 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -11,11 +11,11 @@ - + - + @@ -546,6 +546,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -593,7 +684,7 @@ - + @@ -621,7 +712,7 @@ - + @@ -709,5 +800,6 @@ + diff --git a/GDproject/Controller/ News and channels/ChannelViewController.swift b/GDproject/Controller/ News and channels/ChannelViewController.swift index ee5ec3e..ad5079f 100644 --- a/GDproject/Controller/ News and channels/ChannelViewController.swift +++ b/GDproject/Controller/ News and channels/ChannelViewController.swift @@ -129,7 +129,7 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) if section == 1 { - cell.textLabel?.text = Model.Channels.fullPeopleDict[channel?.people[row-1] ?? 0]?.fullName() + cell.textLabel?.text = "👤 \(Model.Channels.fullPeopleDict[channel?.people[row-1] ?? 0]!.fullName())" } else { cell.textLabel?.text = "# \(channel!.tags[row-1])" } diff --git a/GDproject/Controller/Messages/ChatInfoViewController.swift b/GDproject/Controller/Messages/ChatInfoViewController.swift new file mode 100644 index 0000000..d7a9c4f --- /dev/null +++ b/GDproject/Controller/Messages/ChatInfoViewController.swift @@ -0,0 +1,94 @@ +// +// ChatInfoViewController.swift +// GDproject +// +// Created by cstore on 02/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + + +/// Class for displaying chat info +/// +class ChatInfoViewController: UITableViewController { + + + override func viewDidLoad() { + super.viewDidLoad() + + // Uncomment the following line to preserve selection between presentations + // self.clearsSelectionOnViewWillAppear = false + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + // #warning Incomplete implementation, return the number of sections + return 0 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return 0 + } + + /* + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + + // Configure the cell... + + return cell + } + */ + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/GDproject/Controller/Messages/DialogViewController.swift b/GDproject/Controller/Messages/DialogViewController.swift index 635a219..2c9e462 100644 --- a/GDproject/Controller/Messages/DialogViewController.swift +++ b/GDproject/Controller/Messages/DialogViewController.swift @@ -9,6 +9,8 @@ import UIKit class DialogViewController: UITableViewController { + + var onInfoShow: (()->())? var currentDialog: (id: Int, name: String)? diff --git a/GDproject/Controller/Messages/PeopleToWriteViewController.swift b/GDproject/Controller/Messages/PeopleToWriteViewController.swift index 4e0bea8..0f075fc 100644 --- a/GDproject/Controller/Messages/PeopleToWriteViewController.swift +++ b/GDproject/Controller/Messages/PeopleToWriteViewController.swift @@ -10,6 +10,8 @@ import UIKit class PeopleToWriteViewController: UITableViewController { + // TODO: - edit button when it's used for selection + let searchC = UISearchController(searchResultsController: nil) let users = [(id: 9,name: "Anna Mikhaleva")] diff --git a/GDproject/Supporting files/Assets.xcassets/chat.imageset/Contents.json b/GDproject/Supporting files/Assets.xcassets/chat.imageset/Contents.json new file mode 100644 index 0000000..98484a4 --- /dev/null +++ b/GDproject/Supporting files/Assets.xcassets/chat.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "chat.pdf", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GDproject/Supporting files/Assets.xcassets/chat.imageset/chat.pdf b/GDproject/Supporting files/Assets.xcassets/chat.imageset/chat.pdf new file mode 100644 index 0000000000000000000000000000000000000000..285bf16479af9e5462cc2b480a60167f7f09b10c GIT binary patch literal 70136 zcma%D2e|WO^#@dzfH)8b=mi9U07;rAO+LTx!|pao(~&kwizrExv}s1OnkG>ke+Mey z1Ox@yOJ%5l2(qW33=iIw%t5n$= z-3Q-#*ZUv1@t0d~nO#9vrpjdNwQFJ4a5ULBQcYb!VRj{lpcnx+R|vSgk|hZY=D_9d z3PDlX3|w2;Zw1GQ6*QaOfBpKcHAh_)vhtdrtz%!)x6ZDhnXQoxACy>G{Iue1z2$~W z1d061wOsaJuAvBy|MNH$A<2KamdjB8d@YOR{^h;dEcvhF2=rg%WeMV6pUnXc{PQ|i zwes?{(3cHM^H;UY1I4bO1l*ZU!xgZEl@45Y^yvz={IRs(+6;+ed6J~i0!n2Ic!?^P z^JJw^qAD3GUnu6+ugzD=Sh0W?af--h35=x5IFTvk%2XkP=gZm5`u*XSuSKhSgk{0D z1flGQ3+*FY@49QbS@}0Rw6d}_x;mu|hEOrAQ1ju6C5Ku`8%|XXuCL@snAMz7I9?$L zm|wxj_3MMs*JOL^Eys5&2OMy(dcXl&${TI6v9W3Q(#@~jYpWf$zE(N9^|MQlyY|vu z&i`co(CZI5e5X&{_vt+!f8E);W3Sug#kZbz_x>Agyy-?i{N1LXST&#x(l(gVtESV? zLRpnVc_p(@RZAXe!3w(gxtq8eTm<*5>F5V%pT1}ne}z&;iIsrzq6HZ;|c|INLY>;oYiX4=t$ z>C7K$0WgK))SCLdD-X5RUwC9CxB8m(>)~=7R{0QU9bB)1>nNDn;zkS1K=VUm@pz*M z?kv~eD55Lq(t;Yr3^E1-_8#{arzHN%;)v1ogaPl^fS+U_~~b^x&HSj9)0GGA71~lwFguC-}SeP zUiignH=q9J@S<%_{WX7Bs8)+n9uZR$Xa|6Qq=e_->UyklTrapY0+pI_!Y zbJrDTymL3?wAVekTlc2jiOOw9?taskAN{m>^0i9l9cO=flMBE5J7=_g^TN;mV85ro zYX86+-*`jiYxh5S!={z(_bez|UV6y7>p%70x5VQ2sdFwEJa*caU%%@^k01S{{Ei#8 zqOLgO>W`ik)mFB7V&k{G``tgSy!)c(GLQ7O-|xuL@pOQ1m)~q~&GGK>y>FJb|JY&Z z9_Re{{WtIV)z|&8^ssr#R(EgvzJqF~b34E8-mhML`*A(-T|cdD*V}o=>puA2V~)7) zBHbKe>{Kqz=_** z-ulEB=jfaE{B9*7E_tze+R5sDd+#y5@ZIR{H?16Y@q7Aw{lYV^I)BYp-aA@9IQWRG zpMA$=fB4jq%3b^Ic=vs$T=M9-Yqox1yvIk6JLa72!#Ax{hd+4R4>mrtcrW$aM{hi{ z`^ST#`+vLdQ-wF}e*U4yocHFZAN#@UfBLB_@4H)7Zd-f7w;sRpcL)2AK78yJ_YFVr z$@hHX%6C82{N% zis9D#ee�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 Date: Fri, 3 May 2019 15:38:47 +0300 Subject: [PATCH 15/34] Channels: preview bag left. Added new view for adding hashtags. Some minor changes in api. --- GDproject.xcodeproj/project.pbxproj | 4 + GDproject/Base.lproj/Main.storyboard | 116 ++++++++++++++++-- GDproject/Constants.swift | 2 + .../ News and channels/AddToChannelVC.swift | 75 ++++++----- .../ChannelViewController.swift | 26 ++-- .../NewPostViewController.swift | 26 +++- .../TaggsSelectionViewController.swift | 106 ++++++++++++++++ .../Coordinators/ChannelsCoordinator.swift | 20 +++ GDproject/Simple model/Model.swift | 39 ++++-- 9 files changed, 349 insertions(+), 65 deletions(-) create mode 100644 GDproject/Controller/ News and channels/TaggsSelectionViewController.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index ea07cbc..9b4815b 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */; }; 121A8972226B1EAC005EE599 /* AnonimousChannelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */; }; + 1220FF94227C41270092C6BC /* TaggsSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */; }; 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A4221F1B3200F6E42A /* LogInViewController.swift */; }; 123E37A7221F1DD700F6E42A /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A6221F1DD700F6E42A /* MainController.swift */; }; 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; }; @@ -102,6 +103,7 @@ /* Begin PBXFileReference section */ 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplifiedChannelsList.swift; sourceTree = ""; }; 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnonimousChannelController.swift; sourceTree = ""; }; + 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaggsSelectionViewController.swift; sourceTree = ""; }; 123E37A4221F1B3200F6E42A /* LogInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInViewController.swift; sourceTree = ""; }; 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = ""; }; 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; @@ -311,6 +313,7 @@ isa = PBXGroup; children = ( 1261BB92227B364C003898CF /* ChannelViewController.swift */, + 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */, 1261BB94227B3991003898CF /* AddToChannelVC.swift */, 12D7D134221C42B700B35452 /* ChannelController.swift */, 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */, @@ -411,6 +414,7 @@ files = ( 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */, 124CC7032221C00C009DF531 /* Model.swift in Sources */, + 1220FF94227C41270092C6BC /* TaggsSelectionViewController.swift in Sources */, 12F3D6B0224106F700A69214 /* TabbarController.swift in Sources */, 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */, 12D7D137221D78E800B35452 /* Channel.swift in Sources */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index e8348ef..77908d9 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -379,33 +379,35 @@ - + - - - + - + + + - - - + - - + @@ -430,6 +432,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GDproject/Constants.swift b/GDproject/Constants.swift index fc87350..0f93aa6 100644 --- a/GDproject/Constants.swift +++ b/GDproject/Constants.swift @@ -59,5 +59,7 @@ let channelViewControllerId = "ChannelViewController" let addToChannelVCId = "AddToChannelVC" +let taggsSelectionViewController = "TaggsSelectionViewController" + let blueSystemColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5) diff --git a/GDproject/Controller/ News and channels/AddToChannelVC.swift b/GDproject/Controller/ News and channels/AddToChannelVC.swift index fad589d..7cdbc40 100644 --- a/GDproject/Controller/ News and channels/AddToChannelVC.swift +++ b/GDproject/Controller/ News and channels/AddToChannelVC.swift @@ -15,35 +15,42 @@ enum DataSourse{ class AddToChannelVC: UITableViewController { var channel: Model.Channels? - var dataSourse: DataSourse = .people weak var update: UpdatableChannel? // data sources for people and hashtags var dataSourcePeople: [Model.Users] = Model.Channels.fullPeople - var fullTags: [String] = CompletionTree.getCompletion(tree: Model.hashTagTree!, word: "") - var reloadtable: Bool = false { didSet{ tableView.reloadData() } } + + var filteredDataSource = [Model.Users]() + + func filterContentForSearchText(_ searchText: String, scope: String = "All") + { + filteredDataSource = dataSourcePeople.filter { $0.fullName().lowercased().contains(searchText.lowercased()) } + tableView.reloadData() + } let searchC = UISearchController(searchResultsController: nil) override func viewDidLoad() { super.viewDidLoad() + navigationItem.title = "People" tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") self.navigationItem.searchController = searchC navigationItem.largeTitleDisplayMode = .never self.navigationItem.hidesSearchBarWhenScrolling = false - + definesPresentationContext = true + searchC.searchResultsUpdater = self + searchC.obscuresBackgroundDuringPresentation = false } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - update?.updateChannel(with: channel!) } // MARK: - Table view data source @@ -52,12 +59,19 @@ class AddToChannelVC: UITableViewController { return 1 } + var isFiltering: Bool { + return searchC.isActive && !searchBarIsEmpty() + } + + func searchBarIsEmpty() -> Bool { + return searchC.searchBar.text?.isEmpty ?? true + } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - switch dataSourse { - case .people: + if isFiltering { + return filteredDataSource.count + } else { return dataSourcePeople.count - default: - return fullTags.count } } @@ -65,17 +79,16 @@ class AddToChannelVC: UITableViewController { { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) - switch dataSourse { - case .people: - cell.textLabel?.text = dataSourcePeople[indexPath.row].fullName() - if channel!.people.contains(dataSourcePeople[indexPath.row].id) { + if isFiltering{ + cell.textLabel?.text = filteredDataSource[indexPath.row].fullName() + if channel!.people.contains(filteredDataSource[indexPath.row].id) { cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) } else { cell.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) } - default: - cell.textLabel?.text = "# \(fullTags[indexPath.row])" - if channel!.tags.contains(fullTags[indexPath.row]) { + } else { + cell.textLabel?.text = dataSourcePeople[indexPath.row].fullName() + if channel!.people.contains(dataSourcePeople[indexPath.row].id) { cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) } else { cell.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) @@ -89,8 +102,16 @@ class AddToChannelVC: UITableViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath) { - switch dataSourse { - case .people: + if isFiltering{ + if cell.backgroundColor == #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) { + let filtered = channel!.people.filter{ $0 != filteredDataSource[indexPath.row].id } + channel?.people = filtered + cell.backgroundColor = #colorLiteral(red: 0.9999960065, green: 1, blue: 1, alpha: 1) + } else { + channel?.people.append(filteredDataSource[indexPath.row].id) + cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) + } + } else { if cell.backgroundColor == #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) { let filtered = channel!.people.filter{ $0 != dataSourcePeople[indexPath.row].id } channel?.people = filtered @@ -99,19 +120,13 @@ class AddToChannelVC: UITableViewController { channel?.people.append(dataSourcePeople[indexPath.row].id) cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) } - default: //tags - if cell.backgroundColor == #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) { - let filtered = channel!.tags.filter{ - !fullTags[indexPath.row].contains($0) - } - channel?.tags = filtered - cell.backgroundColor = #colorLiteral(red: 0.9999960065, green: 1, blue: 1, alpha: 1) - } else { - channel?.tags.append(fullTags[indexPath.row]) - cell.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1) - } } } } - +} + +extension AddToChannelVC : UISearchResultsUpdating { + func updateSearchResults(for searchController: UISearchController) { + filterContentForSearchText(searchController.searchBar.text!) + } } diff --git a/GDproject/Controller/ News and channels/ChannelViewController.swift b/GDproject/Controller/ News and channels/ChannelViewController.swift index ad5079f..6063bd7 100644 --- a/GDproject/Controller/ News and channels/ChannelViewController.swift +++ b/GDproject/Controller/ News and channels/ChannelViewController.swift @@ -16,7 +16,15 @@ protocol UpdatableChannel: class{ func updateChannel(with channel: Model.Channels) } -class ChannelViewController: UITableViewController, UpdatableName, UpdatableChannel { +class ChannelViewController: UITableViewController, UpdatableName, UpdatableChannel, TagsReceiver +{ + var onChoosingHashTags: (([String]?)->())? + + var onChoosingPeople: ((Model.Channels?)->())? + + func receiveTags(tags: [String]) { + self.channel?.tags = tags + } func updateName(with name: String){ channel?.name = name @@ -34,7 +42,7 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan override func viewDidLoad() { super.viewDidLoad() - + tableView.keyboardDismissMode = .onDrag navigationItem.rightBarButtonItems = [ UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(showPreview)), self.editButtonItem] navigationItem.largeTitleDisplayMode = .never tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") @@ -160,15 +168,13 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if indexPath.row == 0 && indexPath.section != 0 + if indexPath.row == 0 && indexPath.section == 1 { - let vc = storyboard?.instantiateViewController(withIdentifier: addToChannelVCId) as! AddToChannelVC - - vc.dataSourse = indexPath.section == 1 ? .people : .tags - vc.channel = channel - vc.update = self - - navigationController?.pushViewController(vc, animated: true) + onChoosingPeople?(channel) + } + + if indexPath.row == 0 && indexPath.section == 2 { + onChoosingHashTags?(channel?.tags) } } } diff --git a/GDproject/Controller/ News and channels/NewPostViewController.swift b/GDproject/Controller/ News and channels/NewPostViewController.swift index 1c90e38..18d2ef1 100644 --- a/GDproject/Controller/ News and channels/NewPostViewController.swift +++ b/GDproject/Controller/ News and channels/NewPostViewController.swift @@ -10,20 +10,38 @@ import UIKit import Cartography import Marklight import TinyConstraints +import TaggerKit // import WSTagsField -class NewPostViewController: UIViewController, UITextViewDelegate { +class NewPostViewController: UIViewController, UITextViewDelegate, TagsReceiver +{ + func receiveTags(tags: [String]) { + currentTags = tags + } @IBOutlet weak var view1: UIView! - @IBOutlet weak var viewForTags: UIView! + var currentTags: [String] = [] + + // We want the whole experience, let's create two TKCollectionViews + let productTags = TKCollectionView() + + @IBAction func chooseTags(_ sender: UIButton) { + let vc = storyboard?.instantiateViewController(withIdentifier: taggsSelectionViewController) as! TaggsSelectionViewController + + NewPostViewController.draft = textView.text + vc.receiver = self + vc.currentTags = currentTags + + navigationController?.pushViewController(vc, animated: true) + } // fileprivate let tagsField = WSTagsField() // Keep strong instance of the `NSTextStorage` subclass let textStorage = MarklightTextStorage() static var draft: String = "" - static var hashTagsDraft: [String] = [] + static var hashTagsDraft: [String] = ["tt"] var textView: UITextView! @@ -213,7 +231,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate { var indexOfPost = 0 // MARK:- new post @objc func newPost(){ - Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: /*tagsField.tags.map { $0.text }*/ []) + Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: currentTags) // adding row to uiTableView after adding new post // myProtocol?.addPost(post: p) moveBackToParentVC?() diff --git a/GDproject/Controller/ News and channels/TaggsSelectionViewController.swift b/GDproject/Controller/ News and channels/TaggsSelectionViewController.swift new file mode 100644 index 0000000..af3aff7 --- /dev/null +++ b/GDproject/Controller/ News and channels/TaggsSelectionViewController.swift @@ -0,0 +1,106 @@ +// +// TaggsSelectionViewController.swift +// GDproject +// +// Created by cstore on 03/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit +import TaggerKit + +protocol TagsReceiver: class { + func receiveTags(tags: [String]) +} + +class TaggsSelectionViewController: UIViewController { + + @IBOutlet weak var addTagsTextField: TKTextField! + + @IBOutlet weak var searchContainerView: UIView! + + @IBOutlet weak var testContainer: UIView! + + // We want the whole experience, let's create two TKCollectionViews + let productTags = TKCollectionView() + let allTags = TKCollectionView() + + var currentTags: [String]? + weak var receiver: TagsReceiver? + + // MARK: - Lifecycle Methods + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.title = "Hashtags" + addTagsTextField.delegate = self + // Customisation example + // testCollection.customFont = UIFont.boldSystemFont(ofSize: 14) // Custom font + // testCollection.customCornerRadius = 14.0 // Corner radius of tags + // testCollection.customSpacing = 20.0 // Spacing between cells + // testCollection.customBackgroundColor = UIColor.red // Background of cells + + + // These are the tags already added by the user, give an aray of strings to the collection + if let tags = currentTags { + productTags.tags = tags + } + // These are intended to be all the tags the user has added in the app, which are going to be filtered + allTags.tags = CompletionTree.getCompletion(tree: Model.hashTagTree!, word: "") + + /* + We set this collection's action to .removeTag, + becasue these are supposed to be the tags the user has already added + */ + productTags.action = .removeTag + + + // Set the current controller as the delegate of both collections + productTags.delegate = self + allTags.delegate = self + + // "testCollection" takes the tags sent by "searchCollection" + allTags.receiver = productTags + + // The tags in "searchCollection" are going to be added, so we set the action to addTag + allTags.action = .addTag + + + // Set the sender and receiver of the TextField + addTagsTextField.sender = allTags + addTagsTextField.receiver = productTags + + add(productTags, toView: testContainer) + add(allTags, toView: searchContainerView) + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if let receiver = receiver{ + receiver.receiveTags(tags: productTags.tags) + } + } + + /* + These methods come from UIViewController now conforming to TKCollectionViewDelegate, + You use these to do whatever you want when a tag is added or removed (e.g. save to file, etc) + */ + override func tagIsBeingAdded(name: String?) { + // Example: save testCollection.tags to UserDefault + print("added \(name!)") + } + + override func tagIsBeingRemoved(name: String?) { + print("removed \(name!)") + } +} + +// textField deletage to close keyboard on return +extension TaggsSelectionViewController: UITextFieldDelegate +{ + func textFieldShouldReturn(_ textField: UITextField) -> Bool + { + self.view.endEditing(true) + return false + } +} diff --git a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift index 1602d47..50b741f 100644 --- a/GDproject/Controller/Coordinators/ChannelsCoordinator.swift +++ b/GDproject/Controller/Coordinators/ChannelsCoordinator.swift @@ -55,6 +55,26 @@ class ChannelsCoordinator: BaseCoordinator{ let vc = self.presentNewsController(with: ch, previewMode: true) mainContentVC?.navigationController?.pushViewController(vc, animated: true) } + + // to go for choosing hashtags + mainContentVC.onChoosingHashTags = { [weak mainContentVC, unowned self] ch in + let vc = self.storyboard.instantiateViewController(withIdentifier: taggsSelectionViewController) as! TaggsSelectionViewController + + vc.currentTags = ch + vc.receiver = mainContentVC + + self.navigationController?.pushViewController(vc, animated: true) + } + + mainContentVC.onChoosingPeople = { [weak mainContentVC, unowned self] channel in + let vc = self.storyboard.instantiateViewController(withIdentifier: addToChannelVCId) as! AddToChannelVC + + vc.channel = channel + vc.update = mainContentVC + + self.navigationController?.pushViewController(vc, animated: true) + } + mainContentVC.channel = channel return mainContentVC } diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 6814db5..4188176 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -41,6 +41,15 @@ class Model{ static let channelsGetAnonURL = URL(string: "\(baseUrl)/channels/getAnonymous")! static let complexURL = URL(string: "\(baseUrl)/complex")! static let hashTagTreeURL = URL(string: "\(baseUrl)/tagCompletions")! + static let createGroupChatURL = URL(string: "\(baseUrl)/chats/createGroupChat")! + static let chatsGetAllURL = URL(string: "\(baseUrl)/chats/getAll")! + static let getGroupChatURL = URL(string: "\(baseUrl)/chats/getGroupChat")! + static let leaveGroupChatURL = URL(string: "\(baseUrl)/chats/leaveGroupChat")! + static let updateGroupChatURL = URL(string: "\(baseUrl)/chats/updateGroupChat")! + static let messagesGetGroupChatURL = URL(string: "\(baseUrl)/messages/get/groupChat")! //r + static let messagesSendURL = URL(string: "\(baseUrl)/messages/send")! + static let messagesUserChatURL = URL(string: "\(baseUrl)/messages/userChat")! + struct QueryPosts: Codable { var users: [Int: Users] @@ -212,15 +221,25 @@ class Model{ } } - struct PostsLastRequest: Codable { + struct GeneralRequest: Codable + { + var direction: String var limit: Int var exclusiveFrom: Int? var request: T + init(direction: String = "backward", limit: Int, exclusiveFrom: Int?, request: T) { + self.direction = direction + self.limit = limit + self.exclusiveFrom = exclusiveFrom + self.request = request + } + enum CodingKeys: String, CodingKey { case limit case exclusiveFrom case request + case direction } func encode(to encoder: Encoder) throws { @@ -228,12 +247,13 @@ class Model{ try container.encode(limit, forKey: .limit) try container.encode(exclusiveFrom, forKey: .exclusiveFrom) try container.encode(request, forKey: .request) + try container.encode(direction, forKey: .direction) } } static func getLast(on limit: Int = 10, from pointInTime: Int? = nil, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())) { - let postRequest = PostsLastRequest<[Int]>(limit: limit, exclusiveFrom: pointInTime, request: []) + let postRequest = GeneralRequest<[Int]>(limit: limit, exclusiveFrom: pointInTime, request: []) var request = URLRequest(url: postsLastURL) request.httpBody = try? JSONEncoder().encode(postRequest) @@ -275,14 +295,15 @@ class Model{ } } +// static func requestForPosts(limit: Int = 10, ){ +// +// } - static func getPostsForUser(with id: Int, completion: @escaping (([Posts])->())){ - let json = [ - "request" : id, - "limit": 10 - ] + static func getPostsForUser(for limit: Int = 10, from pointInTime: Int? = nil, with id: Int, completion: @escaping (([Posts])->())) + { + let postsRequest = GeneralRequest(limit: limit, exclusiveFrom: pointInTime, request: id) - let jsonData = try? JSONSerialization.data(withJSONObject: json) + let jsonData = try? JSONEncoder().encode(postsRequest) var request = URLRequest(url: postsForUserURL) request.httpMethod = "POST" @@ -338,7 +359,7 @@ class Model{ // get channel (with id): in responce -- PostQuery static func getChannel(with channelId: Int, on limit: Int = 10, from pointInTime: Int? = nil, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())) { - let postRequest = PostsLastRequest(limit: limit, exclusiveFrom: pointInTime, request: channelId) + let postRequest = GeneralRequest(limit: limit, exclusiveFrom: pointInTime, request: channelId) let jsonData = try? JSONEncoder().encode(postRequest) var request = URLRequest(url: channelsGetURL) From a32c3421d1d46be00e27f291f1c5c9979368f8bb Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Fri, 3 May 2019 22:52:03 +0300 Subject: [PATCH 16/34] Strated to work on dialogs: getAll is done, need to add other functionality and change design --- GDproject/Base.lproj/Main.storyboard | 2 +- GDproject/Constants.swift | 2 + .../Coordinators/MessagesCoordinator.swift | 4 +- .../Messages/DialogViewController.swift | 97 +++----- .../Messages/MessagesViewController.swift | 69 ++---- .../PeopleToWriteViewController.swift | 82 +++---- GDproject/Simple model/CompletionTree.swift | 3 +- GDproject/Simple model/Model.swift | 208 +++++++++++++++++- 8 files changed, 287 insertions(+), 180 deletions(-) diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 77908d9..149c020 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -570,7 +570,7 @@ - + diff --git a/GDproject/Constants.swift b/GDproject/Constants.swift index 0f93aa6..b26e8fc 100644 --- a/GDproject/Constants.swift +++ b/GDproject/Constants.swift @@ -61,5 +61,7 @@ let addToChannelVCId = "AddToChannelVC" let taggsSelectionViewController = "TaggsSelectionViewController" +let dialogViewController = "DialogViewController" + let blueSystemColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5) diff --git a/GDproject/Controller/Coordinators/MessagesCoordinator.swift b/GDproject/Controller/Coordinators/MessagesCoordinator.swift index b6bb4ea..20808bc 100644 --- a/GDproject/Controller/Coordinators/MessagesCoordinator.swift +++ b/GDproject/Controller/Coordinators/MessagesCoordinator.swift @@ -35,9 +35,9 @@ class MessagesCoordinator: BaseCoordinator { vc?.navigationController?.pushViewController(newVC, animated: true) } - vc.onDialogDisplay = { [weak vc, unowned self] in + vc.onDialogDisplay = { [weak vc, unowned self] _ in let newVC = self.storyboard.instantiateViewController(withIdentifier: dialogVC) as! DialogViewController - newVC.currentDialog = $0 + //newVC.currentDialog = $0 vc?.navigationController?.pushViewController(newVC, animated: true) } navigationController?.viewControllers = [vc] diff --git a/GDproject/Controller/Messages/DialogViewController.swift b/GDproject/Controller/Messages/DialogViewController.swift index 2c9e462..fadfbe2 100644 --- a/GDproject/Controller/Messages/DialogViewController.swift +++ b/GDproject/Controller/Messages/DialogViewController.swift @@ -11,82 +11,57 @@ import UIKit class DialogViewController: UITableViewController { var onInfoShow: (()->())? - - var currentDialog: (id: Int, name: String)? + var group: Model.Group? override func viewDidLoad() { super.viewDidLoad() - if let person = currentDialog { - navigationItem.title = person.name + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + + if let id = group { + navigationItem.title = "🌌 \(id.id) \(id.name)" + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Info", style: .plain, target: self, action: #selector(moveToInfoVC)) + } + } + + + @objc func moveToInfoVC(){ + + } + + var currentMessagesInChat: [Model.LastMessage]? { + didSet { + tableView.reloadData() } } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { - // #warning Incomplete implementation, return the number of sections - return 0 + return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - // #warning Incomplete implementation, return the number of rows - return 0 + return currentMessagesInChat?.count ?? 0 } - /* - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) - - // Configure the cell... - + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + if let group = group { + Model.getMessagesForGroupChat(chat: group.id) { [unowned self] in + self.currentMessagesInChat = $0 + } + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + + cell.detailTextLabel?.text = currentMessagesInChat![indexPath.row].body.markdown + cell.textLabel?.text = "\(currentMessagesInChat![indexPath.row].author)" + return cell } - */ - - /* - // Override to support conditional editing of the table view. - override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the specified item to be editable. - return true - } - */ - - /* - // Override to support editing the table view. - override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == .delete { - // Delete the row from the data source - tableView.deleteRows(at: [indexPath], with: .fade) - } else if editingStyle == .insert { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } - } - */ - - /* - // Override to support rearranging the table view. - override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { - - } - */ - - /* - // Override to support conditional rearranging of the table view. - override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the item to be re-orderable. - return true - } - */ - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. - } - */ - } diff --git a/GDproject/Controller/Messages/MessagesViewController.swift b/GDproject/Controller/Messages/MessagesViewController.swift index 85b95ce..285895c 100644 --- a/GDproject/Controller/Messages/MessagesViewController.swift +++ b/GDproject/Controller/Messages/MessagesViewController.swift @@ -11,11 +11,15 @@ import UIKit class MessagesViewController: UITableViewController { // curreent Active which can be displayed - var currentActiveDialogs: [(id: Int, name: String)] = [(id: Int, name: String)]() + var currentActiveDialogs: [Model.Dialog] = [] { + didSet{ + tableView.reloadData() + } + } var onUserDisplayList: (()->())? - var onDialogDisplay: (((id: Int, name: String))->())? + var onDialogDisplay: ((Model.Dialog)->())? let searchC = UISearchController(searchResultsController: nil) @@ -39,10 +43,12 @@ class MessagesViewController: UITableViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - tableView.reloadData() + Model.getChatAll { [weak self] in + self?.currentActiveDialogs = $0 + } } + // MARK: - Table view data source - override func numberOfSections(in tableView: UITableView) -> Int { return 1 } @@ -56,8 +62,14 @@ class MessagesViewController: UITableViewController { { let cell = tableView.dequeueReusableCell(withIdentifier: "MessagesCell", for: indexPath) - cell.textLabel?.text = currentActiveDialogs[indexPath.row].name - cell.detailTextLabel?.text = "from \(currentActiveDialogs[indexPath.row].name) some message is displayed here..." + switch currentActiveDialogs[indexPath.row].self { + case .groupChat(let group): + cell.textLabel?.text = group.group.name + cell.detailTextLabel?.text = group.lastMessage.body.markdown + case .userChat(let userChat): + cell.textLabel?.text = "\(userChat.user)" + cell.detailTextLabel?.text = userChat.lastMessage.body.markdown + } return cell } @@ -65,49 +77,4 @@ class MessagesViewController: UITableViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { onDialogDisplay?(currentActiveDialogs[indexPath.row]) } - /* - // Override to support conditional editing of the table view. - override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the specified item to be editable. - return true - } - */ - - /* - // Override to support editing the table view. - override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == .delete { - // Delete the row from the data source - tableView.deleteRows(at: [indexPath], with: .fade) - } else if editingStyle == .insert { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } - } - */ - - /* - // Override to support rearranging the table view. - override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { - - } - */ - - /* - // Override to support conditional rearranging of the table view. - override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the item to be re-orderable. - return true - } - */ - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. - } - */ - } diff --git a/GDproject/Controller/Messages/PeopleToWriteViewController.swift b/GDproject/Controller/Messages/PeopleToWriteViewController.swift index 0f075fc..7f18c59 100644 --- a/GDproject/Controller/Messages/PeopleToWriteViewController.swift +++ b/GDproject/Controller/Messages/PeopleToWriteViewController.swift @@ -14,17 +14,24 @@ class PeopleToWriteViewController: UITableViewController { let searchC = UISearchController(searchResultsController: nil) - let users = [(id: 9,name: "Anna Mikhaleva")] + let users = Model.Channels.fullPeople + + var chosenUsers: [Int: Model.UserPermission] = [:] override func viewDidLoad() { super.viewDidLoad() + tableView.isEditing = true self.navigationItem.title = "People" + self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(newMessage)) self.navigationItem.largeTitleDisplayMode = .never self.navigationItem.searchController = searchC self.navigationItem.hidesSearchBarWhenScrolling = false } + override func setEditing(_ editing: Bool, animated: Bool) { + super.setEditing(true, animated: animated) + } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { @@ -37,72 +44,33 @@ class PeopleToWriteViewController: UITableViewController { return users.count } + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + chosenUsers[users[indexPath.row].id] = Model.UserPermission(isAdmin: false) + } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "peopleToWriteCell", for: indexPath) - cell.textLabel?.text = users[indexPath.row].name + cell.textLabel?.text = users[indexPath.row].fullName() cell.detailTextLabel?.text = "\(users[indexPath.row].id)" return cell } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - navigationController?.popViewController(animated: true) - - guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else + + @objc func newMessage() + { + if chosenUsers.count != 0 { - (navigationController?.viewControllers.last as? MessagesViewController)?.currentActiveDialogs.append(users[indexPath.row]) - (navigationController?.viewControllers.last as? MessagesViewController)?.onDialogDisplay?(users[indexPath.row]) - return + navigationController?.popViewController(animated: false) + var group = Model.Group(users: chosenUsers, name: "Untitled", id: 0) + + Model.createGroupChat(from: group) { [unowned self] (id) in + let vc = self.storyboard?.instantiateViewController(withIdentifier: dialogViewController) as! DialogViewController + group.id = id + vc.group = group + self.navigationController?.pushViewController(vc, animated: true) + } } } - - - /* - // Override to support conditional editing of the table view. - override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the specified item to be editable. - return true - } - */ - - /* - // Override to support editing the table view. - override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == .delete { - // Delete the row from the data source - tableView.deleteRows(at: [indexPath], with: .fade) - } else if editingStyle == .insert { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } - } - */ - - /* - // Override to support rearranging the table view. - override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { - - } - */ - - /* - // Override to support conditional rearranging of the table view. - override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the item to be re-orderable. - return true - } - */ - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. - } - */ - } diff --git a/GDproject/Simple model/CompletionTree.swift b/GDproject/Simple model/CompletionTree.swift index 1e2795d..2d03cca 100644 --- a/GDproject/Simple model/CompletionTree.swift +++ b/GDproject/Simple model/CompletionTree.swift @@ -27,7 +27,8 @@ struct CompletionTree: Codable { } } - static func getValues(tree: CompletionTree) -> [String] { + static func getValues(tree: CompletionTree) -> [String] + { var out = [String]() if let treeVal = tree.value { diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 4188176..ded2d2c 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -51,9 +51,9 @@ class Model{ static let messagesUserChatURL = URL(string: "\(baseUrl)/messages/userChat")! - struct QueryPosts: Codable { + struct QueryPosts: Codable { var users: [Int: Users] - var response: [Posts] + var response: [T] } struct Posts: Codable { @@ -269,7 +269,7 @@ class Model{ guard let json = response.data else { return } - guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { print("no") + guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { print("no") return } idUser = newQueery.users @@ -319,7 +319,7 @@ class Model{ guard let json = response.data else { return } - guard let newPost = try? decoder.decode(QueryPosts.self, from: json) else { return } + guard let newPost = try? decoder.decode(QueryPosts.self, from: json) else { return } completion(newPost.response) } @@ -377,7 +377,7 @@ class Model{ guard let json = response.data else { return } - guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return } + guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return } idUser = newQueery.users completion((newQueery.users, newQueery.response)) @@ -539,7 +539,7 @@ class Model{ guard let json = response.data else { return } - guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return } + guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return } // idUser = newQueery.users completion((newQueery.users, newQueery.response)) @@ -550,11 +550,205 @@ class Model{ AF.request(URLRequest(url: hashTagTreeURL)).responseJSON { (response) in - + isValidTocken?(response.response?.statusCode ?? 498) guard let json = response.data else { return } guard let tree = try? decoder.decode(CompletionTree.self, from: json) else { return } completion(tree) } } + static func getChatAll(limit: Int = 10, exclusiveFrom: Int? = nil, request: [Int] = [], completion: @escaping (([Dialog])->())) + { + let req = GeneralRequest<[Int]>(limit: limit, exclusiveFrom: exclusiveFrom, request: request) + var request = URLRequest(url: chatsGetAllURL) + request.httpMethod = "POST" + request.httpBody = try? JSONEncoder().encode(req) + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + + AF.request(request).responseJSON { response in + isValidTocken?(response.response?.statusCode ?? 498) + + guard let json = response.data else { return } + let dialogs = try! decoder.decode(QueryPosts.self, from: json) + + print(dialogs.response) + completion(dialogs.response) + } + } + + enum Dialog: Codable + { + case groupChat(GroupChat) + case userChat(UserChat) + + private enum DialogKeys: CodingKey{ + case groupChat + case userChat + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: DialogKeys.self) + if container.contains(.groupChat){ + self = .groupChat(try GroupChat(from: container.superDecoder(forKey: .groupChat))) + } else if container.contains(.userChat){ + self = .userChat(try UserChat(from: container.superDecoder(forKey: .userChat))) + } else { + throw DecodingError.keyNotFound(DialogKeys.groupChat, DecodingError.Context(codingPath: container.codingPath, debugDescription: "hz")) + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: DialogKeys.self) + switch self{ + case .groupChat(let chat): + try chat.encode(to: container.superEncoder(forKey: .groupChat)) + case .userChat(let chat): + try chat.encode(to: container.superEncoder(forKey: .userChat)) + } + } + } + + struct GroupChat: Codable { + var group: Group + var lastMessage: LastMessage + } + + struct UserChat: Codable { + var user: Int + var lastMessage: LastMessage + } + + struct Group: Codable { + var users: [Int: UserPermission] + var name: String + var id: Int + } + + struct UserPermission: Codable { + var isAdmin: Bool + } + + struct LastMessage: Codable { + var body: Attachments + var destination: MessageDestination + var time: String + var author: Int + var id: Int + + private enum MessageCodingKeys: CodingKey{ + case user + case group + case body + case time + case author + case id + } + + init(from decoder: Decoder) throws + { + let container = try decoder.container(keyedBy: MessageCodingKeys.self) + time = try container.decode(String.self, forKey: .time) + body = try container.decode(Attachments.self, forKey: .body) + author = try container.decode(Int.self, forKey: .author) + id = try container.decode(Int.self, forKey: .id) + + if container.contains(.user){ + destination = MessageDestination.userChatDestination(try Int(from: container.superDecoder(forKey: .user))) + } else if container.contains(.group){ + destination = MessageDestination.groupChatDestination(try Int(from: container.superDecoder(forKey: .group))) + } else { + throw DecodingError.keyNotFound(MessageCodingKeys.group, DecodingError.Context(codingPath: container.codingPath, debugDescription: "hz gr")) + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: MessageCodingKeys.self) + + try container.encode(time, forKey: .time) + try container.encode(id, forKey: .id) + try container.encode(author, forKey: .author) + try container.encode(body, forKey: .body) + + switch destination { + case .userChatDestination(let uId): + try uId.encode(to: container.superEncoder(forKey: .user)) + case .groupChatDestination(let gId): + try gId.encode(to: container.superEncoder(forKey: .group)) + } + } + } + + enum MessageDestination: Codable + { + func encode(to encoder: Encoder) throws + { + var container = encoder.container(keyedBy: MessCodingKeys.self) + switch self { + case .userChatDestination(let uId): + try uId.encode(to: container.superEncoder(forKey: .user)) + case .groupChatDestination(let gId): + try gId.encode(to: container.superEncoder(forKey: .group)) + } + } + + init(from decoder: Decoder) throws + { + let container = try decoder.container(keyedBy: MessCodingKeys.self) + if container.contains(.user){ + self = .userChatDestination(try Int(from: container.superDecoder(forKey: .user))) + } else if container.contains(.group){ + self = .groupChatDestination(try Int(from: container.superDecoder(forKey: .group))) + } else { + throw DecodingError.keyNotFound(MessCodingKeys.group, DecodingError.Context(codingPath: container.codingPath, debugDescription: "hz gr")) + } + } + + enum MessCodingKeys: CodingKey{ + case user + case group + } + + case userChatDestination(Int) + case groupChatDestination(Int) + + } + + static func createGroupChat(from group: Group, completion: @escaping ((Int)->())) { + let req = group + var request = URLRequest(url: createGroupChatURL) + request.httpMethod = "POST" + request.httpBody = try? JSONEncoder().encode(req) + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + + AF.request(request).response { (response) in + + isValidTocken?(response.response?.statusCode ?? 498) + + guard let json = response.data else { return } + let stringInt = String.init(data: json, encoding: String.Encoding.utf8) + let intId = Int.init(stringInt!) + + completion(intId!) + } + } + + static func getMessagesForGroupChat(chat id: Int, exclusiveFrom: Int? = nil, limit l: Int = 10, direction: String = "backward", completion: @escaping (([LastMessage])->())) + { + let req = GeneralRequest(direction: direction, limit: l, exclusiveFrom: exclusiveFrom, request: id) + + var request = URLRequest(url: messagesGetGroupChatURL) + request.httpMethod = "POST" + request.httpBody = try? JSONEncoder().encode(req) + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + + AF.request(request).response { (response) in + + isValidTocken?(response.response?.statusCode ?? 498) + + guard let json = response.data else { return } + guard let messages = try? decoder.decode([LastMessage].self, from: json) else { return } + + completion(messages) + } + } } From 8f02f3af4a9cc3b8d2d9e69afe6dbba4c5962cd7 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Sat, 4 May 2019 16:48:18 +0300 Subject: [PATCH 17/34] Seach is added!!! CompletionTree algorithm's being used! Slowly moving towards only Model.GetAnonymousChannel instead of every other request (getLast, getUser..) --- GDproject.xcodeproj/project.pbxproj | 8 +-- GDproject/Base.lproj/Main.storyboard | 37 ++++++++++++ GDproject/Constants.swift | 2 + .../AnonimousChannelController.swift | 18 ------ .../ News and channels/NewsController.swift | 56 ++++++++++++++++--- .../ News and channels/NewsVC.swift | 18 +++--- .../TagsSuggestionController.swift | 52 +++++++++++++++++ GDproject/Simple model/Model.swift | 6 +- 8 files changed, 156 insertions(+), 41 deletions(-) delete mode 100644 GDproject/Controller/ News and channels/AnonimousChannelController.swift create mode 100644 GDproject/Controller/ News and channels/TagsSuggestionController.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index 9b4815b..13af48f 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */; }; - 121A8972226B1EAC005EE599 /* AnonimousChannelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */; }; + 121A8972226B1EAC005EE599 /* TagsSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121A8971226B1EAC005EE599 /* TagsSuggestionController.swift */; }; 1220FF94227C41270092C6BC /* TaggsSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */; }; 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A4221F1B3200F6E42A /* LogInViewController.swift */; }; 123E37A7221F1DD700F6E42A /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A6221F1DD700F6E42A /* MainController.swift */; }; @@ -102,7 +102,7 @@ /* Begin PBXFileReference section */ 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplifiedChannelsList.swift; sourceTree = ""; }; - 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnonimousChannelController.swift; sourceTree = ""; }; + 121A8971226B1EAC005EE599 /* TagsSuggestionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsSuggestionController.swift; sourceTree = ""; }; 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaggsSelectionViewController.swift; sourceTree = ""; }; 123E37A4221F1B3200F6E42A /* LogInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInViewController.swift; sourceTree = ""; }; 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = ""; }; @@ -316,7 +316,7 @@ 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */, 1261BB94227B3991003898CF /* AddToChannelVC.swift */, 12D7D134221C42B700B35452 /* ChannelController.swift */, - 121A8971226B1EAC005EE599 /* AnonimousChannelController.swift */, + 121A8971226B1EAC005EE599 /* TagsSuggestionController.swift */, 12D7D132221C321600B35452 /* ChannelListController.swift */, 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */, 12E36DC922144635006FCDD7 /* NewPostViewController.swift */, @@ -423,7 +423,7 @@ 125BD5812217314A008A3575 /* NewsVC.swift in Sources */, 12E36DB72214400A006FCDD7 /* Post.swift in Sources */, 12DB7FDF221877160096878E /* InviteViewController.swift in Sources */, - 121A8972226B1EAC005EE599 /* AnonimousChannelController.swift in Sources */, + 121A8972226B1EAC005EE599 /* TagsSuggestionController.swift in Sources */, 1293200D2279D7310035C7B3 /* CompletionTree.swift in Sources */, 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */, 12E36DCA22144635006FCDD7 /* NewPostViewController.swift in Sources */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 149c020..e079894 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -729,6 +729,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GDproject/Constants.swift b/GDproject/Constants.swift index b26e8fc..f2a56b7 100644 --- a/GDproject/Constants.swift +++ b/GDproject/Constants.swift @@ -63,5 +63,7 @@ let taggsSelectionViewController = "TaggsSelectionViewController" let dialogViewController = "DialogViewController" +let tagsSuggestionController = "TagsSuggestionController" + let blueSystemColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5) diff --git a/GDproject/Controller/ News and channels/AnonimousChannelController.swift b/GDproject/Controller/ News and channels/AnonimousChannelController.swift deleted file mode 100644 index a841946..0000000 --- a/GDproject/Controller/ News and channels/AnonimousChannelController.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// AnonimousChannelController.swift -// GDproject -// -// Created by cstore on 20/04/2019. -// Copyright © 2019 drHSE. All rights reserved. -// - -import UIKit - -class AnonimousChannelController: UITableViewController { - - override func viewDidLoad() { - super.viewDidLoad() - } - - -} diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index 374df7d..0658bf8 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -12,6 +12,7 @@ import Cartography protocol UpdateableWithChannel: class { var channel: Model.Channels? { get set } + func updateChannel(on channel: Model.Channels) } protocol UpdateableWithUser: class { @@ -19,8 +20,14 @@ protocol UpdateableWithUser: class { } // MARK:- Controller with posts and channels availiable. // Search is availiable within every table (posts and channels). Has button-functionality for boths post and chnnels -class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWithChannel +class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWithChannel, UISearchResultsUpdating { + func updateChannel(on channel: Model.Channels) { + self.channel = channel + news.currChannel = channel + decideWhatChannelDisplay() + } + var changedChannelName: ((String)->())? @IBOutlet weak var tableView: UITableView! @@ -31,7 +38,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi // MARK: - Output - var onSelectChannel: (() -> Void)? - var searchController = UISearchController(searchResultsController: nil) + var searchController: UISearchController? var news = NewsVC() var type: HeaderType? = .NEWS @@ -41,6 +48,11 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi override func viewDidLoad() { super.viewDidLoad() + + let updater = TagsSuggestionController() + updater.delegate = self + searchController = UISearchController(searchResultsController: updater) + navigationItem.title = "Loading ..." tableView.refreshControl = refreshContr // Configure Refresh Control @@ -49,8 +61,9 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId) tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") - // setUpSearchContr() + setUpSearchContr() + // navigationItem. news.viewController = self news.type = type == .NEWS ? .NEWS : type! news.currChannel = channel @@ -59,11 +72,12 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi } func setUpSearchContr(){ - searchController.delegate = self - searchController.obscuresBackgroundDuringPresentation = false + searchController?.delegate = self + searchController?.obscuresBackgroundDuringPresentation = false navigationItem.searchController = searchController definesPresentationContext = true - searchController.searchBar.placeholder = "Search anything" + searchController?.searchResultsUpdater = self + searchController?.searchBar.placeholder = "Search tags" } @objc func refreshPostsData( _ ff: UIRefreshControl){ @@ -76,7 +90,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - searchController.isActive = false + searchController?.isActive = false // TODO:- display something if no posts are availiable decideWhatChannelDisplay() @@ -85,16 +99,18 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi } func decideWhatChannelDisplay(){ + switch type! { case .NEWS, .NONE: if let channel = channel, let id = channel.id, id != -1 { - Model.getChannel(with: id) { [weak self] in + Model.getAnonymousChannel(by: channel) { [weak self] in self?.news.dataSourse = $0.posts self?.news.dictionary = $0.users self?.refreshContr.endRefreshing() self?.changedChannelName?(channel.name) } + } else { Model.getLast { [weak self] in self?.news.dataSourse = $0.posts @@ -110,6 +126,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi changedChannelName?("Anonymous") } } + } func setUpNavigationItemsforPosts(){ @@ -117,4 +134,27 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi tableView.dataSource = news tableView.reloadData() } + + func updateSearchResults(for searchController: UISearchController) + { + let filteredData = CompletionTree.getCompletion(tree: Model.hashTagTree!, word: searchController.searchBar.text?.lowercased() ?? "") + + // Apply the filtered results to the search results table. + if let resultsController = searchController.searchResultsController as? TagsSuggestionController { + resultsController.suggestions = filteredData + resultsController.tableView.reloadData() + } + } + + func willPresentSearchController(_ searchController: UISearchController) { + searchController.searchResultsController?.view.isHidden = false; + } + + func didPresentSearchController(_ searchController: UISearchController) { + searchController.searchResultsController?.view.isHidden = false; + } + + func didDismissSearchController(_ searchController: UISearchController) { + searchController.searchBar.text = "" + } } diff --git a/GDproject/Controller/ News and channels/NewsVC.swift b/GDproject/Controller/ News and channels/NewsVC.swift index d68c59c..4349a5f 100644 --- a/GDproject/Controller/ News and channels/NewsVC.swift +++ b/GDproject/Controller/ News and channels/NewsVC.swift @@ -91,12 +91,12 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { } } - cell.onAnonymousChannelDisplay = { - [weak self] (tag) in - Model.getAnonymousChannel(by: Model.AnonymousChannel(people: [], tags: [tag]), - completion: { (tuple) in self?.onChannelDidChange?(tuple) } - ) - } +// cell.onAnonymousChannelDisplay = { +// [weak self] (tag) in +// Model.getAnonymousChannel(by: Model.AnonymousChannel(people: [], tags: [tag]), +// completion: { (tuple) in self?.onChannelDidChange?(tuple) } +// ) +// } cell.selectionStyle = .none return cell @@ -117,13 +117,13 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { // pagination if indexPath.row == cellDataSourse.count - 1 && prevLast != indexPath.row { - if let ch = currChannel, let id = ch.id, ch.id != -1{ + if let ch = currChannel, let id = ch.id, id != -1 { // check this! - Model.getChannel(with: id, on: 10, from: dataSourse.last?.id ) - { [weak self] in + Model.getAnonymousChannel(by: ch, exclusiveFrom: dataSourse.last?.id) { [weak self] in self?.dataSourse.append(contentsOf: $0.posts) $0.users.forEach { self?.dictionary[$0.key] = $0.value } } + } else { // check this! Model.getLast(on: 10, from: dataSourse.last?.id ) diff --git a/GDproject/Controller/ News and channels/TagsSuggestionController.swift b/GDproject/Controller/ News and channels/TagsSuggestionController.swift new file mode 100644 index 0000000..1145b6b --- /dev/null +++ b/GDproject/Controller/ News and channels/TagsSuggestionController.swift @@ -0,0 +1,52 @@ +// +// AnonimousChannelController.swift +// GDproject +// +// Created by cstore on 20/04/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class TagsSuggestionController: UITableViewController +{ + weak var delegate: UpdateableWithChannel? + + var suggestions = [String]() + var channel: Model.Channels? + + override func viewDidLoad() + { + super.viewDidLoad() + if let tree = Model.hashTagTree { + suggestions = CompletionTree.getCompletion(tree: tree, word: "") + print(suggestions) + } + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + tableView.reloadData() + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return suggestions.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + cell.textLabel?.text = suggestions[indexPath.row] + + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) + { + channel = Model.Channels(people: [], name: "\(self.suggestions[indexPath.row])", id: 0, tags: [self.suggestions[indexPath.row]]) + + delegate?.updateChannel(on: channel!) + self.dismiss(animated: true) + } +} diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index ded2d2c..cddd1e0 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -525,11 +525,13 @@ class Model{ } } */ - static func getAnonymousChannel(by anonymousChannel: Model.AnonymousChannel, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){ + static func getAnonymousChannel(by anonymousChannel: Model.Channels, exclusiveFrom: Int? = nil, completion: @escaping (((users:[Int: Users], posts:[Posts]))->())){ + let req = GeneralRequest(limit: 10, exclusiveFrom: exclusiveFrom, request: anonymousChannel) + var request = URLRequest(url: channelsGetAnonURL) request.httpMethod = "POST" - request.httpBody = try? JSONEncoder().encode(anonymousChannel) + request.httpBody = try? JSONEncoder().encode(req) request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") AF.request(request).response { From 6b11f20214595b5db712e1784ac74d06873ab709 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Sat, 4 May 2019 18:05:16 +0300 Subject: [PATCH 18/34] Added tap on hashtags in NewsVC. It's working! I love Model.GetAnonymousChannel --- GDproject/Controller/ News and channels/NewsVC.swift | 12 +++++------- .../Controller/ News and channels/PostViewCell.swift | 2 ++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/GDproject/Controller/ News and channels/NewsVC.swift b/GDproject/Controller/ News and channels/NewsVC.swift index 4349a5f..8080c65 100644 --- a/GDproject/Controller/ News and channels/NewsVC.swift +++ b/GDproject/Controller/ News and channels/NewsVC.swift @@ -85,19 +85,17 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { vc.idProfile = id self?.viewController!.navigationController!.pushViewController(vc, animated: true) } + + cell.onAnonymousChannelDisplay = { + [weak self] in + (self?.viewController as? UpdateableWithChannel)?.updateChannel(on: Model.Channels(people: [], name: $0, id: 0, tags: [$0])) + } default: cell.onUserDisplay = { (id) in print("tapped when profile is opened already \(id)") } } -// cell.onAnonymousChannelDisplay = { -// [weak self] (tag) in -// Model.getAnonymousChannel(by: Model.AnonymousChannel(people: [], tags: [tag]), -// completion: { (tuple) in self?.onChannelDidChange?(tuple) } -// ) -// } - cell.selectionStyle = .none return cell } diff --git a/GDproject/Controller/ News and channels/PostViewCell.swift b/GDproject/Controller/ News and channels/PostViewCell.swift index 7adab89..7dae0c8 100644 --- a/GDproject/Controller/ News and channels/PostViewCell.swift +++ b/GDproject/Controller/ News and channels/PostViewCell.swift @@ -13,6 +13,7 @@ import TinyConstraints class PostViewCell: UITableViewCell { + var onUserDisplay: ((Int)->())? var onAnonymousChannelDisplay: ((String)->())? @@ -123,6 +124,7 @@ class PostViewCell: UITableViewCell let button = UIButton() button.setTitle("#" + hash, for: .normal) button.addTarget(self, action: #selector(setAnonymousChannel(on:)), for: .touchUpInside) + // button.addGestureRecognizer(longPressRecognizer) button.titleLabel?.font = UIFont.monospacedDigitSystemFont(ofSize: 15, weight: .semibold) button.setTitleColor(#colorLiteral(red: 0, green: 0.4784313725, blue: 1, alpha: 1), for: .normal) buttons.append(button) From b65cc457d4b7f3a60bdb14511c74639d1c9107dd Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Mon, 6 May 2019 01:25:40 +0300 Subject: [PATCH 19/34] Worked on messages: infoVC is almost perfect, CGAffineTransform is done, whole design-beauty-style is left thought.. --- GDproject.xcodeproj/project.pbxproj | 20 +- GDproject/Base.lproj/Main.storyboard | 113 +++----- GDproject/Constants.swift | 2 + .../ChannelViewController.swift | 5 +- .../ News and channels/NewsController.swift | 1 + .../ News and channels/PostViewCell.swift | 5 +- .../Coordinators/MessagesCoordinator.swift | 13 +- .../Messages/ChatInfoViewController.swift | 258 ++++++++++++++---- .../Messages/Dialog/DialogCell.swift | 93 +++++++ .../Dialog/DialogViewController.swift | 145 ++++++++++ .../Messages/DialogViewController.swift | 67 ----- .../Messages/MessagesViewController.swift | 17 +- .../PeopleToWriteViewController.swift | 20 +- GDproject/Simple model/Model.swift | 62 ++++- 14 files changed, 582 insertions(+), 239 deletions(-) create mode 100644 GDproject/Controller/Messages/Dialog/DialogCell.swift create mode 100644 GDproject/Controller/Messages/Dialog/DialogViewController.swift delete mode 100644 GDproject/Controller/Messages/DialogViewController.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index 13af48f..6487e58 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 1261BB93227B364C003898CF /* ChannelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB92227B364C003898CF /* ChannelViewController.swift */; }; 1261BB95227B3991003898CF /* AddToChannelVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB94227B3991003898CF /* AddToChannelVC.swift */; }; 1261BB9E227B793D003898CF /* ChatInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1261BB9D227B793D003898CF /* ChatInfoViewController.swift */; }; + 12758257227F1401001F291F /* DialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12758256227F1401001F291F /* DialogViewController.swift */; }; 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1288B5CD221F1158002BE6B1 /* DataStorage.swift */; }; 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; }; 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; }; @@ -26,8 +27,8 @@ 129320042279B4270035C7B3 /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320032279B4270035C7B3 /* MessagesViewController.swift */; }; 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320062279B5690035C7B3 /* MessagesCoordinator.swift */; }; 129320092279C3B50035C7B3 /* PeopleToWriteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */; }; - 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1293200A2279D02D0035C7B3 /* DialogViewController.swift */; }; 1293200D2279D7310035C7B3 /* CompletionTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1293200C2279D7310035C7B3 /* CompletionTree.swift */; }; + 1298810B227F13840032ACA3 /* DialogCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1298810A227F13840032ACA3 /* DialogCell.swift */; }; 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */; }; 12BA4B9D224101E700DF93D7 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */; }; 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */; }; @@ -113,6 +114,7 @@ 1261BB92227B364C003898CF /* ChannelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelViewController.swift; sourceTree = ""; }; 1261BB94227B3991003898CF /* AddToChannelVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToChannelVC.swift; sourceTree = ""; }; 1261BB9D227B793D003898CF /* ChatInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoViewController.swift; sourceTree = ""; }; + 12758256227F1401001F291F /* DialogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DialogViewController.swift; sourceTree = ""; }; 1288B5CD221F1158002BE6B1 /* DataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStorage.swift; sourceTree = ""; }; 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = ""; }; 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = ""; }; @@ -120,8 +122,8 @@ 129320032279B4270035C7B3 /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; 129320062279B5690035C7B3 /* MessagesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesCoordinator.swift; sourceTree = ""; }; 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PeopleToWriteViewController.swift; path = GDproject/Controller/Messages/PeopleToWriteViewController.swift; sourceTree = SOURCE_ROOT; }; - 1293200A2279D02D0035C7B3 /* DialogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DialogViewController.swift; path = GDproject/Controller/Messages/DialogViewController.swift; sourceTree = SOURCE_ROOT; }; 1293200C2279D7310035C7B3 /* CompletionTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionTree.swift; sourceTree = ""; }; + 1298810A227F13840032ACA3 /* DialogCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialogCell.swift; sourceTree = ""; }; 12BA4B9A224101A400DF93D7 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; 12BA4B9C224101E700DF93D7 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; }; 12BA4B9E224102B700DF93D7 /* LogInCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator.swift; sourceTree = ""; }; @@ -225,12 +227,21 @@ children = ( 1261BB9D227B793D003898CF /* ChatInfoViewController.swift */, 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */, - 1293200A2279D02D0035C7B3 /* DialogViewController.swift */, + 1298810C227F138C0032ACA3 /* Dialog */, 129320032279B4270035C7B3 /* MessagesViewController.swift */, ); path = Messages; sourceTree = ""; }; + 1298810C227F138C0032ACA3 /* Dialog */ = { + isa = PBXGroup; + children = ( + 12758256227F1401001F291F /* DialogViewController.swift */, + 1298810A227F13840032ACA3 /* DialogCell.swift */, + ); + path = Dialog; + sourceTree = ""; + }; 12E36D8F221424EA006FCDD7 = { isa = PBXGroup; children = ( @@ -425,10 +436,10 @@ 12DB7FDF221877160096878E /* InviteViewController.swift in Sources */, 121A8972226B1EAC005EE599 /* TagsSuggestionController.swift in Sources */, 1293200D2279D7310035C7B3 /* CompletionTree.swift in Sources */, - 1293200B2279D02D0035C7B3 /* DialogViewController.swift in Sources */, 12E36DCA22144635006FCDD7 /* NewPostViewController.swift in Sources */, 1261BB9E227B793D003898CF /* ChatInfoViewController.swift in Sources */, 12E36D9E221424EA006FCDD7 /* NewsController.swift in Sources */, + 1298810B227F13840032ACA3 /* DialogCell.swift in Sources */, 12E36DCC22144725006FCDD7 /* PostViewCell.swift in Sources */, 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */, 125BD57F22171D73008A3575 /* Extentions.swift in Sources */, @@ -439,6 +450,7 @@ 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */, 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */, 129320042279B4270035C7B3 /* MessagesViewController.swift in Sources */, + 12758257227F1401001F291F /* DialogViewController.swift in Sources */, 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */, 12BA4B9F224102B700DF93D7 /* LogInCoordinator.swift in Sources */, 12F3D6B22241097B00A69214 /* ProfileCoordinator.swift in Sources */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index e079894..b4207de 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -619,12 +619,28 @@ - + + + + + @@ -641,84 +657,31 @@ - - + + - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/GDproject/Constants.swift b/GDproject/Constants.swift index f2a56b7..e638202 100644 --- a/GDproject/Constants.swift +++ b/GDproject/Constants.swift @@ -65,5 +65,7 @@ let dialogViewController = "DialogViewController" let tagsSuggestionController = "TagsSuggestionController" +let chatInfoViewController = "ChatInfoViewController" + let blueSystemColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5) diff --git a/GDproject/Controller/ News and channels/ChannelViewController.swift b/GDproject/Controller/ News and channels/ChannelViewController.swift index 6063bd7..dfc5667 100644 --- a/GDproject/Controller/ News and channels/ChannelViewController.swift +++ b/GDproject/Controller/ News and channels/ChannelViewController.swift @@ -53,7 +53,7 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan super.viewWillAppear(animated) tableView.reloadData() } - // TODO: update channel + override func viewWillDisappear(_ animated: Bool) { defer { @@ -147,7 +147,8 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == UITableViewCell.EditingStyle.delete { + if editingStyle == UITableViewCell.EditingStyle.delete + { if indexPath.section == 1 { channel?.people.remove(at: indexPath.row-1) } else { diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index 0658bf8..1168feb 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -53,6 +53,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi updater.delegate = self searchController = UISearchController(searchResultsController: updater) + navigationController?.navigationBar.prefersLargeTitles = true navigationItem.title = "Loading ..." tableView.refreshControl = refreshContr // Configure Refresh Control diff --git a/GDproject/Controller/ News and channels/PostViewCell.swift b/GDproject/Controller/ News and channels/PostViewCell.swift index 7dae0c8..505532e 100644 --- a/GDproject/Controller/ News and channels/PostViewCell.swift +++ b/GDproject/Controller/ News and channels/PostViewCell.swift @@ -8,7 +8,6 @@ import UIKit import Cartography -import MarkdownKit import TinyConstraints class PostViewCell: UITableViewCell @@ -182,7 +181,9 @@ class PostViewCell: UITableViewCell } @objc func displayProfile(){ - onUserDisplay?(post!.authorId) + if let id = post?.authorId { + onUserDisplay?(id) + } } @objc func setAnonymousChannel(on button: UIButton){ diff --git a/GDproject/Controller/Coordinators/MessagesCoordinator.swift b/GDproject/Controller/Coordinators/MessagesCoordinator.swift index 20808bc..6eecd3b 100644 --- a/GDproject/Controller/Coordinators/MessagesCoordinator.swift +++ b/GDproject/Controller/Coordinators/MessagesCoordinator.swift @@ -32,14 +32,23 @@ class MessagesCoordinator: BaseCoordinator { vc.onUserDisplayList = { [weak vc, unowned self] in let newVC = self.storyboard.instantiateViewController(withIdentifier: peopleToWriteVC) as! PeopleToWriteViewController + +// newVC.whatToDoWithSelection = { +// +// } + vc?.navigationController?.pushViewController(newVC, animated: true) } - vc.onDialogDisplay = { [weak vc, unowned self] _ in + vc.onDialogDisplay = { [weak vc, unowned self] in let newVC = self.storyboard.instantiateViewController(withIdentifier: dialogVC) as! DialogViewController - //newVC.currentDialog = $0 + newVC.dialog = $0.dialog + newVC.users = $0.users vc?.navigationController?.pushViewController(newVC, animated: true) } + + + navigationController?.viewControllers = [vc] } } diff --git a/GDproject/Controller/Messages/ChatInfoViewController.swift b/GDproject/Controller/Messages/ChatInfoViewController.swift index d7a9c4f..c3a88d7 100644 --- a/GDproject/Controller/Messages/ChatInfoViewController.swift +++ b/GDproject/Controller/Messages/ChatInfoViewController.swift @@ -8,87 +8,233 @@ import UIKit - /// Class for displaying chat info /// class ChatInfoViewController: UITableViewController { + enum PersonStatus{ + case admin + case ordinary + } + + weak var delegate: UpdatableGroup? + + var groupChat: Model.Group? { + didSet{ + if let groupChat = groupChat { + usersArray = groupChat.users.map { $0.key } + } + } + } + + var usersArray: [Int] = [] { + didSet { + tableView.reloadData() + } + } + + var users: [Int: Model.Users] = [:] + + func canIEditThisChat() -> PersonStatus + { + let myId = UserDefaults.standard.integer(forKey: UserDefaultsKeys.id.rawValue) + + if let groupChatUserPermission = groupChat?.users[myId]?.isAdmin { + return groupChatUserPermission ? .admin : .ordinary + } + + return .ordinary + } + + var myPermissions: PersonStatus = .ordinary + + override func viewDidLoad() { super.viewDidLoad() - // Uncomment the following line to preserve selection between presentations - // self.clearsSelectionOnViewWillAppear = false - - // Uncomment the following line to display an Edit button in the navigation bar for this view controller. - // self.navigationItem.rightBarButtonItem = self.editButtonItem + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + myPermissions = canIEditThisChat() + tableView.reloadData() + + switch myPermissions { + case .admin: + navigationItem.rightBarButtonItem = self.editButtonItem + default: + return + } } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { - // #warning Incomplete implementation, return the number of sections - return 0 + return 3 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - // #warning Incomplete implementation, return the number of rows - return 0 + switch section { + case 0: + return 1 + case 1: + return 1 + default: + return usersArray.count + (myPermissions == .ordinary ? 0 : 1) + } } - /* - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) - // Configure the cell... + if indexPath.row == 0 { + switch indexPath.section { + case 0: + cell.textLabel?.text = groupChat!.name + return cell + case 1: + cell.textLabel?.text = "Leave chat" + cell.textLabel?.textColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1) + return cell + default: + switch myPermissions{ + case .admin: + cell.textLabel?.text = "Add participants" + cell.accessoryType = .disclosureIndicator + case .ordinary: + cell.textLabel?.text = name(for: users[usersArray[indexPath.row]]) + } + return cell + } + } + + switch myPermissions{ + case .admin: + cell.textLabel?.text = name(for: users[usersArray[indexPath.row-1]]) + case .ordinary: + cell.textLabel?.text = name(for: users[usersArray[indexPath.row]]) + } return cell } - */ + + private func name(for user: Model.Users?) -> String { + return "👤 \(user!.fullName())" + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? + { + switch section { + case 0: + return "Title" + case 1: + return "Opportunities" + default: + return "Participants" + } + } + - /* - // Override to support conditional editing of the table view. + func editName(for cell: UITableViewCell){ + let alert = UIAlertController(title: "Вы уверены?", message: "Название:", preferredStyle: .alert) + + let action1 = UIAlertAction(title: "Oтмена", style: .cancel, handler: nil) + + alert.addTextField { [weak self] (tf) in + tf.textColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) + tf.text = self?.groupChat?.name + } + + let action2 = UIAlertAction(title: "OK", style: .default) { [unowned self] _ in + let name = alert.textFields?.first?.text + cell.textLabel?.text = name + self.groupChat?.name = name! + Model.updateGroupChat(with: self.groupChat!) + self.delegate?.updateGroup(with: self.groupChat!) + } + + alert.addAction(action1) + alert.addAction(action2) + + present(alert, animated: true, completion: nil) + } + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) + { + guard let groupChat = groupChat else { + return + } + + guard let cell = tableView.cellForRow(at: indexPath) else { + return + } + + if indexPath.row == 0 { + switch indexPath.section{ + case 0: + switch myPermissions{ + case .admin: + editName(for: cell) + default: + break + } + case 1: + Model.leaveGroupChat(id: groupChat.id) { + [weak self] in + self?.navigationController?.popViewController(animated: true) + } + default: + showUserChoiceVC() + } + } + } + + func showUserChoiceVC() + { + let vc = storyboard?.instantiateViewController(withIdentifier: peopleToWriteVC) as! PeopleToWriteViewController + + vc.whatToDoWithSelection = { [weak self, weak vc] mapa in + + mapa.forEach { self?.users[$0.key] = Model.Channels.fullPeopleDict[$0.key] } + mapa.forEach { self?.groupChat?.users[$0.key] = $0.value } + vc?.navigationController?.popViewController(animated: true) + } + + navigationController?.pushViewController(vc, animated: true) + } + + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) + { + if editingStyle == UITableViewCell.EditingStyle.delete { + groupChat?.users[usersArray[indexPath.row-1]] = nil + } + + tableView.reloadData() + } + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the specified item to be editable. + if indexPath.row == 0 { + return false + } + return true } - */ - - /* - // Override to support editing the table view. - override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == .delete { - // Delete the row from the data source - tableView.deleteRows(at: [indexPath], with: .fade) - } else if editingStyle == .insert { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } + + /// update chatInfo + override func viewWillDisappear(_ animated: Bool) { + + defer { + super.viewWillDisappear(animated) + } + + guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else { + switch myPermissions { + case .ordinary: + return + case .admin: + Model.updateGroupChat(with: groupChat!) + delegate?.updateGroup(with: groupChat!) + } + return + } } - */ - - /* - // Override to support rearranging the table view. - override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { - - } - */ - - /* - // Override to support conditional rearranging of the table view. - override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the item to be re-orderable. - return true - } - */ - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. - } - */ - } diff --git a/GDproject/Controller/Messages/Dialog/DialogCell.swift b/GDproject/Controller/Messages/Dialog/DialogCell.swift new file mode 100644 index 0000000..6436854 --- /dev/null +++ b/GDproject/Controller/Messages/Dialog/DialogCell.swift @@ -0,0 +1,93 @@ +// +// DialogCell.swift +// GDproject +// +// Created by cstore on 05/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit +import TinyConstraints +import MarkdownKit + +class DialogCell: UITableViewCell { + + let nameLabel: UIButton = { + let button = UIButton() + button.setTitleColor(.black, for: .normal) + button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16) + button.addTarget(self, action: #selector(displayProfile), for: .touchUpInside) + button.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left + return button + }() + + @objc func displayProfile() + { + if let id = self.user?.id{ + onUserDisplay?(id) + } + } + var onUserDisplay: ((Int)->())? + + let fullNameLabel: UILabel = { + let label = UILabel() + label.textColor = #colorLiteral(red: 0.3333333433, green: 0.3333333433, blue: 0.3333333433, alpha: 1) + label.text = "Богомазова Вероника Львовна" + label.font = UIFont.systemFont(ofSize: 12) + return label + }() + + let timeLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 12) + label.textColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1) + return label + }() + + func createTextView(with text: NSAttributedString, _ isSelectable: Bool) -> UITextView + { + let textView = UITextView() + textView.isScrollEnabled = false + textView.isEditable = false + + if isSelectable { + textView.isSelectable = true + } else { + textView.isUserInteractionEnabled = false + } + + textView.attributedText = text + return textView + } + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) + { + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + required init?(coder aDecoder: NSCoder) + { + fatalError("init(coder:) has not been implemented") + } + + var user: Model.Users? { + didSet{ + nameLabel.setTitle(user!.fullName(), for: .normal) + } + } + + func fill(with markdownText: NSAttributedString, byUser: Model.Users) + { + // important + contentView.subviews.forEach({ $0.removeFromSuperview() }) + + self.user = byUser + let textView = createTextView(with: markdownText, true) + + self.contentView.addSubview(nameLabel) + self.contentView.addSubview(textView) + nameLabel.edgesToSuperview(excluding: .bottom, insets: .top(8) + .left(8)) + textView.edgesToSuperview(excluding: .top, insets: .right(8) + .left(8) + .bottom(8)) + textView.topToBottom(of: nameLabel) + } +} diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift new file mode 100644 index 0000000..54f8ea8 --- /dev/null +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -0,0 +1,145 @@ +// +// DialogViewController.swift +// GDproject +// +// Created by cstore on 01/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +protocol UpdatableGroup:class { + func updateGroup(with group: Model.Group) +} + +class DialogViewController: UITableViewController, UpdatableGroup { + + func updateGroup(with group: Model.Group){ + self.groupChat?.group = group + setTitleForGroup(groupChat: groupChat!) + } + + let cellId = "cell3" + var onInfoShow: (()->())? + + var dialog: Model.Dialog? { + didSet{ + switch dialog! { + case .groupChat(let chat): + self.groupChat = chat + case .userChat(let chat): + self.userChat = chat + } + } + } + + var groupChat: Model.GroupChat? + var userChat: Model.UserChat? + + var users: [Int: Model.Users]? + + var groupId: Int? + + var cellData: [PostCellData] = [] { + didSet { + print(cellData) + tableView.reloadData() + } + } + + override func viewDidLoad() { + super.viewDidLoad() + + tabBarController?.tabBar.isHidden = true + navigationItem.largeTitleDisplayMode = .never + + tableView.register(DialogCell.self, forCellReuseIdentifier: cellId) + tableView.transform = CGAffineTransform(scaleX: 1, y: -1) + tableView.contentInsetAdjustmentBehavior = .never + tableView.contentOffset = CGPoint(x: 0, y: -30) + + if let groupChat = groupChat { + setTitleForGroup(groupChat: groupChat) + } else if let userChat = userChat{ + setTitleForChat(userChat: userChat) + } + } + + func setTitleForChat(userChat: Model.UserChat){ + navigationItem.title = "🌌 \(users![userChat.user]!.fullName())" + } + + func setTitleForGroup(groupChat: Model.GroupChat){ + navigationItem.title = "🌌 \(groupChat.group.id) \(groupChat.group.name)" + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Info", style: .plain, target: self, action: #selector(moveToInfoVC)) + } + + @objc func moveToInfoVC(){ + let vc = storyboard?.instantiateViewController(withIdentifier: chatInfoViewController) as! ChatInfoViewController + + vc.delegate = self + vc.users = users! + + if let groupChat = groupChat { + vc.groupChat = groupChat.group + } + + navigationController?.pushViewController(vc, animated: true) + } + + var currentMessagesInChat: [Model.LastMessage]? { + didSet { + if let currentMessagesInChat = currentMessagesInChat { + cellData = currentMessagesInChat.map { PostCellData(attributedData: PostCellData.create(with: [$0.body])) } + } + } + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return cellData.count + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + if let dialog = dialog { + getMessages(for: dialog) + } + } + + func getMessages(for dialog: Model.Dialog){ + switch dialog { + case .groupChat(let groupChat): + Model.getMessagesFor(typeOfChat: Model.Dialog.groupChat(groupChat), chat: groupChat.group.id) + { [unowned self] in + self.currentMessagesInChat = $0 + } + case .userChat(let userChat): + Model.getMessagesFor(typeOfChat: Model.Dialog.userChat(userChat), chat: userChat.user) + { [unowned self] in + self.currentMessagesInChat = $0 + } + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! DialogCell + + //In cellForRowAtIndexPath + cell.transform = CGAffineTransform(scaleX: 1, y: -1) + + if let author = currentMessagesInChat?[indexPath.row].author, let user = users?[author] + { + cell.fill(with: cellData[indexPath.row].attributedData, byUser: user) + } + + return cell + } +} diff --git a/GDproject/Controller/Messages/DialogViewController.swift b/GDproject/Controller/Messages/DialogViewController.swift deleted file mode 100644 index fadfbe2..0000000 --- a/GDproject/Controller/Messages/DialogViewController.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// DialogViewController.swift -// GDproject -// -// Created by cstore on 01/05/2019. -// Copyright © 2019 drHSE. All rights reserved. -// - -import UIKit - -class DialogViewController: UITableViewController { - - var onInfoShow: (()->())? - var group: Model.Group? - - override func viewDidLoad() { - super.viewDidLoad() - - tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") - - if let id = group { - navigationItem.title = "🌌 \(id.id) \(id.name)" - navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Info", style: .plain, target: self, action: #selector(moveToInfoVC)) - } - } - - - @objc func moveToInfoVC(){ - - } - - var currentMessagesInChat: [Model.LastMessage]? { - didSet { - tableView.reloadData() - } - } - - // MARK: - Table view data source - - override func numberOfSections(in tableView: UITableView) -> Int { - return 1 - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return currentMessagesInChat?.count ?? 0 - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - if let group = group { - Model.getMessagesForGroupChat(chat: group.id) { [unowned self] in - self.currentMessagesInChat = $0 - } - } - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell - { - let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) - - cell.detailTextLabel?.text = currentMessagesInChat![indexPath.row].body.markdown - cell.textLabel?.text = "\(currentMessagesInChat![indexPath.row].author)" - - return cell - } -} diff --git a/GDproject/Controller/Messages/MessagesViewController.swift b/GDproject/Controller/Messages/MessagesViewController.swift index 285895c..93b2ea3 100644 --- a/GDproject/Controller/Messages/MessagesViewController.swift +++ b/GDproject/Controller/Messages/MessagesViewController.swift @@ -12,14 +12,17 @@ class MessagesViewController: UITableViewController { // curreent Active which can be displayed var currentActiveDialogs: [Model.Dialog] = [] { - didSet{ + didSet { tableView.reloadData() } } + // curreent users + var users: [Int: Model.Users] = [:] + var onUserDisplayList: (()->())? - var onDialogDisplay: ((Model.Dialog)->())? + var onDialogDisplay: (((dialog: Model.Dialog, users: [Int:Model.Users]))->())? let searchC = UISearchController(searchResultsController: nil) @@ -42,9 +45,10 @@ class MessagesViewController: UITableViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + tabBarController?.tabBar.isHidden = false Model.getChatAll { [weak self] in - self?.currentActiveDialogs = $0 + self?.currentActiveDialogs = $0.0 + self?.users = $0.1 } } @@ -67,7 +71,7 @@ class MessagesViewController: UITableViewController { cell.textLabel?.text = group.group.name cell.detailTextLabel?.text = group.lastMessage.body.markdown case .userChat(let userChat): - cell.textLabel?.text = "\(userChat.user)" + cell.textLabel?.text = "\(users[userChat.user]!)" cell.detailTextLabel?.text = userChat.lastMessage.body.markdown } @@ -75,6 +79,7 @@ class MessagesViewController: UITableViewController { } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - onDialogDisplay?(currentActiveDialogs[indexPath.row]) + let tuple = (currentActiveDialogs[indexPath.row],users) + onDialogDisplay?(tuple) } } diff --git a/GDproject/Controller/Messages/PeopleToWriteViewController.swift b/GDproject/Controller/Messages/PeopleToWriteViewController.swift index 7f18c59..84cf759 100644 --- a/GDproject/Controller/Messages/PeopleToWriteViewController.swift +++ b/GDproject/Controller/Messages/PeopleToWriteViewController.swift @@ -10,7 +10,8 @@ import UIKit class PeopleToWriteViewController: UITableViewController { - // TODO: - edit button when it's used for selection + // TODO: - edit button when it's used for selection + var whatToDoWithSelection: (([Int: Model.UserPermission])->())? let searchC = UISearchController(searchResultsController: nil) @@ -23,7 +24,9 @@ class PeopleToWriteViewController: UITableViewController { tableView.isEditing = true self.navigationItem.title = "People" - self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(newMessage)) + + self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(newMessage)) + self.navigationItem.largeTitleDisplayMode = .never self.navigationItem.searchController = searchC self.navigationItem.hidesSearchBarWhenScrolling = false @@ -32,15 +35,12 @@ class PeopleToWriteViewController: UITableViewController { override func setEditing(_ editing: Bool, animated: Bool) { super.setEditing(true, animated: animated) } - // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { - // #warning Incomplete implementation, return the number of sections return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - // #warning Incomplete implementation, return the number of rows return users.count } @@ -62,15 +62,7 @@ class PeopleToWriteViewController: UITableViewController { { if chosenUsers.count != 0 { - navigationController?.popViewController(animated: false) - var group = Model.Group(users: chosenUsers, name: "Untitled", id: 0) - - Model.createGroupChat(from: group) { [unowned self] (id) in - let vc = self.storyboard?.instantiateViewController(withIdentifier: dialogViewController) as! DialogViewController - group.id = id - vc.group = group - self.navigationController?.pushViewController(vc, animated: true) - } + whatToDoWithSelection?(chosenUsers) } } } diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index cddd1e0..6b9999d 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -40,7 +40,7 @@ class Model{ static let channelsDeleteURL = URL(string: "\(baseUrl)/channels/delete")! static let channelsGetAnonURL = URL(string: "\(baseUrl)/channels/getAnonymous")! static let complexURL = URL(string: "\(baseUrl)/complex")! - static let hashTagTreeURL = URL(string: "\(baseUrl)/tagCompletions")! + static let hashTagTreeURL = URL(string: "\(baseUrl)/tags/completions")! static let createGroupChatURL = URL(string: "\(baseUrl)/chats/createGroupChat")! static let chatsGetAllURL = URL(string: "\(baseUrl)/chats/getAll")! static let getGroupChatURL = URL(string: "\(baseUrl)/chats/getGroupChat")! @@ -48,7 +48,7 @@ class Model{ static let updateGroupChatURL = URL(string: "\(baseUrl)/chats/updateGroupChat")! static let messagesGetGroupChatURL = URL(string: "\(baseUrl)/messages/get/groupChat")! //r static let messagesSendURL = URL(string: "\(baseUrl)/messages/send")! - static let messagesUserChatURL = URL(string: "\(baseUrl)/messages/userChat")! + static let messagesGetUserChatURL = URL(string: "\(baseUrl)/messages/get/userChat")! struct QueryPosts: Codable { @@ -559,7 +559,7 @@ class Model{ } } - static func getChatAll(limit: Int = 10, exclusiveFrom: Int? = nil, request: [Int] = [], completion: @escaping (([Dialog])->())) + static func getChatAll(limit: Int = 10, exclusiveFrom: Int? = nil, request: [Int] = [], completion: @escaping ((([Dialog],[Int:Model.Users]))->())) { let req = GeneralRequest<[Int]>(limit: limit, exclusiveFrom: exclusiveFrom, request: request) var request = URLRequest(url: chatsGetAllURL) @@ -573,8 +573,7 @@ class Model{ guard let json = response.data else { return } let dialogs = try! decoder.decode(QueryPosts.self, from: json) - print(dialogs.response) - completion(dialogs.response) + completion((dialogs.response,dialogs.users)) } } @@ -624,6 +623,12 @@ class Model{ var users: [Int: UserPermission] var name: String var id: Int + + init(users: [Int: UserPermission] = [:], name: String = "", id: Int) { + self.id = id + self.users = users + self.name = name + } } struct UserPermission: Codable { @@ -734,16 +739,23 @@ class Model{ } } - static func getMessagesForGroupChat(chat id: Int, exclusiveFrom: Int? = nil, limit l: Int = 10, direction: String = "backward", completion: @escaping (([LastMessage])->())) + static func getMessagesFor(typeOfChat: Model.Dialog, chat id: Int, exclusiveFrom: Int? = nil, limit l: Int = 10, direction: String = "backward", completion: @escaping (([LastMessage])->())) { let req = GeneralRequest(direction: direction, limit: l, exclusiveFrom: exclusiveFrom, request: id) + var request: URLRequest? - var request = URLRequest(url: messagesGetGroupChatURL) - request.httpMethod = "POST" - request.httpBody = try? JSONEncoder().encode(req) - request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + switch typeOfChat { + case .groupChat: + request = URLRequest(url: messagesGetGroupChatURL) + case .userChat: + request = URLRequest(url: messagesGetUserChatURL) + } - AF.request(request).response { (response) in + request!.httpMethod = "POST" + request!.httpBody = try? JSONEncoder().encode(req) + request!.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + + AF.request(request!).response { (response) in isValidTocken?(response.response?.statusCode ?? 498) @@ -753,4 +765,32 @@ class Model{ completion(messages) } } + + static func leaveGroupChat(id: Int, completion: @escaping (()->())) + { + var request = URLRequest(url: leaveGroupChatURL) + request.httpMethod = "POST" + request.httpBody = "\(id)".data(using: .utf8) + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + + AF.request(request).response { (response) in + isValidTocken?(response.response?.statusCode ?? 498) + } + + completion() + } + + static func updateGroupChat(with group: Model.Group) + { + var request = URLRequest(url: updateGroupChatURL) + request.httpMethod = "POST" + request.httpBody = try? JSONEncoder().encode(group) + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + + AF.request(request).response { (response) in + isValidTocken?(response.response?.statusCode ?? 498) + } + + // completion() + } } From 4acfa6e9aafabb6bfd76281869c8136191d5fa98 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Mon, 6 May 2019 18:54:52 +0300 Subject: [PATCH 20/34] Added ability to write and send message to chats and groups. Full UI is waiting for me. Can give admins. Chat info is completed. --- GDproject/Base.lproj/Main.storyboard | 44 ------- .../Coordinators/MessagesCoordinator.swift | 6 +- .../Messages/ChatInfoViewController.swift | 44 ++++++- .../Dialog/DialogViewController.swift | 114 +++++++++++++++++- .../Messages/MessagesViewController.swift | 3 +- .../Profile/ProfileViewController.swift | 2 +- GDproject/Simple model/Model.swift | 53 +++++++- 7 files changed, 209 insertions(+), 57 deletions(-) diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index b4207de..2f3d480 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -610,50 +610,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/GDproject/Controller/Coordinators/MessagesCoordinator.swift b/GDproject/Controller/Coordinators/MessagesCoordinator.swift index 6eecd3b..7d0eecb 100644 --- a/GDproject/Controller/Coordinators/MessagesCoordinator.swift +++ b/GDproject/Controller/Coordinators/MessagesCoordinator.swift @@ -40,15 +40,13 @@ class MessagesCoordinator: BaseCoordinator { vc?.navigationController?.pushViewController(newVC, animated: true) } - vc.onDialogDisplay = { [weak vc, unowned self] in - let newVC = self.storyboard.instantiateViewController(withIdentifier: dialogVC) as! DialogViewController + vc.onDialogDisplay = { [weak vc] in + let newVC = DialogViewController() newVC.dialog = $0.dialog newVC.users = $0.users vc?.navigationController?.pushViewController(newVC, animated: true) } - - navigationController?.viewControllers = [vc] } } diff --git a/GDproject/Controller/Messages/ChatInfoViewController.swift b/GDproject/Controller/Messages/ChatInfoViewController.swift index c3a88d7..80edd77 100644 --- a/GDproject/Controller/Messages/ChatInfoViewController.swift +++ b/GDproject/Controller/Messages/ChatInfoViewController.swift @@ -119,7 +119,14 @@ class ChatInfoViewController: UITableViewController { } private func name(for user: Model.Users?) -> String { - return "👤 \(user!.fullName())" + if let user = user, let perm = groupChat?.users[user.id]?.isAdmin { + if perm { + return "🤴🏻 \(user.fullName())" + } + return "👤 \(user.fullName())" + } + + return "left" } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? @@ -219,6 +226,41 @@ class ChatInfoViewController: UITableViewController { return true } + override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? + { + let editButton = UITableViewRowAction(style: .normal, title: "Promote") { [unowned self] (rowAction, indexPath) in + + self.tableView.beginUpdates() + self.groupChat?.users[self.usersArray[indexPath.row-1]]?.isAdmin = true + self.tableView.reloadRows(at: [indexPath], with: .none) + self.tableView.endUpdates() + } + + editButton.backgroundColor = #colorLiteral(red: 0.476841867, green: 0.5048075914, blue: 1, alpha: 1) + + let restrictButton = UITableViewRowAction(style: .normal, title: "Restrict") { [unowned self] (rowAction, indexPath) in + + self.tableView.beginUpdates() + self.groupChat?.users[self.usersArray[indexPath.row-1]]?.isAdmin = false + self.tableView.reloadRows(at: [indexPath], with: .none) + self.tableView.endUpdates() + } + + restrictButton.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) + + let deleteButton = UITableViewRowAction(style: .normal, title: "Delete") { [unowned self] (action, indexPath) in + + self.tableView.beginUpdates() + self.groupChat?.users[self.usersArray[indexPath.row-1]] = nil + self.tableView.deleteRows(at: [indexPath], with: .none) + self.tableView.endUpdates() + } + + deleteButton.backgroundColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1) + + return [editButton, restrictButton, deleteButton] + } + /// update chatInfo override func viewWillDisappear(_ animated: Bool) { diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift index 54f8ea8..b4ab918 100644 --- a/GDproject/Controller/Messages/Dialog/DialogViewController.swift +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -7,12 +7,16 @@ // import UIKit +import TinyConstraints protocol UpdatableGroup:class { func updateGroup(with group: Model.Group) } -class DialogViewController: UITableViewController, UpdatableGroup { +class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegate, UITableViewDataSource +{ + + var tableView: UITableView = UITableView() func updateGroup(with group: Model.Group){ self.groupChat?.group = group @@ -49,6 +53,12 @@ class DialogViewController: UITableViewController, UpdatableGroup { override func viewDidLoad() { super.viewDidLoad() + + setConstraints() + magicForKeyboardChanges() + + self.tableView.delegate = self + self.tableView.dataSource = self tabBarController?.tabBar.isHidden = true navigationItem.largeTitleDisplayMode = .never @@ -65,6 +75,97 @@ class DialogViewController: UITableViewController, UpdatableGroup { } } + var messageSendView: UIView = { + let view = UIView() + view.backgroundColor = .red + return view + }() + + var messageTextView: UITextView = { + let textView = UITextView() + textView.isEditable = true + textView.isScrollEnabled = true + textView.backgroundColor = .green + return textView + }() + + var sendButton: UIButton = { + let button = UIButton() + button.setTitle("Send", for: .normal) + button.setTitleColor(.blue, for: .normal) + button.addTarget(self, action: #selector(sendMessage), for: .touchUpInside) + return button + }() + + @objc func sendMessage() + { + var destination: Model.MessageDestination? + + if let group = groupChat{ + destination = Model.MessageDestination.groupChatDestination(group.group.id) + } else if let user = userChat { + destination = Model.MessageDestination.userChatDestination(user.user) + } + + if let destination = destination + { + Model.sendMessage(message: Model.SendMessage(body: Model.Attachments(markdown: messageTextView.text), destination: destination)) { [unowned self] in + self.getMessages(for: self.dialog!) + } + } + } + + var bottomConstraint: NSLayoutConstraint! + + func setConstraints(){ + self.view.addSubview(tableView) + self.view.addSubview(messageSendView) + + messageSendView.addSubview(messageTextView) + messageSendView.addSubview(sendButton) + + messageSendView.edgesToSuperview(excluding: [.top,.bottom]) + + bottomConstraint = NSLayoutConstraint(item: messageSendView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0) + view.addConstraint(bottomConstraint) + + messageSendView.height(40) + + sendButton.edgesToSuperview(excluding: .left) + sendButton.width(60) + + messageTextView.edgesToSuperview(excluding: .right) + messageTextView.rightToLeft(of: sendButton) + + tableView.edgesToSuperview(excluding: .bottom) + tableView.bottomToTop(of: messageSendView) + + self.view.layoutSubviews() + } + + func magicForKeyboardChanges() + { + NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillShowNotification, object: nil) + + NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillHideNotification, object: nil) + } + + @objc func handleKeyboardNotifications(notification: NSNotification){ + if let userInfo = notification.userInfo{ + // UIKeyboardFrameEndUserInfoKey + + let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue + + bottomConstraint.constant = notification.name == UIResponder.keyboardWillShowNotification ? -(keyBoardFrame.height) : 0 + + UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: { + self.view.layoutIfNeeded() + }) { (completed) in + + } + } + } + func setTitleForChat(userChat: Model.UserChat){ navigationItem.title = "🌌 \(users![userChat.user]!.fullName())" } @@ -75,7 +176,7 @@ class DialogViewController: UITableViewController, UpdatableGroup { } @objc func moveToInfoVC(){ - let vc = storyboard?.instantiateViewController(withIdentifier: chatInfoViewController) as! ChatInfoViewController + let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: chatInfoViewController) as! ChatInfoViewController vc.delegate = self vc.users = users! @@ -97,11 +198,11 @@ class DialogViewController: UITableViewController, UpdatableGroup { // MARK: - Table view data source - override func numberOfSections(in tableView: UITableView) -> Int { + func numberOfSections(in tableView: UITableView) -> Int { return 1 } - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return cellData.count } @@ -128,7 +229,7 @@ class DialogViewController: UITableViewController, UpdatableGroup { } } - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! DialogCell @@ -143,3 +244,6 @@ class DialogViewController: UITableViewController, UpdatableGroup { return cell } } + + + diff --git a/GDproject/Controller/Messages/MessagesViewController.swift b/GDproject/Controller/Messages/MessagesViewController.swift index 93b2ea3..dc52acf 100644 --- a/GDproject/Controller/Messages/MessagesViewController.swift +++ b/GDproject/Controller/Messages/MessagesViewController.swift @@ -67,11 +67,12 @@ class MessagesViewController: UITableViewController { let cell = tableView.dequeueReusableCell(withIdentifier: "MessagesCell", for: indexPath) switch currentActiveDialogs[indexPath.row].self { + case .groupChat(let group): cell.textLabel?.text = group.group.name cell.detailTextLabel?.text = group.lastMessage.body.markdown case .userChat(let userChat): - cell.textLabel?.text = "\(users[userChat.user]!)" + cell.textLabel?.text = "👤 \(users[userChat.user]!.fullName())" cell.detailTextLabel?.text = userChat.lastMessage.body.markdown } diff --git a/GDproject/Controller/Profile/ProfileViewController.swift b/GDproject/Controller/Profile/ProfileViewController.swift index d930c41..b2db09b 100644 --- a/GDproject/Controller/Profile/ProfileViewController.swift +++ b/GDproject/Controller/Profile/ProfileViewController.swift @@ -90,7 +90,7 @@ class ProfileViewController: UIViewController tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId) tableView.register(BasicInfoCell.self, forCellReuseIdentifier: basicInfoCellId) - + tableView.register(InfoCell.self, forCellReuseIdentifier: infoCellId) posts.viewController = self diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 6b9999d..fb9baed 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -642,7 +642,7 @@ class Model{ var author: Int var id: Int - private enum MessageCodingKeys: CodingKey{ + enum MessageCodingKeys: CodingKey{ case user case group case body @@ -793,4 +793,55 @@ class Model{ // completion() } + + struct SendMessage: Codable { + var body: Attachments + var destination: MessageDestination + + init(from decoder: Decoder) throws + { + let container = try decoder.container(keyedBy: LastMessage.MessageCodingKeys.self) + body = try container.decode(Attachments.self, forKey: .body) + + if container.contains(.user){ + destination = MessageDestination.userChatDestination(try Int(from: container.superDecoder(forKey: .user))) + } else if container.contains(.group){ + destination = MessageDestination.groupChatDestination(try Int(from: container.superDecoder(forKey: .group))) + } else { + throw DecodingError.keyNotFound(LastMessage.MessageCodingKeys.group, DecodingError.Context(codingPath: container.codingPath, debugDescription: "hz gr1")) + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: LastMessage.MessageCodingKeys.self) + try container.encode(body, forKey: .body) + + switch destination { + case .userChatDestination(let uId): + try uId.encode(to: container.superEncoder(forKey: .user)) + case .groupChatDestination(let gId): + try gId.encode(to: container.superEncoder(forKey: .group)) + } + } + + init(body: Attachments, destination: MessageDestination) + { + self.body = body + self.destination = destination + } + } + + static func sendMessage(message: SendMessage, completion: @escaping (()->())){ + + var request = URLRequest(url: messagesSendURL) + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + request.httpMethod = "POST" + request.httpBody = try? JSONEncoder().encode(message) + + AF.request(request).response { (response) in + isValidTocken?(response.response?.statusCode ?? 498) + + completion() + } + } } From 0722718f1e9362210a2208d06133a67366b71abe Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Tue, 7 May 2019 23:16:45 +0300 Subject: [PATCH 21/34] Fixed a few bags. Added styling to a chat/gropu chat --- .../ChannelViewController.swift | 3 +- .../ News and channels/NewsVC.swift | 1 + .../Coordinators/MessagesCoordinator.swift | 16 ++++- .../Messages/Dialog/DialogCell.swift | 66 ++++++++++++++----- .../Dialog/DialogViewController.swift | 56 ++++++++++++---- .../Messages/MessagesViewController.swift | 2 +- .../Profile/ProfileViewController.swift | 58 ++++++++-------- GDproject/Extentions.swift | 17 +++++ GDproject/Simple model/Model.swift | 8 ++- 9 files changed, 166 insertions(+), 61 deletions(-) diff --git a/GDproject/Controller/ News and channels/ChannelViewController.swift b/GDproject/Controller/ News and channels/ChannelViewController.swift index dfc5667..c61cb93 100644 --- a/GDproject/Controller/ News and channels/ChannelViewController.swift +++ b/GDproject/Controller/ News and channels/ChannelViewController.swift @@ -72,7 +72,8 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan // nou } - @objc func showPreview(){ + @objc func showPreview() + { if let channel = channel { onShowingPreview?(channel) } diff --git a/GDproject/Controller/ News and channels/NewsVC.swift b/GDproject/Controller/ News and channels/NewsVC.swift index 8080c65..7442640 100644 --- a/GDproject/Controller/ News and channels/NewsVC.swift +++ b/GDproject/Controller/ News and channels/NewsVC.swift @@ -42,6 +42,7 @@ class NewsVC: UIViewController, UITableViewDelegate, UITableViewDataSource { } (viewController as? NewsController)?.tableView.reloadData() + (viewController as? ProfileViewController)?.tableView.reloadData() } } diff --git a/GDproject/Controller/Coordinators/MessagesCoordinator.swift b/GDproject/Controller/Coordinators/MessagesCoordinator.swift index 7d0eecb..46c2dbe 100644 --- a/GDproject/Controller/Coordinators/MessagesCoordinator.swift +++ b/GDproject/Controller/Coordinators/MessagesCoordinator.swift @@ -33,9 +33,19 @@ class MessagesCoordinator: BaseCoordinator { let newVC = self.storyboard.instantiateViewController(withIdentifier: peopleToWriteVC) as! PeopleToWriteViewController -// newVC.whatToDoWithSelection = { -// -// } + newVC.whatToDoWithSelection = { [weak newVC, weak self] in + newVC?.navigationController?.popViewController(animated: true) + + var group = Model.Group(users: $0, name: "Untitled", id: 0) + Model.createGroupChat(from: group, completion: + { [weak self] id in + + let newVC1 = DialogViewController() + group.id = id + newVC1.dialog = Model.Dialog.groupChat(Model.GroupChat(group: group)) + self?.navigationController?.pushViewController(newVC1, animated: true) + }) + } vc?.navigationController?.pushViewController(newVC, animated: true) } diff --git a/GDproject/Controller/Messages/Dialog/DialogCell.swift b/GDproject/Controller/Messages/Dialog/DialogCell.swift index 6436854..8dadb68 100644 --- a/GDproject/Controller/Messages/Dialog/DialogCell.swift +++ b/GDproject/Controller/Messages/Dialog/DialogCell.swift @@ -27,20 +27,15 @@ class DialogCell: UITableViewCell { onUserDisplay?(id) } } - var onUserDisplay: ((Int)->())? - let fullNameLabel: UILabel = { - let label = UILabel() - label.textColor = #colorLiteral(red: 0.3333333433, green: 0.3333333433, blue: 0.3333333433, alpha: 1) - label.text = "Богомазова Вероника Львовна" - label.font = UIFont.systemFont(ofSize: 12) - return label - }() + var onUserDisplay: ((Int)->())? + let timeLabel: UILabel = { let label = UILabel() - label.font = UIFont.systemFont(ofSize: 12) - label.textColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1) + label.font = UIFont.systemFont(ofSize: 9) + label.textAlignment = NSTextAlignment.right + label.textColor = #colorLiteral(red: 0.4352941176, green: 0.4431372549, blue: 0.4745098039, alpha: 1) return label }() @@ -49,6 +44,7 @@ class DialogCell: UITableViewCell { let textView = UITextView() textView.isScrollEnabled = false textView.isEditable = false + textView.sizeToFit() if isSelectable { textView.isSelectable = true @@ -76,18 +72,58 @@ class DialogCell: UITableViewCell { } } - func fill(with markdownText: NSAttributedString, byUser: Model.Users) + var textView: UITextView! + + func fill(with markdownText: NSAttributedString, byUser: Model.Users, when: String) { + let myId = DataStorage.standard.getUserId() // important contentView.subviews.forEach({ $0.removeFromSuperview() }) self.user = byUser - let textView = createTextView(with: markdownText, true) + textView = createTextView(with: markdownText, true) + textView.layer.cornerRadius = 10 + textView.clipsToBounds = true - self.contentView.addSubview(nameLabel) self.contentView.addSubview(textView) - nameLabel.edgesToSuperview(excluding: .bottom, insets: .top(8) + .left(8)) - textView.edgesToSuperview(excluding: .top, insets: .right(8) + .left(8) + .bottom(8)) + self.contentView.addSubview(timeLabel) + + timeLabel.text = when.getDate() + textView.width(min: 50, max: self.contentView.bounds.width-70, priority: .required, isActive: true) + + if myId == byUser.id + { + layoutMyMessage() + } else { + layOutOtherMessage() + } + } + + func layOutOtherMessage(){ + self.contentView.addSubview(nameLabel) + + nameLabel.setTitle(user!.fullName(), for: .normal) + + nameLabel.edgesToSuperview(excluding: .bottom, insets: .top(4) + .left(8)) + textView.edgesToSuperview(excluding: [.top, .right] , insets: .left(8) + .bottom(20)) textView.topToBottom(of: nameLabel) + + textView.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) + textView.textColor = .white + timeLabel.edgesToSuperview(excluding: .top, insets: .right(70) + .left(4)) + timeLabel.topToBottom(of: textView, offset: 4) + timeLabel.textAlignment = .left + } + + func layoutMyMessage() + { + textView.edgesToSuperview(excluding: .left, insets: .right(8) + .bottom(20) + .top(4)) + textView.backgroundColor = UIColor(red:0.08, green:0.49, blue:0.98, alpha:1.0) + textView.textColor = .white + timeLabel.edgesToSuperview(excluding: .top, insets: .left(70) + .right(4)) + timeLabel.topToBottom(of: textView, offset: 4) } } + + + diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift index b4ab918..d87ce2e 100644 --- a/GDproject/Controller/Messages/Dialog/DialogViewController.swift +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -8,6 +8,7 @@ import UIKit import TinyConstraints +import Marklight protocol UpdatableGroup:class { func updateGroup(with group: Model.Group) @@ -46,7 +47,6 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat var cellData: [PostCellData] = [] { didSet { - print(cellData) tableView.reloadData() } } @@ -63,10 +63,12 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat tabBarController?.tabBar.isHidden = true navigationItem.largeTitleDisplayMode = .never + tableView.keyboardDismissMode = .onDrag + tableView.separatorStyle = .none tableView.register(DialogCell.self, forCellReuseIdentifier: cellId) tableView.transform = CGAffineTransform(scaleX: 1, y: -1) tableView.contentInsetAdjustmentBehavior = .never - tableView.contentOffset = CGPoint(x: 0, y: -30) + tableView.contentOffset = CGPoint(x: 0, y: 30) if let groupChat = groupChat { setTitleForGroup(groupChat: groupChat) @@ -77,15 +79,33 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat var messageSendView: UIView = { let view = UIView() - view.backgroundColor = .red + view.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) return view }() - var messageTextView: UITextView = { - let textView = UITextView() + var messageTextView: UITextView = + { + let textStorage = MarklightTextStorage() + textStorage.marklightTextProcessor.codeColor = UIColor.orange + textStorage.marklightTextProcessor.quoteColor = UIColor.darkGray + textStorage.marklightTextProcessor.syntaxColor = UIColor.blue + textStorage.marklightTextProcessor.codeFontName = "Courier" + textStorage.marklightTextProcessor.fontTextStyle = UIFont.TextStyle.subheadline.rawValue + textStorage.marklightTextProcessor.hideSyntax = false + + let layoutManager = NSLayoutManager() + + // Assign the `UITextView`'s `NSLayoutManager` to the `NSTextStorage` subclass + //textStorage.addLayoutManager(textView.layoutManager) + textStorage.addLayoutManager(layoutManager) + + let textContainer = NSTextContainer() + layoutManager.addTextContainer(textContainer) + + let textView = UITextView(frame: CGRect.zero, textContainer: textContainer) textView.isEditable = true textView.isScrollEnabled = true - textView.backgroundColor = .green + textView.backgroundColor = .white return textView }() @@ -101,7 +121,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat { var destination: Model.MessageDestination? - if let group = groupChat{ + if let group = groupChat { destination = Model.MessageDestination.groupChatDestination(group.group.id) } else if let user = userChat { destination = Model.MessageDestination.userChatDestination(user.user) @@ -111,10 +131,17 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat { Model.sendMessage(message: Model.SendMessage(body: Model.Attachments(markdown: messageTextView.text), destination: destination)) { [unowned self] in self.getMessages(for: self.dialog!) + self.messageTextView.text = "" } } } + var lineView: UIView = { + let view = UIView() + view.backgroundColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1) + return view + }() + var bottomConstraint: NSLayoutConstraint! func setConstraints(){ @@ -123,17 +150,21 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat messageSendView.addSubview(messageTextView) messageSendView.addSubview(sendButton) + messageSendView.addSubview(lineView) messageSendView.edgesToSuperview(excluding: [.top,.bottom]) bottomConstraint = NSLayoutConstraint(item: messageSendView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0) view.addConstraint(bottomConstraint) - messageSendView.height(40) + messageSendView.height(60) sendButton.edgesToSuperview(excluding: .left) sendButton.width(60) + lineView.edgesToSuperview(excluding: .bottom) + lineView.height(0.5) + messageTextView.edgesToSuperview(excluding: .right) messageTextView.rightToLeft(of: sendButton) @@ -167,11 +198,11 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat } func setTitleForChat(userChat: Model.UserChat){ - navigationItem.title = "🌌 \(users![userChat.user]!.fullName())" + navigationItem.title = "👤 \(users![userChat.user]!.fullName())" } func setTitleForGroup(groupChat: Model.GroupChat){ - navigationItem.title = "🌌 \(groupChat.group.id) \(groupChat.group.name)" + navigationItem.title = "\(groupChat.group.name)" navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Info", style: .plain, target: self, action: #selector(moveToInfoVC)) } @@ -215,6 +246,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat } func getMessages(for dialog: Model.Dialog){ + print("Here") switch dialog { case .groupChat(let groupChat): Model.getMessagesFor(typeOfChat: Model.Dialog.groupChat(groupChat), chat: groupChat.group.id) @@ -235,10 +267,12 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat //In cellForRowAtIndexPath cell.transform = CGAffineTransform(scaleX: 1, y: -1) + cell.selectionStyle = .none + if let author = currentMessagesInChat?[indexPath.row].author, let user = users?[author] { - cell.fill(with: cellData[indexPath.row].attributedData, byUser: user) + cell.fill(with: cellData[indexPath.row].attributedData, byUser: user, when: (currentMessagesInChat?[indexPath.row].time)!) } return cell diff --git a/GDproject/Controller/Messages/MessagesViewController.swift b/GDproject/Controller/Messages/MessagesViewController.swift index dc52acf..5dc8d01 100644 --- a/GDproject/Controller/Messages/MessagesViewController.swift +++ b/GDproject/Controller/Messages/MessagesViewController.swift @@ -70,7 +70,7 @@ class MessagesViewController: UITableViewController { case .groupChat(let group): cell.textLabel?.text = group.group.name - cell.detailTextLabel?.text = group.lastMessage.body.markdown + cell.detailTextLabel?.text = group.lastMessage!.body.markdown case .userChat(let userChat): cell.textLabel?.text = "👤 \(users[userChat.user]!.fullName())" cell.detailTextLabel?.text = userChat.lastMessage.body.markdown diff --git a/GDproject/Controller/Profile/ProfileViewController.swift b/GDproject/Controller/Profile/ProfileViewController.swift index b2db09b..1ba0ea8 100644 --- a/GDproject/Controller/Profile/ProfileViewController.swift +++ b/GDproject/Controller/Profile/ProfileViewController.swift @@ -38,7 +38,8 @@ class ProfileViewController: UIViewController var protoDictionary: [Int: UIImage] = [9: #imageLiteral(resourceName: "9"), 5051: #imageLiteral(resourceName: "5051"), 69: #imageLiteral(resourceName: "69"), 42: #imageLiteral(resourceName: "42")] - func fill(with user: Model.Users){ + func fill(with user: Model.Users) + { self.facultyLabel.text = "Студент: Факультет Компьютерных Наук" self.nameLabel.text = "\(user.firstName) \(user.middleName)" self.surnameLabel.text = "\(user.lastName)" @@ -54,36 +55,38 @@ class ProfileViewController: UIViewController var user: Model.Users? { didSet { self.fill(with: user!) - Model.getPostsForUser(with: user!.id) { [weak self] (posts) in - self?.dataSourse = posts - } navigationItem.title = "\(user!.firstName) \(user!.lastName)" } } + var channel: Model.Channels? { + didSet { + self.update() + } + } + + func update() + { + Model.getAnonymousChannel(by: channel!) { [unowned self] in + self.posts.dataSourse = $0.posts + self.posts.dictionary = $0.users + self.user = $0.users[self.idProfile!] + } + } + var basicInfo = BasicInfoController() var posts = NewsVC() - var dataSourse: [Model.Posts]?{ - didSet{ - - var newPosts: [Model.Posts] = [] - - dataSourse?.forEach({ (post) in - newPosts.append(Model.Posts(body: post.body, authorId: post.authorId, id: post.id, user: user!, date: post.updated, tags: post.tags)) - }) - - self.posts.dataSourse = newPosts - tableView.reloadData() - } - } - override func viewDidLoad() { super.viewDidLoad() - print("hell") + if idProfile == nil { + idProfile = DataStorage.standard.getUserId() + } + posts.viewController = self posts.type = .NONE + posts.currChannel = channel tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") @@ -100,23 +103,20 @@ class ProfileViewController: UIViewController tableView.reloadData() } - var idProfile: Int? + var idProfile: Int? { + didSet { + channel = Model.Channels(people: [idProfile!], name: "", id: 0, tags: []) + } + } override func viewWillAppear(_ animated: Bool) { if idProfile == nil { idProfile = DataStorage.standard.getUserId() } + user = Model.Channels.fullPeopleDict[idProfile!] - if let id = idProfile { - if let user = Model.idUser[id] { - self.user = user - } else { - Model.getUsers(for: [id]) { [weak self] (dic) in - self?.user = dic[id] - } - } - } + update() setUpNavigarionBar() } diff --git a/GDproject/Extentions.swift b/GDproject/Extentions.swift index 571607e..fe9738c 100644 --- a/GDproject/Extentions.swift +++ b/GDproject/Extentions.swift @@ -60,3 +60,20 @@ extension UIStoryboard { return UIStoryboard.userEdit.instantiateViewController(withIdentifier: logInController) as! LogInViewController } } + + +extension String { + func getDate() -> String + { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"//this your string date format + dateFormatter.timeZone = TimeZone(abbreviation: "UTC") + let date = dateFormatter.date(from: self) + + dateFormatter.dateFormat = "MMM d, yyyy HH:mm" ///this is what you want to convert format + dateFormatter.timeZone = NSTimeZone.local + let timeStamp = dateFormatter.string(from: date!) + + return timeStamp + } +} diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index fb9baed..74f3f39 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -611,7 +611,13 @@ class Model{ struct GroupChat: Codable { var group: Group - var lastMessage: LastMessage + var lastMessage: LastMessage? + + + init(group: Group, lastMessage: LastMessage? = nil) { + self.group = group + self.lastMessage = lastMessage + } } struct UserChat: Codable { From 1a8769e664df7787206a22011b17ee93c1665b69 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Wed, 8 May 2019 19:41:21 +0300 Subject: [PATCH 22/34] Added userChats --- GDproject/Base.lproj/Main.storyboard | 3 + .../FullPostController.swift | 1 + .../ News and channels/NewsController.swift | 6 +- .../Coordinators/MessagesCoordinator.swift | 40 ++++++++++--- .../Messages/ChatInfoViewController.swift | 7 ++- .../Messages/Dialog/DialogCell.swift | 11 ++-- .../Dialog/DialogViewController.swift | 58 ++++++++++++++----- .../Messages/MessagesViewController.swift | 2 +- .../Profile/ProfileViewController.swift | 13 +++++ GDproject/Simple model/Model.swift | 7 ++- 10 files changed, 111 insertions(+), 37 deletions(-) diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 2f3d480..23aeadc 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -90,6 +90,9 @@ diff --git a/GDproject/Controller/ News and channels/FullPostController.swift b/GDproject/Controller/ News and channels/FullPostController.swift index ef3435b..e423cb9 100644 --- a/GDproject/Controller/ News and channels/FullPostController.swift +++ b/GDproject/Controller/ News and channels/FullPostController.swift @@ -102,6 +102,7 @@ class FullPostController: UITableViewController { cell.onUserDisplay = { [weak self] (id) in let vc = self?.storyboard!.instantiateViewController(withIdentifier: profileViewController) as! ProfileViewController vc.idProfile = id + self?.navigationController?.pushViewController(vc, animated: true) } default: diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index 1168feb..da86a19 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -121,11 +121,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi } } default: - if let anonChannel = anonymousChannel { - news.dataSourse = anonChannel.posts - news.dictionary = anonChannel.users - changedChannelName?("Anonymous") - } + break } } diff --git a/GDproject/Controller/Coordinators/MessagesCoordinator.swift b/GDproject/Controller/Coordinators/MessagesCoordinator.swift index 46c2dbe..3ab371e 100644 --- a/GDproject/Controller/Coordinators/MessagesCoordinator.swift +++ b/GDproject/Controller/Coordinators/MessagesCoordinator.swift @@ -36,24 +36,46 @@ class MessagesCoordinator: BaseCoordinator { newVC.whatToDoWithSelection = { [weak newVC, weak self] in newVC?.navigationController?.popViewController(animated: true) - var group = Model.Group(users: $0, name: "Untitled", id: 0) - Model.createGroupChat(from: group, completion: - { [weak self] id in - - let newVC1 = DialogViewController() - group.id = id - newVC1.dialog = Model.Dialog.groupChat(Model.GroupChat(group: group)) - self?.navigationController?.pushViewController(newVC1, animated: true) - }) + // detect is it a user or group dialog + let count = $0.count + if count == 1 { + + let user = $0.first!.key + let createdDialog = Model.Dialog.userChat(Model.UserChat(user: user)) + + let vc = DialogViewController() + vc.users = Model.Channels.fullPeopleDict + vc.dialog = createdDialog + self?.navigationController?.pushViewController(vc, animated: true) + + } else { + var group = Model.Group(users: $0, name: "Untitled", id: 0) + Model.createGroupChat(from: group, completion: { [weak self] id in + + let newVC1 = DialogViewController() + group.id = id + newVC1.dialog = Model.Dialog.groupChat(Model.GroupChat(group: group)) + self?.navigationController?.pushViewController(newVC1, animated: true) + }) + } } vc?.navigationController?.pushViewController(newVC, animated: true) } vc.onDialogDisplay = { [weak vc] in + let newVC = DialogViewController() newVC.dialog = $0.dialog newVC.users = $0.users + newVC.onUserDisplay = { [weak self] id in + + let vc = self?.storyboard.instantiateViewController(withIdentifier: profileViewController) as! ProfileViewController + vc.idProfile = id + self?.navigationController!.pushViewController(vc, animated: true) + + } + vc?.navigationController?.pushViewController(newVC, animated: true) } diff --git a/GDproject/Controller/Messages/ChatInfoViewController.swift b/GDproject/Controller/Messages/ChatInfoViewController.swift index 80edd77..f2c1808 100644 --- a/GDproject/Controller/Messages/ChatInfoViewController.swift +++ b/GDproject/Controller/Messages/ChatInfoViewController.swift @@ -190,7 +190,12 @@ class ChatInfoViewController: UITableViewController { self?.navigationController?.popViewController(animated: true) } default: - showUserChoiceVC() + switch myPermissions{ + case .admin: + showUserChoiceVC() + default: + break + } } } } diff --git a/GDproject/Controller/Messages/Dialog/DialogCell.swift b/GDproject/Controller/Messages/Dialog/DialogCell.swift index 8dadb68..7524af4 100644 --- a/GDproject/Controller/Messages/Dialog/DialogCell.swift +++ b/GDproject/Controller/Messages/Dialog/DialogCell.swift @@ -16,14 +16,14 @@ class DialogCell: UITableViewCell { let button = UIButton() button.setTitleColor(.black, for: .normal) button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16) - button.addTarget(self, action: #selector(displayProfile), for: .touchUpInside) button.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left return button }() @objc func displayProfile() { - if let id = self.user?.id{ + if let id = self.user?.id { + print("diplay") onUserDisplay?(id) } } @@ -80,6 +80,8 @@ class DialogCell: UITableViewCell { // important contentView.subviews.forEach({ $0.removeFromSuperview() }) + nameLabel.addTarget(self, action: #selector(displayProfile), for: .touchUpInside) + self.user = byUser textView = createTextView(with: markdownText, true) textView.layer.cornerRadius = 10 @@ -110,7 +112,7 @@ class DialogCell: UITableViewCell { textView.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) textView.textColor = .white - timeLabel.edgesToSuperview(excluding: .top, insets: .right(70) + .left(4)) + timeLabel.edgesToSuperview(excluding: .top, insets: .right(70) + .left(8)) timeLabel.topToBottom(of: textView, offset: 4) timeLabel.textAlignment = .left } @@ -120,8 +122,9 @@ class DialogCell: UITableViewCell { textView.edgesToSuperview(excluding: .left, insets: .right(8) + .bottom(20) + .top(4)) textView.backgroundColor = UIColor(red:0.08, green:0.49, blue:0.98, alpha:1.0) textView.textColor = .white - timeLabel.edgesToSuperview(excluding: .top, insets: .left(70) + .right(4)) + timeLabel.edgesToSuperview(excluding: .top, insets: .left(70) + .right(8)) timeLabel.topToBottom(of: textView, offset: 4) + timeLabel.textAlignment = .right } } diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift index d87ce2e..d864e88 100644 --- a/GDproject/Controller/Messages/Dialog/DialogViewController.swift +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -16,6 +16,7 @@ protocol UpdatableGroup:class { class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegate, UITableViewDataSource { + var onUserDisplay: ((Int)->())? var tableView: UITableView = UITableView() @@ -47,6 +48,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat var cellData: [PostCellData] = [] { didSet { + prevLast = cellData.count - 1 tableView.reloadData() } } @@ -198,7 +200,14 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat } func setTitleForChat(userChat: Model.UserChat){ - navigationItem.title = "👤 \(users![userChat.user]!.fullName())" + navigationItem.title = "\(users![userChat.user]!.fullName())" + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "👤", style: .plain, target: self, action: #selector(showPersonPage)) + } + + @objc func showPersonPage(){ + if let id = userChat?.user{ + onUserDisplay?(id) + } } func setTitleForGroup(groupChat: Model.GroupChat){ @@ -206,6 +215,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Info", style: .plain, target: self, action: #selector(moveToInfoVC)) } + // onInfoShow @objc func moveToInfoVC(){ let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: chatInfoViewController) as! ChatInfoViewController @@ -219,11 +229,10 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat navigationController?.pushViewController(vc, animated: true) } - var currentMessagesInChat: [Model.LastMessage]? { - didSet { - if let currentMessagesInChat = currentMessagesInChat { - cellData = currentMessagesInChat.map { PostCellData(attributedData: PostCellData.create(with: [$0.body])) } - } + var currentMessagesInChat: [Model.LastMessage] = [] { + didSet + { + cellData = currentMessagesInChat.map { PostCellData(attributedData: PostCellData.create(with: [$0.body])) } } } @@ -245,18 +254,18 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat } } - func getMessages(for dialog: Model.Dialog){ - print("Here") + func getMessages(for dialog: Model.Dialog, starting from: Int? = nil) + { switch dialog { case .groupChat(let groupChat): - Model.getMessagesFor(typeOfChat: Model.Dialog.groupChat(groupChat), chat: groupChat.group.id) - { [unowned self] in - self.currentMessagesInChat = $0 + Model.getMessagesFor(typeOfChat: Model.Dialog.groupChat(groupChat), chat: groupChat.group.id, exclusiveFrom: from) + { [weak self] in + self?.currentMessagesInChat = $0 } case .userChat(let userChat): - Model.getMessagesFor(typeOfChat: Model.Dialog.userChat(userChat), chat: userChat.user) - { [unowned self] in - self.currentMessagesInChat = $0 + Model.getMessagesFor(typeOfChat: Model.Dialog.userChat(userChat), chat: userChat.user, exclusiveFrom: from) + { [weak self] in + self?.currentMessagesInChat = $0 } } } @@ -270,13 +279,30 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat cell.selectionStyle = .none - if let author = currentMessagesInChat?[indexPath.row].author, let user = users?[author] + if let user = users?[currentMessagesInChat[indexPath.row].author] { - cell.fill(with: cellData[indexPath.row].attributedData, byUser: user, when: (currentMessagesInChat?[indexPath.row].time)!) + cell.fill(with: cellData[indexPath.row].attributedData, byUser: user, when: currentMessagesInChat[indexPath.row].time) + + cell.onUserDisplay = onUserDisplay } return cell } + + var prevLast = -1 + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) + { + // pagination +// if indexPath.row == cellData.count - 1 && prevLast != indexPath.row +// { +// if let dialog = dialog +// { +// getMessages(for: dialog, starting: prevLast) +// } +// +// prevLast = indexPath.row +// } + } } diff --git a/GDproject/Controller/Messages/MessagesViewController.swift b/GDproject/Controller/Messages/MessagesViewController.swift index 5dc8d01..6993827 100644 --- a/GDproject/Controller/Messages/MessagesViewController.swift +++ b/GDproject/Controller/Messages/MessagesViewController.swift @@ -73,7 +73,7 @@ class MessagesViewController: UITableViewController { cell.detailTextLabel?.text = group.lastMessage!.body.markdown case .userChat(let userChat): cell.textLabel?.text = "👤 \(users[userChat.user]!.fullName())" - cell.detailTextLabel?.text = userChat.lastMessage.body.markdown + cell.detailTextLabel?.text = userChat.lastMessage!.body.markdown } return cell diff --git a/GDproject/Controller/Profile/ProfileViewController.swift b/GDproject/Controller/Profile/ProfileViewController.swift index 1ba0ea8..42d115a 100644 --- a/GDproject/Controller/Profile/ProfileViewController.swift +++ b/GDproject/Controller/Profile/ProfileViewController.swift @@ -34,6 +34,18 @@ class ProfileViewController: UIViewController var onSettings: (()->())? + @IBAction func sendMessage(_ sender: UIButton) + { + if let userId = idProfile + { + let createdDialog = Model.Dialog.userChat(Model.UserChat(user: userId)) + let vc = DialogViewController() + vc.users = Model.Channels.fullPeopleDict + vc.dialog = createdDialog + self.navigationController?.pushViewController(vc, animated: true) + } + } + var onChannelsListToAddAPerson: ((Model.Users)->())? var protoDictionary: [Int: UIImage] = [9: #imageLiteral(resourceName: "9"), 5051: #imageLiteral(resourceName: "5051"), 69: #imageLiteral(resourceName: "69"), 42: #imageLiteral(resourceName: "42")] @@ -110,6 +122,7 @@ class ProfileViewController: UIViewController } override func viewWillAppear(_ animated: Bool) { + tabBarController?.tabBar.isHidden = false if idProfile == nil { idProfile = DataStorage.standard.getUserId() diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 74f3f39..74b355a 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -622,7 +622,12 @@ class Model{ struct UserChat: Codable { var user: Int - var lastMessage: LastMessage + var lastMessage: LastMessage? + + init(user: Int, lastMessage: LastMessage? = nil) { + self.user = user + self.lastMessage = lastMessage + } } struct Group: Codable { From 8825e4d2b56d88bd6b0fa2b0dc80ee606119ecc4 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Thu, 9 May 2019 15:26:04 +0300 Subject: [PATCH 23/34] Added pagination (needs modifications) --- .../Dialog/DialogViewController.swift | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift index d864e88..40a4ad4 100644 --- a/GDproject/Controller/Messages/Dialog/DialogViewController.swift +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -48,8 +48,8 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat var cellData: [PostCellData] = [] { didSet { - prevLast = cellData.count - 1 tableView.reloadData() + canBePaginated = true } } @@ -62,7 +62,6 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat self.tableView.delegate = self self.tableView.dataSource = self - tabBarController?.tabBar.isHidden = true navigationItem.largeTitleDisplayMode = .never tableView.keyboardDismissMode = .onDrag @@ -132,9 +131,12 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat if let destination = destination { Model.sendMessage(message: Model.SendMessage(body: Model.Attachments(markdown: messageTextView.text), destination: destination)) { [unowned self] in - self.getMessages(for: self.dialog!) + + self.getMessagesNew(for: self.dialog!) self.messageTextView.text = "" } + + prevLast = -1 } } @@ -248,6 +250,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + tabBarController?.tabBar.isHidden = true if let dialog = dialog { getMessages(for: dialog) @@ -255,6 +258,23 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat } func getMessages(for dialog: Model.Dialog, starting from: Int? = nil) + { + switch dialog { + case .groupChat(let groupChat): + Model.getMessagesFor(typeOfChat: Model.Dialog.groupChat(groupChat), chat: groupChat.group.id, exclusiveFrom: from) + { [weak self] in + self?.currentMessagesInChat.append(contentsOf: $0) + } + case .userChat(let userChat): + Model.getMessagesFor(typeOfChat: Model.Dialog.userChat(userChat), chat: userChat.user, exclusiveFrom: from) + { [weak self] in + self?.currentMessagesInChat.append(contentsOf: $0) + } + } + } + + + func getMessagesNew(for dialog: Model.Dialog, starting from: Int? = nil) { switch dialog { case .groupChat(let groupChat): @@ -290,18 +310,19 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat } var prevLast = -1 + var canBePaginated = false func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - // pagination -// if indexPath.row == cellData.count - 1 && prevLast != indexPath.row -// { -// if let dialog = dialog -// { -// getMessages(for: dialog, starting: prevLast) -// } -// -// prevLast = indexPath.row -// } + if indexPath.row == cellData.count - 1 && prevLast != indexPath.row && canBePaginated + { + print("exclusiveFrom \(currentMessagesInChat.last?.id ?? 0)") + if let dialog = dialog + { + getMessages(for: dialog, starting: currentMessagesInChat.last?.id) + } + + prevLast = indexPath.row + } } } From c621bf184f6269b9fde313883449a548a0f60e1b Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Sat, 11 May 2019 00:42:31 +0300 Subject: [PATCH 24/34] Not working authentication --- GDproject.xcodeproj/project.pbxproj | 20 +- GDproject/Base.lproj/Main.storyboard | 243 ++++++++++++++++++ .../Coordinators/LogInCoordinator.swift | 44 +++- .../Coordinators/ProfileCoordinator.swift | 10 +- .../Log In/CodeViewController.swift | 105 ++++++++ .../Log In/FacultyTableViewController.swift | 87 +++++++ .../Controller/Log In/InititalsViewCell.swift | 23 ++ .../Controller/Log In/LogInCoordinator1.swift | 48 ---- .../Log In/LogInViewController.swift | 8 +- .../Log In/RegisterTableViewController.swift | 145 +++++++++++ GDproject/Simple model/Model.swift | 185 +++++++++++-- 11 files changed, 830 insertions(+), 88 deletions(-) create mode 100644 GDproject/Controller/Log In/CodeViewController.swift create mode 100644 GDproject/Controller/Log In/FacultyTableViewController.swift create mode 100644 GDproject/Controller/Log In/InititalsViewCell.swift delete mode 100644 GDproject/Controller/Log In/LogInCoordinator1.swift create mode 100644 GDproject/Controller/Log In/RegisterTableViewController.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index 6487e58..daa6033 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -13,6 +13,10 @@ 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A4221F1B3200F6E42A /* LogInViewController.swift */; }; 123E37A7221F1DD700F6E42A /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A6221F1DD700F6E42A /* MainController.swift */; }; 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; }; + 125A9A8A2285C3DA00513E2A /* CodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125A9A892285C3DA00513E2A /* CodeViewController.swift */; }; + 125A9A8E2286068A00513E2A /* RegisterTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125A9A8D2286068A00513E2A /* RegisterTableViewController.swift */; }; + 125A9A90228609C500513E2A /* InititalsViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125A9A8F228609C500513E2A /* InititalsViewCell.swift */; }; + 125A9A922286100800513E2A /* FacultyTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125A9A912286100800513E2A /* FacultyTableViewController.swift */; }; 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; }; 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; }; 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; }; @@ -23,7 +27,6 @@ 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1288B5CD221F1158002BE6B1 /* DataStorage.swift */; }; 1291BE2D2221312D009D3F23 /* ChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */; }; 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */; }; - 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */; }; 129320042279B4270035C7B3 /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320032279B4270035C7B3 /* MessagesViewController.swift */; }; 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320062279B5690035C7B3 /* MessagesCoordinator.swift */; }; 129320092279C3B50035C7B3 /* PeopleToWriteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */; }; @@ -108,6 +111,10 @@ 123E37A4221F1B3200F6E42A /* LogInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInViewController.swift; sourceTree = ""; }; 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = ""; }; 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; + 125A9A892285C3DA00513E2A /* CodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeViewController.swift; sourceTree = ""; }; + 125A9A8D2286068A00513E2A /* RegisterTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterTableViewController.swift; sourceTree = ""; }; + 125A9A8F228609C500513E2A /* InititalsViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InititalsViewCell.swift; sourceTree = ""; }; + 125A9A912286100800513E2A /* FacultyTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FacultyTableViewController.swift; sourceTree = ""; }; 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; }; 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = ""; }; 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = ""; }; @@ -118,7 +125,6 @@ 1288B5CD221F1158002BE6B1 /* DataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStorage.swift; sourceTree = ""; }; 1291BE2C2221312C009D3F23 /* ChannelsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsCoordinator.swift; sourceTree = ""; }; 1291BE332221569B009D3F23 /* TabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = ""; }; - 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInCoordinator1.swift; sourceTree = ""; }; 129320032279B4270035C7B3 /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; 129320062279B5690035C7B3 /* MessagesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesCoordinator.swift; sourceTree = ""; }; 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PeopleToWriteViewController.swift; path = GDproject/Controller/Messages/PeopleToWriteViewController.swift; sourceTree = SOURCE_ROOT; }; @@ -216,8 +222,11 @@ 1291BE3722218DC4009D3F23 /* Log In */ = { isa = PBXGroup; children = ( + 125A9A912286100800513E2A /* FacultyTableViewController.swift */, + 125A9A8F228609C500513E2A /* InititalsViewCell.swift */, + 125A9A8D2286068A00513E2A /* RegisterTableViewController.swift */, + 125A9A892285C3DA00513E2A /* CodeViewController.swift */, 123E37A4221F1B3200F6E42A /* LogInViewController.swift */, - 1291BE3522218DBF009D3F23 /* LogInCoordinator1.swift */, ); path = "Log In"; sourceTree = ""; @@ -425,11 +434,11 @@ files = ( 129320072279B5690035C7B3 /* MessagesCoordinator.swift in Sources */, 124CC7032221C00C009DF531 /* Model.swift in Sources */, + 125A9A90228609C500513E2A /* InititalsViewCell.swift in Sources */, 1220FF94227C41270092C6BC /* TaggsSelectionViewController.swift in Sources */, 12F3D6B0224106F700A69214 /* TabbarController.swift in Sources */, 12E36DD522156559006FCDD7 /* MyStackView.swift in Sources */, 12D7D137221D78E800B35452 /* Channel.swift in Sources */, - 1291BE3622218DBF009D3F23 /* LogInCoordinator1.swift in Sources */, 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */, 125BD5812217314A008A3575 /* NewsVC.swift in Sources */, 12E36DB72214400A006FCDD7 /* Post.swift in Sources */, @@ -440,10 +449,13 @@ 1261BB9E227B793D003898CF /* ChatInfoViewController.swift in Sources */, 12E36D9E221424EA006FCDD7 /* NewsController.swift in Sources */, 1298810B227F13840032ACA3 /* DialogCell.swift in Sources */, + 125A9A922286100800513E2A /* FacultyTableViewController.swift in Sources */, + 125A9A8E2286068A00513E2A /* RegisterTableViewController.swift in Sources */, 12E36DCC22144725006FCDD7 /* PostViewCell.swift in Sources */, 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */, 125BD57F22171D73008A3575 /* Extentions.swift in Sources */, 12D7D133221C321600B35452 /* ChannelListController.swift in Sources */, + 125A9A8A2285C3DA00513E2A /* CodeViewController.swift in Sources */, 1291BE342221569B009D3F23 /* TabbarCoordinator.swift in Sources */, 129320092279C3B50035C7B3 /* PeopleToWriteViewController.swift in Sources */, 12BA4B9B224101A400DF93D7 /* ApplicationCoordinator.swift in Sources */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 23aeadc..24b2369 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -231,6 +231,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GDproject/Controller/Coordinators/LogInCoordinator.swift b/GDproject/Controller/Coordinators/LogInCoordinator.swift index f603f3b..1b2665c 100644 --- a/GDproject/Controller/Coordinators/LogInCoordinator.swift +++ b/GDproject/Controller/Coordinators/LogInCoordinator.swift @@ -10,6 +10,8 @@ import UIKit class LogInCoordinator: BaseCoordinator { + let storyboard = UIStoryboard(name: "Main", bundle: nil) + var didEndFlow: (()->())? var window: UIWindow! var navigationController: UINavigationController? @@ -22,20 +24,46 @@ class LogInCoordinator: BaseCoordinator { override func start(){ let logInVC = LogInViewController() - logInVC.authenticate = { [weak self] (id) in + logInVC.authenticate = { [weak self, weak logInVC] (email) in - Model.authenticate(with: id) { - (res) in + Model.authenticate(with: email) { [weak self] + (authStatus) in - if (res) { - DataStorage.standard.setUserKey(with: id) - self?.didEndFlow?() + print(authStatus.userStatus) + + if (authStatus.userStatus == "invalid") { + logInVC?.mailTextField.text = "invalid" } - else { - DataStorage.standard.setUserKey(with: 0) + else if (authStatus.userStatus == "canRegister") { // register form + self?.presentRegisterVC() + } else { // validation code + self?.presentViewControllerForCodeInput() } } } + navigationController?.pushViewController(logInVC, animated: false) } + + func presentViewControllerForCodeInput() + { + let vc = storyboard.instantiateViewController(withIdentifier: "CodeViewController") as! CodeViewController + self.navigationController?.pushViewController(vc, animated: true) + vc.onSuccessLogIn = { + [weak self] in self?.didEndFlow?() + } + } + + func presentRegisterVC(){ + + let vc1 = storyboard.instantiateViewController(withIdentifier: "RegisterTableViewController") as! RegisterTableViewController + + vc1.onRegistration = { [weak self] in + self?.presentViewControllerForCodeInput() + } + + self.navigationController?.pushViewController(vc1, animated: true) + } + + } diff --git a/GDproject/Controller/Coordinators/ProfileCoordinator.swift b/GDproject/Controller/Coordinators/ProfileCoordinator.swift index fff48a0..75c0e25 100644 --- a/GDproject/Controller/Coordinators/ProfileCoordinator.swift +++ b/GDproject/Controller/Coordinators/ProfileCoordinator.swift @@ -27,9 +27,13 @@ class ProfileCoordinator: BaseCoordinator { let storyboard = UIStoryboard(name: "Main", bundle: nil) let profile = storyboard.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController - profile.logOut = { [weak self] in - DataStorage.standard.setIsLoggedIn(value: false, with: 0) - self?.didEndSession?() + profile.logOut = { + Model.logout() { + [weak self] in + + DataStorage.standard.setIsLoggedIn(value: false, with: 0) + self?.didEndSession?() + } } profile.onSettings = { [weak self, weak storyboard] in diff --git a/GDproject/Controller/Log In/CodeViewController.swift b/GDproject/Controller/Log In/CodeViewController.swift new file mode 100644 index 0000000..93519ec --- /dev/null +++ b/GDproject/Controller/Log In/CodeViewController.swift @@ -0,0 +1,105 @@ +// +// CodeViewController.swift +// GDproject +// +// Created by cstore on 10/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class CodeViewController: UIViewController { + + @IBOutlet weak var f1: UITextField! + @IBOutlet weak var f2: UITextField! + @IBOutlet weak var f3: UITextField! + @IBOutlet weak var f4: UITextField! + @IBOutlet weak var f5: UITextField! + @IBOutlet weak var f6: UITextField! + + var onSuccessLogIn: (()->())? + + override func viewDidLoad() { + super.viewDidLoad() + setUpConstraint() + + f1.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) + f2.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) + f3.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) + f4.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) + f5.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) + f6.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + f1.becomeFirstResponder() + } + + @objc func textFiledDidChange(textField: UITextField) { + let text = textField.text + + // only 1 char + if text?.utf16.count == 1 { + switch textField { + case f1: + f2.becomeFirstResponder() + case f2: + f3.becomeFirstResponder() + case f3: + f4.becomeFirstResponder() + case f4: + f5.becomeFirstResponder() + case f5: + f6.becomeFirstResponder() + case f6: + f6.resignFirstResponder() + default: + break + } + } + } + + + @IBOutlet weak var bottomConstraint: NSLayoutConstraint! + + @IBAction func whereToGoNext(_ sender: UIButton) + { + let code = "\(f1.text!)\(f2.text!)\(f3.text!)\(f4.text!)\(f5.text!)\(f6.text!)" + if let codeToInt = Int(code) + { + Model.verify(with: codeToInt) { _ in + // if everything is okay we can authemticicate + // Model.authemticiateMe + Model.authenticateMe { [weak self] (res) in + if res { + self?.onSuccessLogIn?() + } else { + print("sosi") + } + } + } + } + } + + func setUpConstraint(){ + // for keyboard notifications + NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillShowNotification, object: nil) + + NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillHideNotification, object: nil) + } + + @objc func handleKeyboardNotifications(notification: NSNotification){ + if let userInfo = notification.userInfo { + // UIKeyboardFrameEndUserInfoKey + let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue + bottomConstraint.constant = notification.name == UIResponder.keyboardWillShowNotification ? keyBoardFrame.height - self.view.safeAreaInsets.bottom : 0 + + UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: { + self.view.layoutIfNeeded() + }) + } + } + +} diff --git a/GDproject/Controller/Log In/FacultyTableViewController.swift b/GDproject/Controller/Log In/FacultyTableViewController.swift new file mode 100644 index 0000000..3b995fe --- /dev/null +++ b/GDproject/Controller/Log In/FacultyTableViewController.swift @@ -0,0 +1,87 @@ +// +// FacultyTableViewController.swift +// GDproject +// +// Created by cstore on 10/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +protocol ChosenFactulty: class { + func onChooseFaculty(faculty: Model.Faculty) +} + +class FacultyTableViewController: UITableViewController { + + weak var delegate: ChosenFactulty? + + var currentFaculties: [Model.Faculty] = []{ + didSet { + tableView.reloadData() + } + } + + var isFiltering: Bool { + return searchController.isActive && !searchBarIsEmpty() + } + + func searchBarIsEmpty() -> Bool { + return searchController.searchBar.text?.isEmpty ?? true + } + + var searchController = UISearchController(searchResultsController: nil) + + override func viewDidLoad() { + super.viewDidLoad() + + self.navigationItem.title = "Faculties" + self.navigationItem.searchController = searchController + self.navigationItem.hidesSearchBarWhenScrolling = false + searchController.obscuresBackgroundDuringPresentation = false + + setUpTimer() + } + + func setUpTimer(){ + Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { (timer) in + DispatchQueue.main.async { [weak self] in + + if self!.isFiltering { + Model.searchFaculty(string: (self?.searchController.searchBar.text)!) { [weak self] in + self?.currentFaculties = $0 + } + } + } + } + } + + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return currentFaculties.count + } + + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "F", for: indexPath) + + cell.textLabel?.text = currentFaculties[indexPath.row].name + cell.detailTextLabel?.text = currentFaculties[indexPath.row].campusName + cell.selectionStyle = .none + + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + delegate?.onChooseFaculty(faculty: currentFaculties[indexPath.row]) + self.navigationController?.popViewController(animated: true) + } + +} diff --git a/GDproject/Controller/Log In/InititalsViewCell.swift b/GDproject/Controller/Log In/InititalsViewCell.swift new file mode 100644 index 0000000..8cdbf80 --- /dev/null +++ b/GDproject/Controller/Log In/InititalsViewCell.swift @@ -0,0 +1,23 @@ +// +// InititalsViewCell.swift +// GDproject +// +// Created by cstore on 10/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class InititalsViewCell: UITableViewCell { + + @IBOutlet weak var initialsTextField: UITextField! + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + func fill(with name: String){ + initialsTextField.text = name + } +} diff --git a/GDproject/Controller/Log In/LogInCoordinator1.swift b/GDproject/Controller/Log In/LogInCoordinator1.swift deleted file mode 100644 index f47f836..0000000 --- a/GDproject/Controller/Log In/LogInCoordinator1.swift +++ /dev/null @@ -1,48 +0,0 @@ -//// -//// LogInCoordinator.swift -//// GDproject -//// -//// Created by cstore on 23/02/2019. -//// Copyright © 2019 drHSE. All rights reserved. -//// -// -//import UIKit -// -//class LogInCoordinator{ -// -// private weak var navigationController: UINavigationController? -// private var window: UIWindow! -// // MARK:- Init -// init(navigationController: UINavigationController, window: UIWindow) { -// self.navigationController = navigationController -// self.window = window -// } -// -// func start(){ -// showLogInPage() -// } -// -// // MARK: - Private implementation -//// private func showStatusPage(){ -//// let controller = TabbarCoordinator(window: window) -//// controller.start() -//// } -// -// private func showLogInPage(){ -// let controller = UIStoryboard.makeLogIn() -// -// controller.onLogIn = { -// (id) in Model.authenticate(with: id) { -// (res) in -// -// if (res) { DataStorage.standard.setUserKey(with: id) } -// else { DataStorage.standard.setUserKey(with: 0) } -// -// controller.authenticateSucceeded = res -// } -// } -// -// navigationController?.pushViewController(controller, animated: false) -// } -// -//} diff --git a/GDproject/Controller/Log In/LogInViewController.swift b/GDproject/Controller/Log In/LogInViewController.swift index 5375350..090815a 100644 --- a/GDproject/Controller/Log In/LogInViewController.swift +++ b/GDproject/Controller/Log In/LogInViewController.swift @@ -14,7 +14,7 @@ import Result class LogInViewController: UIViewController { - var authenticate: ((Int)->())? + var authenticate: ((String)->())? let logInLabel: UILabel = { let label = UILabel() @@ -45,7 +45,7 @@ class LogInViewController: UIViewController { }() @objc func handleTap(){ - authenticate?(Int(mailTextField.text!)!) + authenticate?(mailTextField.text!) } private lazy var keyboardBar = UIView() @@ -72,14 +72,14 @@ class LogInViewController: UIViewController { // configure keyboardBar components keyboardBar.addSubview(logInButton) logInButton.height(50) - logInButton.leftToSuperview(view.leftAnchor, offset: 16, relation: .equal, isActive: true) + logInButton.rightToSuperview(view.rightAnchor, offset: 16, relation: .equal, isActive: true) } func logicOfLogInInputValidation() -> ((String?)->()) { let logic: ((String?)->()) = { [weak self] (someText) in - if let text = someText, !text.isEmpty, let _ = Int(text) { + if let text = someText, !text.isEmpty { self?.logInButton.isEnabled = true } else { self?.logInButton.isEnabled = false diff --git a/GDproject/Controller/Log In/RegisterTableViewController.swift b/GDproject/Controller/Log In/RegisterTableViewController.swift new file mode 100644 index 0000000..5bc0ada --- /dev/null +++ b/GDproject/Controller/Log In/RegisterTableViewController.swift @@ -0,0 +1,145 @@ +// +// RegisterTableViewController.swift +// GDproject +// +// Created by cstore on 10/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class RegisterTableViewController: UITableViewController, ChosenFactulty +{ + + func onChooseFaculty(faculty: Model.Faculty) { + self.faculty = faculty + tableView.reloadData() + } + + + var onRegistration: (()->())? + + var faculty: Model.Faculty? + var user: Model.NewRegistration = Model.NewRegistration(email: "dyurednikina@edu.hse.ru", firstName: nil, middleName: nil, lastName: nil, faculty: nil) + + override func viewDidLoad() + { + super.viewDidLoad() + + tableView.keyboardDismissMode = .interactive + navigationItem.title = user.email + navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(endRegistration)) + } + + @objc func endRegistration() + { + updateUser() + guard let faculty = faculty else { return } + + user.faculty = faculty.url + + Model.register(object: user) + { [weak self] in + self?.onRegistration?() + } + + } + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + return 2 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + + switch section { + case 0: + return 3 + default: + if let _ = faculty { + return 2 + } else { + return 1 + } + } + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? + { + switch section { + case 0: + return "Initials" + default: + return "Faculty" + } + } + + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + switch indexPath.section { + case 0: + let cell = tableView.dequeueReusableCell(withIdentifier: "nameCell", for: indexPath) as! InititalsViewCell + switch indexPath.row{ + case 0: + if let name = user.firstName { + cell.fill(with: name) + } else { + cell.initialsTextField.placeholder = "First name" + } + case 1: + if let name = user.middleName { + cell.fill(with: name) + } else { + cell.initialsTextField.placeholder = "Middle name" + } + default: + if let name = user.lastName { + cell.fill(with: name) + } else { + cell.initialsTextField.placeholder = "Last name" + } + } + cell.selectionStyle = .none + return cell + default: + switch indexPath.row { + case 0: + let cell = tableView.dequeueReusableCell(withIdentifier: "cell111", for: indexPath) + cell.selectionStyle = .none + return cell + default: + let cell = tableView.dequeueReusableCell(withIdentifier: "facultyCell", for: indexPath) + cell.textLabel?.text = faculty!.name + cell.detailTextLabel?.text = faculty!.campusName + cell.selectionStyle = .none + return cell + } + } + } + + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if indexPath.section == 1 && indexPath.row == 0 { + + updateUser() + + let vc = storyboard?.instantiateViewController(withIdentifier: "FacultyTableViewController") as! FacultyTableViewController + + vc.delegate = self + + self.navigationController?.pushViewController(vc, animated: true) + } + } + + func updateUser(){ + guard let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? InititalsViewCell else {return} + guard let cell1 = tableView.cellForRow(at: IndexPath(row: 1, section: 0)) as? InititalsViewCell else {return} + guard let cell2 = tableView.cellForRow(at: IndexPath(row: 2, section: 0)) as? InititalsViewCell else {return} + + user.firstName = cell.initialsTextField.text! + user.middleName = cell1.initialsTextField.text! + user.lastName = cell2.initialsTextField.text! + } + +} diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 74b355a..4f136b9 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -14,20 +14,27 @@ class Model{ static let invalidTocken = 498 - static var hashTagTree: CompletionTree? + static var hashTagTree: CompletionTree? private static var isValidTocken: ((Int)->())? = { responce in print(responce) if responce == invalidTocken { - DataStorage.standard.setIsLoggedIn(value: false, with: 0) - (UIApplication.shared.delegate as! AppDelegate).relaunch() + Model.logout { + print("Logout Is Successful") + DataStorage.standard.setIsLoggedIn(value: false, with: 0) + (UIApplication.shared.delegate as! AppDelegate).relaunch() + } } } private static let baseUrl = "https://valera-denis.herokuapp.com" static let decoder = JSONDecoder() - + + static let authMeURL = URL(string: "\(baseUrl)/authentication/me")! + static let registerMeURL = URL(string: "\(baseUrl)/authentication/register")! + static let authVerifyURL = URL(string: "\(baseUrl)/authentication/verify")! static let authenticationURL = URL(string:"\(baseUrl)/authentication/login")! + static let logOutURL = URL(string:"\(baseUrl)/authentication/logout")! static let postsLastURL = URL(string:"\(baseUrl)/posts/last")! static let postsForUserURL = URL(string:"\(baseUrl)/posts/forUser")! static let postsPublishURL = URL(string:"\(baseUrl)/posts/publish")! @@ -49,6 +56,7 @@ class Model{ static let messagesGetGroupChatURL = URL(string: "\(baseUrl)/messages/get/groupChat")! //r static let messagesSendURL = URL(string: "\(baseUrl)/messages/send")! static let messagesGetUserChatURL = URL(string: "\(baseUrl)/messages/get/userChat")! + static let facultySearchURL = URL(string: "\(baseUrl)/faculty/search")! struct QueryPosts: Codable { @@ -183,9 +191,117 @@ class Model{ } } - static func authenticate(with id: Int, completion: @escaping ((Bool)->())) { + struct AuthStatus: Codable { + var userStatus: String - let json: [String:Any] = ["authenticationId" : id] + init(){ + userStatus = "invalid" + } + } + + static func logout(completion: @escaping (()->())){ + var request = URLRequest(url: logOutURL) + request.httpMethod = "POST" + + AF.request(request).response { (responce) in + isValidTocken?(responce.response?.statusCode ?? 498) + + completion() + } + } + + /* + "email": "vchernyshev@hse.ru", + "middleName": "Algebrovich", + "lastName": "Leonidov", + "faculty": "cs.hse.ru/dse", + "firstName": "Seva" + */ + + struct NewRegistration: Codable{ + var email: String + var firstName: String? + var middleName: String? + var lastName: String? + var faculty: String? + } + + static func register(object: NewRegistration, completion: @escaping (()->())) { + var request = URLRequest(url: registerMeURL) + request.httpBody = try? JSONEncoder().encode(object) + + request.httpMethod = "POST" + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + + AF.request(request).response { (responce) in + + guard let code = responce.response?.statusCode else { return } + isValidTocken?(code) + + if code == 204 { + let fields = responce.response?.allHeaderFields as? [String :String] + + let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: responce.response!.url!) + + HTTPCookieStorage.shared.setCookie(cookies[0]) + + completion() + } + } + } + + // true only if everything is good + static func verify(with code: Int, completion: @escaping ((Bool)->())){ + var request = URLRequest(url: authVerifyURL) + request.httpMethod = "POST" + request.httpBody = "\(code)".data(using: .utf8) + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + + AF.request(request).response { (responce) in + + guard let statusCode = responce.response?.statusCode else { completion(false); return } + isValidTocken?(statusCode) + + if statusCode == 204 { + // at this point cookies are set + let fields = responce.response?.allHeaderFields as? [String :String] + + let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: responce.response!.url!) + + HTTPCookieStorage.shared.setCookie(cookies[0]) + + completion(true) + } + + completion(false) + } + } + + // yes or no + static func authenticateMe(completion: @escaping ((Bool)->())){ + var request = URLRequest(url: authMeURL) + request.httpMethod = "POST" + + AF.request(request).response { (responce) in + + guard let statusCode = responce.response?.statusCode else { completion(false); return } + isValidTocken?(statusCode) + + guard let json = responce.data else { return } + guard let personId = Int(String(data: json, encoding: String.Encoding.utf8)!) else { + completion(false) + return + } + + DataStorage.standard.setIsLoggedIn(value: true, with: personId) + completion(true) + } + } + + // register, ok or invalid + static func authenticate(with email: String, completion: @escaping ((AuthStatus)->())) { + + let json: [String:Any] = ["authenticationEmail" : email] let jsonData = try? JSONSerialization.data(withJSONObject: json) var request = URLRequest(url: authenticationURL) @@ -195,29 +311,27 @@ class Model{ request.httpBody = jsonData request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") - AF.request(request).response { (responce) in + AF.request(request).response { (response) in - guard let realResponse = responce.response, realResponse.statusCode == 200 else + guard let realResponse = response.response, realResponse.statusCode == 200 else { print("Not a 200 response") - completion(false) + completion(AuthStatus()) return } + + guard let json = response.data else { return } + guard let res = try? decoder.decode(AuthStatus.self, from: json) else { return } - guard let json = responce.data else { return } - guard let personId = Int(String(data: json, encoding: String.Encoding.utf8)!) else { - completion(false) - return + if realResponse.statusCode == 200 && res.userStatus == "registered" + { + // at this point cookies are set + let fields = realResponse.allHeaderFields as? [String :String] + let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: realResponse.url!) + HTTPCookieStorage.shared.setCookie(cookies[0]) } - let fields = realResponse.allHeaderFields as? [String :String] - - let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: realResponse.url!) - - HTTPCookieStorage.shared.setCookie(cookies[0]) - - DataStorage.standard.setIsLoggedIn(value: true, with: personId) - completion(true) + completion(res) } } @@ -855,4 +969,33 @@ class Model{ completion() } } + + struct Faculty: Codable + { + + var path: String + var url: String + var campusName: String + var campusCode: String + var name: String + var tags: [String] + + } + + static func searchFaculty(string: String, completion: @escaping (([Model.Faculty])->())) + { + var request = URLRequest(url: facultySearchURL) + request.httpMethod = "POST" + request.httpBody = string.data(using: .utf8) + request.setValue("text/plain;charset=utf-8", forHTTPHeaderField: "Content-Type") + + AF.request(request).response { (response) in + isValidTocken?(response.response?.statusCode ?? 498) + + guard let json = response.data else { return } + guard let faculties = try? decoder.decode([Faculty].self, from: json) else { return } + + completion(faculties) + } + } } From 5334c41bb98f048822f9aebca94466b413d902b4 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Sat, 11 May 2019 13:01:25 +0300 Subject: [PATCH 25/34] Working authentication --- GDproject/Base.lproj/Main.storyboard | 1 + .../FullPostController.swift | 2 +- .../Coordinators/MessagesCoordinator.swift | 11 +++++++---- .../Controller/Log In/CodeViewController.swift | 12 ++++++------ .../Log In/LogInViewController.swift | 2 +- .../Messages/Dialog/DialogViewController.swift | 2 +- .../Profile/ProfileViewController.swift | 18 ++++++++++++------ GDproject/Simple model/Model.swift | 11 ++++------- 8 files changed, 33 insertions(+), 26 deletions(-) diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 24b2369..8a6d0b0 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -286,6 +286,7 @@ + diff --git a/GDproject/Controller/ News and channels/FullPostController.swift b/GDproject/Controller/ News and channels/FullPostController.swift index e423cb9..e9b1aa4 100644 --- a/GDproject/Controller/ News and channels/FullPostController.swift +++ b/GDproject/Controller/ News and channels/FullPostController.swift @@ -25,7 +25,7 @@ class FullPostController: UITableViewController { func setUpNavigationBar(){ navigationItem.largeTitleDisplayMode = .never - navigationItem.title = "\(post?.authorId ?? 0)" + navigationItem.title = "Post" navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(self.options))] } diff --git a/GDproject/Controller/Coordinators/MessagesCoordinator.swift b/GDproject/Controller/Coordinators/MessagesCoordinator.swift index 3ab371e..7e81436 100644 --- a/GDproject/Controller/Coordinators/MessagesCoordinator.swift +++ b/GDproject/Controller/Coordinators/MessagesCoordinator.swift @@ -33,14 +33,14 @@ class MessagesCoordinator: BaseCoordinator { let newVC = self.storyboard.instantiateViewController(withIdentifier: peopleToWriteVC) as! PeopleToWriteViewController - newVC.whatToDoWithSelection = { [weak newVC, weak self] in + newVC.whatToDoWithSelection = { [weak newVC, weak self] people in newVC?.navigationController?.popViewController(animated: true) // detect is it a user or group dialog - let count = $0.count + let count = people.count if count == 1 { - let user = $0.first!.key + let user = people.first!.key let createdDialog = Model.Dialog.userChat(Model.UserChat(user: user)) let vc = DialogViewController() @@ -49,11 +49,14 @@ class MessagesCoordinator: BaseCoordinator { self?.navigationController?.pushViewController(vc, animated: true) } else { - var group = Model.Group(users: $0, name: "Untitled", id: 0) + var group = Model.Group(users: people, name: "Untitled", id: 0) + group.users[DataStorage.standard.getUserId()] = Model.UserPermission(isAdmin: true) + Model.createGroupChat(from: group, completion: { [weak self] id in let newVC1 = DialogViewController() group.id = id + newVC1.users = Model.Channels.fullPeopleDict newVC1.dialog = Model.Dialog.groupChat(Model.GroupChat(group: group)) self?.navigationController?.pushViewController(newVC1, animated: true) }) diff --git a/GDproject/Controller/Log In/CodeViewController.swift b/GDproject/Controller/Log In/CodeViewController.swift index 93519ec..68fef5d 100644 --- a/GDproject/Controller/Log In/CodeViewController.swift +++ b/GDproject/Controller/Log In/CodeViewController.swift @@ -23,12 +23,12 @@ class CodeViewController: UIViewController { super.viewDidLoad() setUpConstraint() - f1.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) - f2.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) - f3.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) - f4.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) - f5.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) - f6.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .touchUpInside) + f1.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) + f2.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) + f3.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) + f4.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) + f5.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) + f6.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) } override func viewWillAppear(_ animated: Bool) { diff --git a/GDproject/Controller/Log In/LogInViewController.swift b/GDproject/Controller/Log In/LogInViewController.swift index 090815a..22560e3 100644 --- a/GDproject/Controller/Log In/LogInViewController.swift +++ b/GDproject/Controller/Log In/LogInViewController.swift @@ -72,7 +72,7 @@ class LogInViewController: UIViewController { // configure keyboardBar components keyboardBar.addSubview(logInButton) logInButton.height(50) - logInButton.rightToSuperview(view.rightAnchor, offset: 16, relation: .equal, isActive: true) + logInButton.rightToSuperview(view.rightAnchor, offset: -16, relation: .equal, isActive: true) } diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift index 40a4ad4..0ce66a5 100644 --- a/GDproject/Controller/Messages/Dialog/DialogViewController.swift +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -253,7 +253,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat tabBarController?.tabBar.isHidden = true if let dialog = dialog { - getMessages(for: dialog) + getMessagesNew(for: dialog) } } diff --git a/GDproject/Controller/Profile/ProfileViewController.swift b/GDproject/Controller/Profile/ProfileViewController.swift index 42d115a..1e31831 100644 --- a/GDproject/Controller/Profile/ProfileViewController.swift +++ b/GDproject/Controller/Profile/ProfileViewController.swift @@ -48,15 +48,15 @@ class ProfileViewController: UIViewController var onChannelsListToAddAPerson: ((Model.Users)->())? - var protoDictionary: [Int: UIImage] = [9: #imageLiteral(resourceName: "9"), 5051: #imageLiteral(resourceName: "5051"), 69: #imageLiteral(resourceName: "69"), 42: #imageLiteral(resourceName: "42")] + var protoDictionary: [String: UIImage] = ["135213": #imageLiteral(resourceName: "9"), "135288": #imageLiteral(resourceName: "5051"), "22723" : #imageLiteral(resourceName: "69"), "135083": #imageLiteral(resourceName: "42")] func fill(with user: Model.Users) { - self.facultyLabel.text = "Студент: Факультет Компьютерных Наук" + self.facultyLabel.text = user.faculty.name self.nameLabel.text = "\(user.firstName) \(user.middleName)" self.surnameLabel.text = "\(user.lastName)" - self.profileImageView.image = protoDictionary[user.id]?.roundedImage - self.placeLabel.text = "📍Москва, Кочновский пр.3" + self.profileImageView.image = protoDictionary[user.faculty.campusCode]?.roundedImage + self.placeLabel.text = "📍\(user.faculty.campusName)" if user.id == DataStorage.standard.getUserId() { newMessageButton.isHidden = true } else { @@ -66,8 +66,14 @@ class ProfileViewController: UIViewController var user: Model.Users? { didSet { - self.fill(with: user!) - navigationItem.title = "\(user!.firstName) \(user!.lastName)" + if let user = user { + self.fill(with: user) + navigationItem.title = "\(user.firstName) \(user.lastName)" + } else if let id = idProfile { + Model.getUsers(for: [id]) { [unowned self ] in + self.user = $0[id] + } + } } } diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 4f136b9..432a40b 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -128,6 +128,8 @@ class Model{ var lastName: String var firstName: String var id: Int + var faculty: Faculty + var email: String func fullName() -> String { return "\(firstName) \(lastName)" @@ -264,12 +266,6 @@ class Model{ if statusCode == 204 { // at this point cookies are set - let fields = responce.response?.allHeaderFields as? [String :String] - - let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: responce.response!.url!) - - HTTPCookieStorage.shared.setCookie(cookies[0]) - completion(true) } @@ -462,6 +458,7 @@ class Model{ guard let users = try? decoder.decode([Users].self, from: json) else { return } var dict: [Int:Users] = [:] + users.forEach({ (user) in dict[user.id] = user }) @@ -657,7 +654,7 @@ class Model{ guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return } - // idUser = newQueery.users + print("\(anonymousChannel) \(newQueery)") completion((newQueery.users, newQueery.response)) } } From a733f641a7428aab75b4bdb0cfe3afeb985b0e5f Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Sat, 11 May 2019 19:21:40 +0300 Subject: [PATCH 26/34] Added alerts for log in and registration --- GDproject/Base.lproj/Main.storyboard | 12 ++-- .../ News and channels/PostViewCell.swift | 1 + .../Coordinators/LogInCoordinator.swift | 3 +- .../Log In/CodeViewController.swift | 55 ++++++++++++++++--- .../Log In/LogInViewController.swift | 25 ++++++++- .../Log In/RegisterTableViewController.swift | 15 ++++- .../Profile/ProfileViewController.swift | 2 +- GDproject/DataStorage.swift | 8 +++ GDproject/Simple model/Model.swift | 5 +- 9 files changed, 105 insertions(+), 21 deletions(-) diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 8a6d0b0..5e3ac13 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -248,37 +248,37 @@ - + - + - + - + - + - + diff --git a/GDproject/Controller/ News and channels/PostViewCell.swift b/GDproject/Controller/ News and channels/PostViewCell.swift index 505532e..ad2ddcb 100644 --- a/GDproject/Controller/ News and channels/PostViewCell.swift +++ b/GDproject/Controller/ News and channels/PostViewCell.swift @@ -42,6 +42,7 @@ class PostViewCell: UITableViewCell let shareButton: UIButton = { let button = UIButton(type: UIButton.ButtonType.detailDisclosure) + button.isHidden = true return button }() diff --git a/GDproject/Controller/Coordinators/LogInCoordinator.swift b/GDproject/Controller/Coordinators/LogInCoordinator.swift index 1b2665c..e58e6c0 100644 --- a/GDproject/Controller/Coordinators/LogInCoordinator.swift +++ b/GDproject/Controller/Coordinators/LogInCoordinator.swift @@ -26,13 +26,14 @@ class LogInCoordinator: BaseCoordinator { let logInVC = LogInViewController() logInVC.authenticate = { [weak self, weak logInVC] (email) in + DataStorage.standard.setEmail(email: email) Model.authenticate(with: email) { [weak self] (authStatus) in print(authStatus.userStatus) if (authStatus.userStatus == "invalid") { - logInVC?.mailTextField.text = "invalid" + logInVC?.presentAlertInvalidCode() } else if (authStatus.userStatus == "canRegister") { // register form self?.presentRegisterVC() diff --git a/GDproject/Controller/Log In/CodeViewController.swift b/GDproject/Controller/Log In/CodeViewController.swift index 68fef5d..ac2b60c 100644 --- a/GDproject/Controller/Log In/CodeViewController.swift +++ b/GDproject/Controller/Log In/CodeViewController.swift @@ -7,6 +7,7 @@ // import UIKit +import TinyConstraints class CodeViewController: UIViewController { @@ -17,12 +18,16 @@ class CodeViewController: UIViewController { @IBOutlet weak var f5: UITextField! @IBOutlet weak var f6: UITextField! + var loading = UIActivityIndicatorView(style: .gray) + var onSuccessLogIn: (()->())? override func viewDidLoad() { super.viewDidLoad() setUpConstraint() + navigationItem.title = DataStorage.standard.getEmail() + f1.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) f2.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) f3.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) @@ -31,6 +36,12 @@ class CodeViewController: UIViewController { f6.addTarget(self, action: #selector(textFiledDidChange(textField:)), for: .editingChanged) } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + loading.stopAnimating() + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -62,28 +73,56 @@ class CodeViewController: UIViewController { } + func presentAlertInvalidData(with text: String) + { + let alert = UIAlertController(title: "Invalid code", message: text, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) + self.present(alert, animated: true, completion: nil) + + loading.stopAnimating() + } + @IBOutlet weak var bottomConstraint: NSLayoutConstraint! @IBAction func whereToGoNext(_ sender: UIButton) { + guard let f1t = f1.text, let f2t = f2.text, let f3t = f3.text, let f4t = f4.text, let f5t = f5.text, let f6t = f6.text else { + presentAlertInvalidData(with: "Some fields are missing"); + return + } + + if f1t.isEmpty || f2t.isEmpty || f3t.isEmpty || f4t.isEmpty || f5t.isEmpty || f6t.isEmpty { + presentAlertInvalidData(with: "Some fields are missing"); + return + } + let code = "\(f1.text!)\(f2.text!)\(f3.text!)\(f4.text!)\(f5.text!)\(f6.text!)" if let codeToInt = Int(code) { - Model.verify(with: codeToInt) { _ in + loading.startAnimating() + Model.verify(with: codeToInt) { [weak self] in // if everything is okay we can authemticicate - // Model.authemticiateMe - Model.authenticateMe { [weak self] (res) in - if res { - self?.onSuccessLogIn?() - } else { - print("sosi") + if !$0 { + self?.presentAlertInvalidData(with: "Wrong code. Try again!") + self?.loading.stopAnimating() + return + } else { + // Model.authemticiateMe + Model.authenticateMe { [weak self] (res) in + if res { + self?.onSuccessLogIn?() + } } + return } } } } - func setUpConstraint(){ + func setUpConstraint() + { + self.view.addSubview(loading) + loading.centerInSuperview() // for keyboard notifications NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotifications), name: UIResponder.keyboardWillShowNotification, object: nil) diff --git a/GDproject/Controller/Log In/LogInViewController.swift b/GDproject/Controller/Log In/LogInViewController.swift index 22560e3..801c009 100644 --- a/GDproject/Controller/Log In/LogInViewController.swift +++ b/GDproject/Controller/Log In/LogInViewController.swift @@ -14,8 +14,25 @@ import Result class LogInViewController: UIViewController { + var loading = UIActivityIndicatorView(style: .gray) + var authenticate: ((String)->())? + func presentAlertInvalidCode() + { + let alert = UIAlertController(title: "Invalid email", message: "Try again. Use @hse.ru mail.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) + self.present(alert, animated: true, completion: nil) + mailTextField.text = "" + loading.stopAnimating() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + loading.stopAnimating() + } + let logInLabel: UILabel = { let label = UILabel() label.text = "Log In" @@ -45,6 +62,7 @@ class LogInViewController: UIViewController { }() @objc func handleTap(){ + loading.startAnimating() authenticate?(mailTextField.text!) } @@ -55,6 +73,10 @@ class LogInViewController: UIViewController { func setUpView(){ // logIn stack with textField and label view.addSubview(contentView) + view.addSubview(loading) + + loading.centerInSuperview() + contentView.edgesToSuperview(excluding: .bottom, insets: .left(16) + .right(16) + .top(80), usingSafeArea: true) let views = [logInLabel, mailTextField] contentView.stack(views, axis: .vertical, spacing: 10) @@ -62,6 +84,7 @@ class LogInViewController: UIViewController { func configureKeyboard(){ + mailTextField.keyboardType = .emailAddress // configure keyboardBar setUp view.addSubview(keyboardBar) keyboardBar.height(50) @@ -79,7 +102,7 @@ class LogInViewController: UIViewController { func logicOfLogInInputValidation() -> ((String?)->()) { let logic: ((String?)->()) = { [weak self] (someText) in - if let text = someText, !text.isEmpty { + if let text = someText, !text.isEmpty, text.contains("@") { self?.logInButton.isEnabled = true } else { self?.logInButton.isEnabled = false diff --git a/GDproject/Controller/Log In/RegisterTableViewController.swift b/GDproject/Controller/Log In/RegisterTableViewController.swift index 5bc0ada..68fe90a 100644 --- a/GDproject/Controller/Log In/RegisterTableViewController.swift +++ b/GDproject/Controller/Log In/RegisterTableViewController.swift @@ -11,6 +11,13 @@ import UIKit class RegisterTableViewController: UITableViewController, ChosenFactulty { + func presentAlertInvalidData() + { + let alert = UIAlertController(title: "Invalid data", message: "Some fields are missing. Add more information", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) + self.present(alert, animated: true, completion: nil) + } + func onChooseFaculty(faculty: Model.Faculty) { self.faculty = faculty tableView.reloadData() @@ -20,7 +27,7 @@ class RegisterTableViewController: UITableViewController, ChosenFactulty var onRegistration: (()->())? var faculty: Model.Faculty? - var user: Model.NewRegistration = Model.NewRegistration(email: "dyurednikina@edu.hse.ru", firstName: nil, middleName: nil, lastName: nil, faculty: nil) + var user: Model.NewRegistration = Model.NewRegistration(email: DataStorage.standard.getEmail()!, firstName: nil, middleName: nil, lastName: nil, faculty: nil) override func viewDidLoad() { @@ -34,10 +41,14 @@ class RegisterTableViewController: UITableViewController, ChosenFactulty @objc func endRegistration() { updateUser() - guard let faculty = faculty else { return } + guard let faculty = faculty else { presentAlertInvalidData(); return } user.faculty = faculty.url + guard let _ = user.firstName else { presentAlertInvalidData(); return } + guard let _ = user.middleName else { presentAlertInvalidData(); return } + guard let _ = user.lastName else { presentAlertInvalidData(); return } + Model.register(object: user) { [weak self] in self?.onRegistration?() diff --git a/GDproject/Controller/Profile/ProfileViewController.swift b/GDproject/Controller/Profile/ProfileViewController.swift index 1e31831..c81bd3b 100644 --- a/GDproject/Controller/Profile/ProfileViewController.swift +++ b/GDproject/Controller/Profile/ProfileViewController.swift @@ -56,7 +56,7 @@ class ProfileViewController: UIViewController self.nameLabel.text = "\(user.firstName) \(user.middleName)" self.surnameLabel.text = "\(user.lastName)" self.profileImageView.image = protoDictionary[user.faculty.campusCode]?.roundedImage - self.placeLabel.text = "📍\(user.faculty.campusName)" + self.placeLabel.text = "📍\(user.faculty.address)" if user.id == DataStorage.standard.getUserId() { newMessageButton.isHidden = true } else { diff --git a/GDproject/DataStorage.swift b/GDproject/DataStorage.swift index 98a1bb8..b65b2d1 100644 --- a/GDproject/DataStorage.swift +++ b/GDproject/DataStorage.swift @@ -37,6 +37,13 @@ class DataStorage{ return UserDefaults.standard.integer(forKey: UserDefaultsKeys.id.rawValue) } + func setEmail(email: String) { + UserDefaults.standard.set(email, forKey: UserDefaultsKeys.email.rawValue) + } + + func getEmail() -> String? { + return UserDefaults.standard.string(forKey: UserDefaultsKeys.email.rawValue) + } /** Function to determine is user logged in already or not */ @@ -49,4 +56,5 @@ class DataStorage{ enum UserDefaultsKeys: String{ case loggedIn case id + case email } diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index 432a40b..c6c2467 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -267,9 +267,9 @@ class Model{ if statusCode == 204 { // at this point cookies are set completion(true) + } else { + completion(false) } - - completion(false) } } @@ -976,6 +976,7 @@ class Model{ var campusCode: String var name: String var tags: [String] + var address: String } From 525bc7c1c52b7b2baa590c1485e8a3da72d543c8 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Sat, 11 May 2019 22:50:45 +0300 Subject: [PATCH 27/34] fixed a few bugs --- .../Log In/FacultyTableViewController.swift | 1 + .../Log In/LogInViewController.swift | 2 + GDproject/Simple model/Model.swift | 91 ++++++++++++++++++- 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/GDproject/Controller/Log In/FacultyTableViewController.swift b/GDproject/Controller/Log In/FacultyTableViewController.swift index 3b995fe..50e6905 100644 --- a/GDproject/Controller/Log In/FacultyTableViewController.swift +++ b/GDproject/Controller/Log In/FacultyTableViewController.swift @@ -81,6 +81,7 @@ class FacultyTableViewController: UITableViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { delegate?.onChooseFaculty(faculty: currentFaculties[indexPath.row]) + searchController.dismiss(animated: true, completion: nil) self.navigationController?.popViewController(animated: true) } diff --git a/GDproject/Controller/Log In/LogInViewController.swift b/GDproject/Controller/Log In/LogInViewController.swift index 801c009..08d5f00 100644 --- a/GDproject/Controller/Log In/LogInViewController.swift +++ b/GDproject/Controller/Log In/LogInViewController.swift @@ -47,6 +47,8 @@ class LogInViewController: UIViewController { textField.placeholder = "Mail" textField.borderStyle = .roundedRect textField.textColor = .black + textField.autocorrectionType = UITextAutocorrectionType.no + textField.autocapitalizationType = UITextAutocapitalizationType.none textField.clearButtonMode = .always return textField }() diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index c6c2467..ef9e1df 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -17,12 +17,14 @@ class Model{ static var hashTagTree: CompletionTree? private static var isValidTocken: ((Int)->())? = { responce in - print(responce) + if responce == invalidTocken { + + DataStorage.standard.setIsLoggedIn(value: false, with: 0) + (UIApplication.shared.delegate as! AppDelegate).relaunch() + Model.logout { print("Logout Is Successful") - DataStorage.standard.setIsLoggedIn(value: false, with: 0) - (UIApplication.shared.delegate as! AppDelegate).relaunch() } } } @@ -208,6 +210,10 @@ class Model{ AF.request(request).response { (responce) in isValidTocken?(responce.response?.statusCode ?? 498) + if let code = responce.response?.statusCode, code == 498 { + return + } + completion() } } @@ -240,6 +246,10 @@ class Model{ guard let code = responce.response?.statusCode else { return } isValidTocken?(code) + if let code = responce.response?.statusCode, code == 498 { + return + } + if code == 204 { let fields = responce.response?.allHeaderFields as? [String :String] @@ -249,6 +259,7 @@ class Model{ completion() } + } } @@ -264,6 +275,10 @@ class Model{ guard let statusCode = responce.response?.statusCode else { completion(false); return } isValidTocken?(statusCode) + if let code = responce.response?.statusCode, code == 498 { + return + } + if statusCode == 204 { // at this point cookies are set completion(true) @@ -283,6 +298,10 @@ class Model{ guard let statusCode = responce.response?.statusCode else { completion(false); return } isValidTocken?(statusCode) + if let code = responce.response?.statusCode, code == 498 { + return + } + guard let json = responce.data else { return } guard let personId = Int(String(data: json, encoding: String.Encoding.utf8)!) else { completion(false) @@ -377,6 +396,10 @@ class Model{ isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } + guard let json = response.data else { return } guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { print("no") @@ -402,6 +425,10 @@ class Model{ (response) in isValidTocken?(response.response?.statusCode ?? 498) + + if let code = response.response?.statusCode, code == 498 { + return + } } } @@ -427,6 +454,10 @@ class Model{ isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } + guard let json = response.data else { return } guard let newPost = try? decoder.decode(QueryPosts.self, from: json) else { return } @@ -453,6 +484,10 @@ class Model{ isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } + guard let json = response.data else { return } guard let users = try? decoder.decode([Users].self, from: json) else { return } @@ -485,6 +520,9 @@ class Model{ isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } guard let json = response.data else { return } @@ -507,6 +545,9 @@ class Model{ (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } } } @@ -519,6 +560,9 @@ class Model{ AF.request(request).responseJSON { (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } guard let json = response.data else { return } @@ -537,6 +581,9 @@ class Model{ AF.request(request).response { (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } } completion() } @@ -552,6 +599,9 @@ class Model{ (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } } } @@ -574,6 +624,9 @@ class Model{ } isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } } } @@ -649,6 +702,9 @@ class Model{ (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } guard let json = response.data else { return } @@ -664,6 +720,9 @@ class Model{ AF.request(URLRequest(url: hashTagTreeURL)).responseJSON { (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } guard let json = response.data else { return } guard let tree = try? decoder.decode(CompletionTree.self, from: json) else { return } completion(tree) @@ -681,6 +740,10 @@ class Model{ AF.request(request).responseJSON { response in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } + guard let json = response.data else { return } let dialogs = try! decoder.decode(QueryPosts.self, from: json) @@ -852,6 +915,9 @@ class Model{ AF.request(request).response { (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } guard let json = response.data else { return } let stringInt = String.init(data: json, encoding: String.Encoding.utf8) @@ -880,6 +946,9 @@ class Model{ AF.request(request!).response { (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } guard let json = response.data else { return } guard let messages = try? decoder.decode([LastMessage].self, from: json) else { return } @@ -897,6 +966,10 @@ class Model{ AF.request(request).response { (response) in isValidTocken?(response.response?.statusCode ?? 498) + + if let code = response.response?.statusCode, code == 498 { + return + } } completion() @@ -911,6 +984,10 @@ class Model{ AF.request(request).response { (response) in isValidTocken?(response.response?.statusCode ?? 498) + + if let code = response.response?.statusCode, code == 498 { + return + } } // completion() @@ -963,6 +1040,10 @@ class Model{ AF.request(request).response { (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } + completion() } } @@ -990,6 +1071,10 @@ class Model{ AF.request(request).response { (response) in isValidTocken?(response.response?.statusCode ?? 498) + if let code = response.response?.statusCode, code == 498 { + return + } + guard let json = response.data else { return } guard let faculties = try? decoder.decode([Faculty].self, from: json) else { return } From 5ca9bbb7f8657de3a0926bcaf17ba7b37c0fb84b Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Mon, 13 May 2019 12:30:50 +0300 Subject: [PATCH 28/34] Alerts and more --- .../ChannelController.swift | 2 +- .../ChannelListController.swift | 2 +- .../ChannelViewController.swift | 6 +- .../FullPostController.swift | 19 +---- .../NewPostViewController.swift | 3 +- .../ News and channels/NewsController.swift | 1 + .../Coordinators/ProfileCoordinator.swift | 30 +++---- .../Log In/RegisterTableViewController.swift | 63 +++++++++++--- .../Dialog/DialogViewController.swift | 3 +- .../Messages/MessagesViewController.swift | 4 +- .../Profile/BasicInfoController.swift | 27 +++--- GDproject/Controller/Profile/InfoCell.swift | 3 +- .../Profile/ProfileViewController.swift | 84 ++++++++++++++----- GDproject/Extentions.swift | 30 +++++++ GDproject/Simple model/Model.swift | 72 ++++++++++++++-- 15 files changed, 257 insertions(+), 92 deletions(-) diff --git a/GDproject/Controller/ News and channels/ChannelController.swift b/GDproject/Controller/ News and channels/ChannelController.swift index 66182c6..60e6f42 100644 --- a/GDproject/Controller/ News and channels/ChannelController.swift +++ b/GDproject/Controller/ News and channels/ChannelController.swift @@ -73,7 +73,7 @@ class ChannelController: UIViewController, UITableViewDelegate, UITableViewDataS if let _ = channel?.id { print("update") Model.updateChannel(with: channel!) } else { print("create") - Model.createChannel(with: channel!) + //Model.createChannel(with: channel!) } return } diff --git a/GDproject/Controller/ News and channels/ChannelListController.swift b/GDproject/Controller/ News and channels/ChannelListController.swift index dcdd2a0..a0701cf 100644 --- a/GDproject/Controller/ News and channels/ChannelListController.swift +++ b/GDproject/Controller/ News and channels/ChannelListController.swift @@ -30,7 +30,7 @@ class ChannelListController: UITableViewController { var displayingChannel: Model.Channels? var toReload: Bool = false { - didSet{ + didSet { tableView.reloadData() } } diff --git a/GDproject/Controller/ News and channels/ChannelViewController.swift b/GDproject/Controller/ News and channels/ChannelViewController.swift index c61cb93..226b103 100644 --- a/GDproject/Controller/ News and channels/ChannelViewController.swift +++ b/GDproject/Controller/ News and channels/ChannelViewController.swift @@ -65,7 +65,11 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan { Model.updateChannel(with: channel!) } else { - Model.createChannel(with: channel!) + Model.createChannel(with: channel!) { + [weak self] in + self?.showAlertOn(result: $0) + return + } } return } diff --git a/GDproject/Controller/ News and channels/FullPostController.swift b/GDproject/Controller/ News and channels/FullPostController.swift index e9b1aa4..14151d9 100644 --- a/GDproject/Controller/ News and channels/FullPostController.swift +++ b/GDproject/Controller/ News and channels/FullPostController.swift @@ -26,26 +26,9 @@ class FullPostController: UITableViewController { func setUpNavigationBar(){ navigationItem.largeTitleDisplayMode = .never navigationItem.title = "Post" - navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(self.options))] } - @objc func options(){ - // drafts - // saved - - let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - let editAction = UIAlertAction(title: "Send via message", style: .default) - let shareAction = UIAlertAction(title: "Send via project", style: .default) - let settingsAction = UIAlertAction(title: "Copy link", style: .default) - let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) - - optionMenu.addAction(editAction) - optionMenu.addAction(shareAction) - optionMenu.addAction(settingsAction) - optionMenu.addAction(cancelAction) - - self.present(optionMenu, animated: true, completion: nil) - } + // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { diff --git a/GDproject/Controller/ News and channels/NewPostViewController.swift b/GDproject/Controller/ News and channels/NewPostViewController.swift index 18d2ef1..00780c8 100644 --- a/GDproject/Controller/ News and channels/NewPostViewController.swift +++ b/GDproject/Controller/ News and channels/NewPostViewController.swift @@ -109,7 +109,8 @@ class NewPostViewController: UIViewController, UITextViewDelegate, TagsReceiver override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - navigationController?.navigationBar.prefersLargeTitles = false + //navigationController?.navigationBar.prefersLargeTitles = false + navigationItem.largeTitleDisplayMode = .never navigationItem.title = "New post" textView.text = NewPostViewController.draft // tagsField.addTags(NewPostViewController.hashTagsDraft) diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index da86a19..78521ec 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -52,6 +52,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi let updater = TagsSuggestionController() updater.delegate = self searchController = UISearchController(searchResultsController: updater) + navigationItem.largeTitleDisplayMode = .always navigationController?.navigationBar.prefersLargeTitles = true navigationItem.title = "Loading ..." diff --git a/GDproject/Controller/Coordinators/ProfileCoordinator.swift b/GDproject/Controller/Coordinators/ProfileCoordinator.swift index 75c0e25..1a18aca 100644 --- a/GDproject/Controller/Coordinators/ProfileCoordinator.swift +++ b/GDproject/Controller/Coordinators/ProfileCoordinator.swift @@ -36,23 +36,25 @@ class ProfileCoordinator: BaseCoordinator { } } - profile.onSettings = { [weak self, weak storyboard] in - let vc = storyboard?.instantiateViewController(withIdentifier: "SettingsViewController") as! SettingsViewController + profile.deleteAllSessions = { + Model.deactivateAll() { + [weak self] in + + DataStorage.standard.setIsLoggedIn(value: false, with: 0) + self?.didEndSession?() + } + } + + profile.onSettings = { [weak self, weak storyboard, weak profile] in + + let vc = storyboard?.instantiateViewController(withIdentifier: "RegisterTableViewController") as! RegisterTableViewController + vc.delegate = profile + vc.userActive = profile?.user + self?.navigationController?.pushViewController(vc, animated: true) } - profile.onChannelsListToAddAPerson = { [weak self, weak storyboard] (user) in - let vc = storyboard?.instantiateViewController(withIdentifier: simplifiedChannelsList) as! SimplifiedChannelsList - vc.user = user - - let transition = CATransition() - transition.duration = 0.5 - transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) - transition.type = CATransitionType.moveIn - transition.subtype = CATransitionSubtype.fromTop - self?.navigationController?.view.layer.add(transition, forKey: nil) - self?.navigationController?.pushViewController(vc, animated: false) - } + navigationController?.setViewControllers([profile], animated: false) } } diff --git a/GDproject/Controller/Log In/RegisterTableViewController.swift b/GDproject/Controller/Log In/RegisterTableViewController.swift index 68fe90a..20f31d5 100644 --- a/GDproject/Controller/Log In/RegisterTableViewController.swift +++ b/GDproject/Controller/Log In/RegisterTableViewController.swift @@ -11,9 +11,9 @@ import UIKit class RegisterTableViewController: UITableViewController, ChosenFactulty { - func presentAlertInvalidData() + func presentAlertInvalidData(message: String) { - let alert = UIAlertController(title: "Invalid data", message: "Some fields are missing. Add more information", preferredStyle: .alert) + let alert = UIAlertController(title: "Invalid data", message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(alert, animated: true, completion: nil) } @@ -23,12 +23,29 @@ class RegisterTableViewController: UITableViewController, ChosenFactulty tableView.reloadData() } - + weak var delegate: UpdateUser? + var onRegistration: (()->())? var faculty: Model.Faculty? var user: Model.NewRegistration = Model.NewRegistration(email: DataStorage.standard.getEmail()!, firstName: nil, middleName: nil, lastName: nil, faculty: nil) + var userActive: Model.Users? { + didSet { + if let userA = userActive { + user.firstName = userA.firstName + user.middleName = userA.middleName + user.lastName = userA.lastName + + faculty = userA.faculty + self.updateUserIfCan = true + tableView.reloadData() + } + } + } + + var updateUserIfCan: Bool = false + override func viewDidLoad() { super.viewDidLoad() @@ -38,22 +55,46 @@ class RegisterTableViewController: UITableViewController, ChosenFactulty navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(endRegistration)) } + @objc func endRegistration() { + let fieldsMissing = "Some fields are missing. Add more information" updateUser() - guard let faculty = faculty else { presentAlertInvalidData(); return } + guard let faculty = faculty else { presentAlertInvalidData(message: fieldsMissing); return } user.faculty = faculty.url - guard let _ = user.firstName else { presentAlertInvalidData(); return } - guard let _ = user.middleName else { presentAlertInvalidData(); return } - guard let _ = user.lastName else { presentAlertInvalidData(); return } + guard let name = user.firstName else { presentAlertInvalidData(message: fieldsMissing); return } + guard let middle = user.middleName else { presentAlertInvalidData(message: fieldsMissing); return } + guard let last = user.lastName else { presentAlertInvalidData(message: fieldsMissing); return } - Model.register(object: user) - { [weak self] in - self?.onRegistration?() + if updateUserIfCan { + guard let delegate = delegate else { return } + guard var userA = userActive else { return } + + // updating current + userA.firstName = name + userA.faculty = faculty + userA.middleName = middle + userA.lastName = last + + Model.userUpdate(with: user) { [weak self] in + + if $0 { + delegate.updateUserObj(with: userA) + self?.navigationController?.popViewController(animated: true) + } else { + self?.presentAlertInvalidData(message: "Something went wrong! Try again") + } + + } + } else { + + Model.register(object: user) + { [weak self] in + self?.onRegistration?() + } } - } // MARK: - Table view data source diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift index 0ce66a5..c0de30d 100644 --- a/GDproject/Controller/Messages/Dialog/DialogViewController.swift +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -248,7 +248,8 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat return cellData.count } - override func viewWillAppear(_ animated: Bool) { + override func viewWillAppear(_ animated: Bool) + { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true diff --git a/GDproject/Controller/Messages/MessagesViewController.swift b/GDproject/Controller/Messages/MessagesViewController.swift index 6993827..60f7007 100644 --- a/GDproject/Controller/Messages/MessagesViewController.swift +++ b/GDproject/Controller/Messages/MessagesViewController.swift @@ -70,10 +70,10 @@ class MessagesViewController: UITableViewController { case .groupChat(let group): cell.textLabel?.text = group.group.name - cell.detailTextLabel?.text = group.lastMessage!.body.markdown + cell.detailTextLabel?.text = group.lastMessage?.body.markdown case .userChat(let userChat): cell.textLabel?.text = "👤 \(users[userChat.user]!.fullName())" - cell.detailTextLabel?.text = userChat.lastMessage!.body.markdown + cell.detailTextLabel?.text = userChat.lastMessage?.body.markdown } return cell diff --git a/GDproject/Controller/Profile/BasicInfoController.swift b/GDproject/Controller/Profile/BasicInfoController.swift index 6735aa1..05ccba7 100644 --- a/GDproject/Controller/Profile/BasicInfoController.swift +++ b/GDproject/Controller/Profile/BasicInfoController.swift @@ -16,17 +16,20 @@ struct CellData{ class BasicInfoController: UIViewController, UITableViewDelegate, UITableViewDataSource { - var dataSourse: [CellData] = [ - CellData(opened: false, title: "Phone", sectionData: ["+7 901 733 01 79"]), - CellData(opened: false, title: "Mail", sectionData: ["vbogomazova@edu.hse.ru"]), - CellData(opened: false, title: "Research ID", sectionData: ["4567834336789456737"]), - CellData(opened: false, title: "Published", sectionData: ["Это будет очень длинное название книги 1, чтобы сделать проверку", "Книга 2", "Книга 3"]), - CellData(opened: false, title: "Courses", sectionData: ["Курс 1", "Курс 2"]), - CellData(opened: false, title: "Link", sectionData: ["https://wwww.hse.ru"]) - ] + var userInfo: Model.Users? { + didSet { + if let userInfo = userInfo { + dataSourse = [CellData(opened: false, title: "Contacts", sectionData: [userInfo.email, userInfo.faculty.address]), + CellData(opened: false, title: "Faculty", sectionData: [userInfo.faculty.campusName, userInfo.faculty.name, userInfo.faculty.address, userInfo.faculty.path]), + CellData(opened: false, title: "Interests", sectionData: userInfo.faculty.tags)] + } + } + } + + var dataSourse: [CellData] = [ ] func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if (dataSourse[section].opened){ + if (dataSourse[section].opened) { return dataSourse[section].sectionData.count + 1 } else { return 1 @@ -45,8 +48,10 @@ class BasicInfoController: UIViewController, UITableViewDelegate, UITableViewDat cell.selectionStyle = .none return cell } else { - let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) - cell.textLabel?.text = dataSourse[indexPath.section].sectionData[indexPath.row - 1] + let cell = tableView.dequeueReusableCell(withIdentifier: infoCellId, for: indexPath) as! InfoCell + cell.fill(title: dataSourse[indexPath.section].sectionData[indexPath.row - 1]) + cell.accessoryType = .none + cell.selectionStyle = .none return cell } } diff --git a/GDproject/Controller/Profile/InfoCell.swift b/GDproject/Controller/Profile/InfoCell.swift index 70823d6..8918914 100644 --- a/GDproject/Controller/Profile/InfoCell.swift +++ b/GDproject/Controller/Profile/InfoCell.swift @@ -16,8 +16,9 @@ class InfoCell: UITableViewCell{ label.font = UIFont.systemFont(ofSize: 16) label.sizeToFit() label.isScrollEnabled = false - label.isUserInteractionEnabled = false + // label.isUserInteractionEnabled = false label.isEditable = false + label.dataDetectorTypes = .all label.textColor = .black return label }() diff --git a/GDproject/Controller/Profile/ProfileViewController.swift b/GDproject/Controller/Profile/ProfileViewController.swift index c81bd3b..f5ce1cf 100644 --- a/GDproject/Controller/Profile/ProfileViewController.swift +++ b/GDproject/Controller/Profile/ProfileViewController.swift @@ -9,8 +9,17 @@ import UIKit import TinyConstraints -class ProfileViewController: UIViewController +protocol UpdateUser: class { + func updateUserObj(with user: Model.Users) +} + +class ProfileViewController: UIViewController, UpdateUser { + func updateUserObj(with user: Model.Users) + { + self.user = user + Model.Channels.fullPeopleDict[user.id] = user + } @IBOutlet weak var segmentedControl: UISegmentedControl! @@ -32,6 +41,8 @@ class ProfileViewController: UIViewController var logOut: (()->())? + var deleteAllSessions: (()->())? + var onSettings: (()->())? @IBAction func sendMessage(_ sender: UIButton) @@ -46,8 +57,6 @@ class ProfileViewController: UIViewController } } - var onChannelsListToAddAPerson: ((Model.Users)->())? - var protoDictionary: [String: UIImage] = ["135213": #imageLiteral(resourceName: "9"), "135288": #imageLiteral(resourceName: "5051"), "22723" : #imageLiteral(resourceName: "69"), "135083": #imageLiteral(resourceName: "42")] func fill(with user: Model.Users) @@ -106,6 +115,15 @@ class ProfileViewController: UIViewController posts.type = .NONE posts.currChannel = channel + posts.onFullPost = { + [weak self] (type,post) in + + let vc = self?.storyboard?.instantiateViewController(withIdentifier: fullPostControllerId) as! FullPostController + vc.type = type + vc.post = post + self?.navigationController!.pushViewController(vc, animated: true) + } + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId) @@ -151,29 +169,52 @@ class ProfileViewController: UIViewController let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - let channelAction = UIAlertAction(title: "Add to a channel", style: .default){ - [weak self] (_) in - self?.onChannelsListToAddAPerson?(self!.user!) - } - - let settingsAction = UIAlertAction(title: "Setting", style: .default) - { [weak self] (_) in - self?.onSettings?() - } - + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) - let logoutAction = UIAlertAction(title: "Log out", style: .destructive) - { [weak self] - (_) in - self?.logOut?() + + if idProfile == DataStorage.standard.getUserId() { + let logoutAction = UIAlertAction(title: "Log out", style: .destructive) + { [weak self] + (_) in + self?.logOut?() + } + + let deleetAllSessionsAction = UIAlertAction(title: "Delete all sessions", style: .destructive) + { [weak self] + (_) in + self?.deleteAllSessions?() + } + + let settingsAction = UIAlertAction(title: "Edit profile", style: .default) + { [weak self] (_) in + self?.onSettings?() + } + + optionMenu.addAction(settingsAction) + optionMenu.addAction(logoutAction) + optionMenu.addAction(deleetAllSessionsAction) + } else { + let channelAction = UIAlertAction(title: "Add to a channel", style: .default) + { + [weak self] (_) in + + let vc = self?.storyboard?.instantiateViewController(withIdentifier: simplifiedChannelsList) as! SimplifiedChannelsList + vc.user = self?.user + + let transition = CATransition() + transition.duration = 0.5 + transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) + transition.type = CATransitionType.moveIn + transition.subtype = CATransitionSubtype.fromTop + self?.navigationController?.view.layer.add(transition, forKey: nil) + self?.navigationController?.pushViewController(vc, animated: false) + } + + optionMenu.addAction(channelAction) } - optionMenu.addAction(channelAction) - optionMenu.addAction(settingsAction) - optionMenu.addAction(logoutAction) optionMenu.addAction(cancelAction) - self.present(optionMenu, animated: true, completion: nil) } @@ -203,6 +244,7 @@ class ProfileViewController: UIViewController func changeToBasicInfo(){ tableView.delegate = basicInfo tableView.dataSource = basicInfo + basicInfo.userInfo = self.user tableView.reloadData() } } diff --git a/GDproject/Extentions.swift b/GDproject/Extentions.swift index fe9738c..473a177 100644 --- a/GDproject/Extentions.swift +++ b/GDproject/Extentions.swift @@ -77,3 +77,33 @@ extension String { return timeStamp } } + +extension UIViewController { + func showAlertOn(result: ResultR) { + + let message: String + + switch result { + case .impossibleContent: + message = "Impossible content" + case .exceededContent: + message = "The content exceeded" + case .longContent: + message = "The content is too long" + case .badAccess: + message = "Try reloading the page again" + case .alreadyRegistered: + message = "User is already registered" + case .tooMuchToAdd: + message = "Limit of channels exceeded" + case .success, .success1: + return + default: + message = "Something went wrong" + } + + let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) + self.present(alert, animated: true, completion: nil) + } +} diff --git a/GDproject/Simple model/Model.swift b/GDproject/Simple model/Model.swift index ef9e1df..fb03b92 100644 --- a/GDproject/Simple model/Model.swift +++ b/GDproject/Simple model/Model.swift @@ -10,6 +10,22 @@ import Foundation import UIKit import Alamofire +enum ResultR: Int { + case internalServerError = 500 + case exceededContent = 470 + case longContent = 471 + case incorrectContent = 472 + case impossibleContent = 473 + case invalidTocken = 498 + case alreadyRegistered = 409 + case invalidCode = 403 + case badAccess = 400 + case tooMuchToAdd = 406 + case notFound = 404 + case success1 = 204 + case success = 200 +} + class Model{ static let invalidTocken = 498 @@ -17,7 +33,7 @@ class Model{ static var hashTagTree: CompletionTree? private static var isValidTocken: ((Int)->())? = { responce in - + print(responce) if responce == invalidTocken { DataStorage.standard.setIsLoggedIn(value: false, with: 0) @@ -33,6 +49,7 @@ class Model{ static let decoder = JSONDecoder() static let authMeURL = URL(string: "\(baseUrl)/authentication/me")! + static let deactivateURL = URL(string: "\(baseUrl)/deactivateAll")! static let registerMeURL = URL(string: "\(baseUrl)/authentication/register")! static let authVerifyURL = URL(string: "\(baseUrl)/authentication/verify")! static let authenticationURL = URL(string:"\(baseUrl)/authentication/login")! @@ -42,6 +59,7 @@ class Model{ static let postsPublishURL = URL(string:"\(baseUrl)/posts/publish")! static let usersURL = URL(string:"\(baseUrl)/users")! static let usersAllURL = URL(string:"\(baseUrl)/users/all")! + static let usersUpdateURL = URL(string:"\(baseUrl)/users/update")! static let channelsGetURL = URL(string: "\(baseUrl)/channels/get")! static let channelsUpdateURL = URL(string: "\(baseUrl)/channels/update")! static let channelsListURL = URL(string: "\(baseUrl)/channels")! @@ -251,15 +269,13 @@ class Model{ } if code == 204 { + let fields = responce.response?.allHeaderFields as? [String :String] - let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: responce.response!.url!) - HTTPCookieStorage.shared.setCookie(cookies[0]) completion() } - } } @@ -470,7 +486,7 @@ class Model{ static func getUsers(for ids: [Int], completion: @escaping (([Int:Users])->())){ let json = "\(Set(ids))" - print(json) + var request = URLRequest(url: usersURL) request.httpMethod = "POST" @@ -534,7 +550,7 @@ class Model{ } - static func createChannel(with channel: Channels) { + static func createChannel(with channel: Channels, completion: @escaping ((ResultR)->())) { var request = URLRequest(url: channelsCreateURL) request.httpMethod = "POST" @@ -545,9 +561,13 @@ class Model{ (response) in isValidTocken?(response.response?.statusCode ?? 498) - if let code = response.response?.statusCode, code == 498 { + + if let code = response.response?.statusCode, let result = ResultR(rawValue: code) { + completion(result) return } + + completion(ResultR.internalServerError) } } @@ -709,8 +729,6 @@ class Model{ guard let json = response.data else { return } guard let newQueery = try? decoder.decode(QueryPosts.self, from: json) else { return } - - print("\(anonymousChannel) \(newQueery)") completion((newQueery.users, newQueery.response)) } } @@ -1081,4 +1099,40 @@ class Model{ completion(faculties) } } + + static func userUpdate(with newUser: NewRegistration, completion: @escaping ((Bool)->())) { + var request = URLRequest(url: usersUpdateURL) + request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") + request.httpMethod = "POST" + request.httpBody = try? JSONEncoder().encode(newUser) + + AF.request(request).response { (response) in + isValidTocken?(response.response?.statusCode ?? 498) + + if let code = response.response?.statusCode, code == 498 { + return + } + + if let code = response.response?.statusCode, code != 204{ + completion (false) + } else { + completion(true) + } + } + } + + static func deactivateAll(completion: @escaping (()->())){ + var request = URLRequest(url: deactivateURL) + request.httpMethod = "DELETE" + + AF.request(request).response { (responce) in + isValidTocken?(responce.response?.statusCode ?? 498) + + if let code = responce.response?.statusCode, code == 498 { + return + } + + completion() + } + } } From d03e63653365fe27c6e191786cd762e6974cccd3 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Mon, 13 May 2019 16:29:55 +0300 Subject: [PATCH 29/34] Fixed a few bags. Preview and pagination in messages --- GDproject.xcodeproj/project.pbxproj | 4 ++ GDproject/Base.lproj/Main.storyboard | 48 ++++++++++++++----- .../ChannelListController.swift | 2 +- .../ChannelViewController.swift | 5 +- .../Messages/ChatInfoViewController.swift | 29 +++++++++-- .../Dialog/DialogViewController.swift | 3 +- .../Controller/Messages/MessageViewCell.swift | 38 +++++++++++++++ .../Messages/MessagesViewController.swift | 13 ++--- .../PeopleToWriteViewController.swift | 1 - .../Profile/ProfileViewController.swift | 37 +++++++++++--- 10 files changed, 144 insertions(+), 36 deletions(-) create mode 100644 GDproject/Controller/Messages/MessageViewCell.swift diff --git a/GDproject.xcodeproj/project.pbxproj b/GDproject.xcodeproj/project.pbxproj index daa6033..ffd97b9 100644 --- a/GDproject.xcodeproj/project.pbxproj +++ b/GDproject.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1210A0BB223940310080686D /* SimplifiedChannelsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */; }; 121A8972226B1EAC005EE599 /* TagsSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121A8971226B1EAC005EE599 /* TagsSuggestionController.swift */; }; 1220FF94227C41270092C6BC /* TaggsSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */; }; + 123589FD22899339002F8028 /* MessageViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123589FC22899339002F8028 /* MessageViewCell.swift */; }; 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A4221F1B3200F6E42A /* LogInViewController.swift */; }; 123E37A7221F1DD700F6E42A /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123E37A6221F1DD700F6E42A /* MainController.swift */; }; 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; }; @@ -108,6 +109,7 @@ 1210A0BA223940310080686D /* SimplifiedChannelsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplifiedChannelsList.swift; sourceTree = ""; }; 121A8971226B1EAC005EE599 /* TagsSuggestionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsSuggestionController.swift; sourceTree = ""; }; 1220FF93227C41270092C6BC /* TaggsSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaggsSelectionViewController.swift; sourceTree = ""; }; + 123589FC22899339002F8028 /* MessageViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageViewCell.swift; sourceTree = ""; }; 123E37A4221F1B3200F6E42A /* LogInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogInViewController.swift; sourceTree = ""; }; 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = ""; }; 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; @@ -238,6 +240,7 @@ 129320082279C3B50035C7B3 /* PeopleToWriteViewController.swift */, 1298810C227F138C0032ACA3 /* Dialog */, 129320032279B4270035C7B3 /* MessagesViewController.swift */, + 123589FC22899339002F8028 /* MessageViewCell.swift */, ); path = Messages; sourceTree = ""; @@ -451,6 +454,7 @@ 1298810B227F13840032ACA3 /* DialogCell.swift in Sources */, 125A9A922286100800513E2A /* FacultyTableViewController.swift in Sources */, 125A9A8E2286068A00513E2A /* RegisterTableViewController.swift in Sources */, + 123589FD22899339002F8028 /* MessageViewCell.swift in Sources */, 12E36DCC22144725006FCDD7 /* PostViewCell.swift in Sources */, 123E37A5221F1B3200F6E42A /* LogInViewController.swift in Sources */, 125BD57F22171D73008A3575 /* Extentions.swift in Sources */, diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 5e3ac13..5ed23eb 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -778,29 +778,53 @@ - - + + - + - + + + + + + + + + + + + + + + + + @@ -811,7 +835,7 @@ - + diff --git a/GDproject/Controller/ News and channels/ChannelListController.swift b/GDproject/Controller/ News and channels/ChannelListController.swift index a0701cf..b62d257 100644 --- a/GDproject/Controller/ News and channels/ChannelListController.swift +++ b/GDproject/Controller/ News and channels/ChannelListController.swift @@ -100,7 +100,7 @@ class ChannelListController: UITableViewController { @objc func addChannel() { // editing mode is on automatically - onEditingModeBegins?(Model.Channels(people: [], name: "Untitled", tags: []),IndexPath(row: 1, section: 0)) + onEditingModeBegins?(Model.Channels(people: [], name: "Untitled", id: 0, tags: []),IndexPath(row: 1, section: 0)) } static let generalChannel = Model.Channels(people: [], name: "General", id: -1, tags: []) diff --git a/GDproject/Controller/ News and channels/ChannelViewController.swift b/GDproject/Controller/ News and channels/ChannelViewController.swift index 226b103..98be2f1 100644 --- a/GDproject/Controller/ News and channels/ChannelViewController.swift +++ b/GDproject/Controller/ News and channels/ChannelViewController.swift @@ -60,8 +60,9 @@ class ChannelViewController: UITableViewController, UpdatableName, UpdatableChan super.viewWillDisappear(animated) } - guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else { - if let _ = channel?.id + guard let _ = self.navigationController?.viewControllers.lastIndex(of: self) else + { + if let id = channel?.id, id != 0 { Model.updateChannel(with: channel!) } else { diff --git a/GDproject/Controller/Messages/ChatInfoViewController.swift b/GDproject/Controller/Messages/ChatInfoViewController.swift index f2c1808..7ff813a 100644 --- a/GDproject/Controller/Messages/ChatInfoViewController.swift +++ b/GDproject/Controller/Messages/ChatInfoViewController.swift @@ -91,10 +91,12 @@ class ChatInfoViewController: UITableViewController { switch indexPath.section { case 0: cell.textLabel?.text = groupChat!.name + cell.selectionStyle = .none return cell case 1: cell.textLabel?.text = "Leave chat" cell.textLabel?.textColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1) + cell.selectionStyle = .none return cell default: switch myPermissions{ @@ -104,6 +106,7 @@ class ChatInfoViewController: UITableViewController { case .ordinary: cell.textLabel?.text = name(for: users[usersArray[indexPath.row]]) } + cell.selectionStyle = .none return cell } } @@ -114,7 +117,7 @@ class ChatInfoViewController: UITableViewController { case .ordinary: cell.textLabel?.text = name(for: users[usersArray[indexPath.row]]) } - + cell.selectionStyle = .none return cell } @@ -143,9 +146,9 @@ class ChatInfoViewController: UITableViewController { func editName(for cell: UITableViewCell){ - let alert = UIAlertController(title: "Вы уверены?", message: "Название:", preferredStyle: .alert) + let alert = UIAlertController(title: "Are you sure?", message: "Title:", preferredStyle: .alert) - let action1 = UIAlertAction(title: "Oтмена", style: .cancel, handler: nil) + let action1 = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) alert.addTextField { [weak self] (tf) in tf.textColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) @@ -194,10 +197,28 @@ class ChatInfoViewController: UITableViewController { case .admin: showUserChoiceVC() default: - break + showParticipantPage(with: usersArray[indexPath.row]) } } } + + + if indexPath.section == 2 && indexPath.row != 0 { + switch myPermissions{ + case .admin: + showParticipantPage(with: usersArray[indexPath.row-1]) + default: + showParticipantPage(with: usersArray[indexPath.row]) + } + } + } + + var onUserDiplay: ((Int)->())? + + func showParticipantPage(with user: Int?) { + if let user = user { + onUserDiplay?(user) + } } func showUserChoiceVC() diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift index c0de30d..20696d8 100644 --- a/GDproject/Controller/Messages/Dialog/DialogViewController.swift +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -223,6 +223,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat vc.delegate = self vc.users = users! + vc.onUserDiplay = onUserDisplay if let groupChat = groupChat { vc.groupChat = groupChat.group @@ -314,7 +315,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat var canBePaginated = false func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - if indexPath.row == cellData.count - 1 && prevLast != indexPath.row && canBePaginated + if indexPath.row == cellData.count - 1 && prevLast != indexPath.row && canBePaginated && cellData.count >= 9 { print("exclusiveFrom \(currentMessagesInChat.last?.id ?? 0)") if let dialog = dialog diff --git a/GDproject/Controller/Messages/MessageViewCell.swift b/GDproject/Controller/Messages/MessageViewCell.swift new file mode 100644 index 0000000..5d180b3 --- /dev/null +++ b/GDproject/Controller/Messages/MessageViewCell.swift @@ -0,0 +1,38 @@ +// +// MessageViewCell.swift +// GDproject +// +// Created by cstore on 13/05/2019. +// Copyright © 2019 drHSE. All rights reserved. +// + +import UIKit + +class MessageViewCell: UITableViewCell { + + @IBOutlet weak var dialogName: UILabel! + + @IBOutlet weak var lastMessagePreview: UILabel! + + @IBOutlet weak var dateLabel: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + func fill(with dialog: Model.Dialog, user: Model.Users? = nil) + { + switch dialog { + + case .groupChat(let group): + dialogName.text = group.group.name + lastMessagePreview.text = group.lastMessage?.body.markdown + dateLabel.text = group.lastMessage?.time.getDate() + case .userChat(let userChat): + dialogName.text = "👤 \(user!.fullName())" + lastMessagePreview.text = userChat.lastMessage?.body.markdown + dateLabel.text = userChat.lastMessage?.time.getDate() + } + } +} diff --git a/GDproject/Controller/Messages/MessagesViewController.swift b/GDproject/Controller/Messages/MessagesViewController.swift index 60f7007..e07a820 100644 --- a/GDproject/Controller/Messages/MessagesViewController.swift +++ b/GDproject/Controller/Messages/MessagesViewController.swift @@ -64,18 +64,15 @@ class MessagesViewController: UITableViewController { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "MessagesCell", for: indexPath) + let cell = tableView.dequeueReusableCell(withIdentifier: "MessageViewCell", for: indexPath) as! MessageViewCell switch currentActiveDialogs[indexPath.row].self { - - case .groupChat(let group): - cell.textLabel?.text = group.group.name - cell.detailTextLabel?.text = group.lastMessage?.body.markdown + case .groupChat: + cell.fill(with: currentActiveDialogs[indexPath.row]) case .userChat(let userChat): - cell.textLabel?.text = "👤 \(users[userChat.user]!.fullName())" - cell.detailTextLabel?.text = userChat.lastMessage?.body.markdown + cell.fill(with: currentActiveDialogs[indexPath.row], user: users[userChat.user]) } - + return cell } diff --git a/GDproject/Controller/Messages/PeopleToWriteViewController.swift b/GDproject/Controller/Messages/PeopleToWriteViewController.swift index 84cf759..cc2ec6a 100644 --- a/GDproject/Controller/Messages/PeopleToWriteViewController.swift +++ b/GDproject/Controller/Messages/PeopleToWriteViewController.swift @@ -53,7 +53,6 @@ class PeopleToWriteViewController: UITableViewController { let cell = tableView.dequeueReusableCell(withIdentifier: "peopleToWriteCell", for: indexPath) cell.textLabel?.text = users[indexPath.row].fullName() - cell.detailTextLabel?.text = "\(users[indexPath.row].id)" return cell } diff --git a/GDproject/Controller/Profile/ProfileViewController.swift b/GDproject/Controller/Profile/ProfileViewController.swift index f5ce1cf..18dbab6 100644 --- a/GDproject/Controller/Profile/ProfileViewController.swift +++ b/GDproject/Controller/Profile/ProfileViewController.swift @@ -175,20 +175,43 @@ class ProfileViewController: UIViewController, UpdateUser if idProfile == DataStorage.standard.getUserId() { let logoutAction = UIAlertAction(title: "Log out", style: .destructive) - { [weak self] - (_) in - self?.logOut?() + { [weak self] (_) in + + if let logOut = self?.logOut { + logOut() + } else { + Model.logout() { + DataStorage.standard.setIsLoggedIn(value: false, with: 0) + (UIApplication.shared.delegate as! AppDelegate).relaunch() + } + } } let deleetAllSessionsAction = UIAlertAction(title: "Delete all sessions", style: .destructive) - { [weak self] - (_) in - self?.deleteAllSessions?() + { [weak self] _ in + + if let delete = self?.deleteAllSessions { + delete() + } else { + Model.deactivateAll() { + DataStorage.standard.setIsLoggedIn(value: false, with: 0) + (UIApplication.shared.delegate as! AppDelegate).relaunch() + } + } } let settingsAction = UIAlertAction(title: "Edit profile", style: .default) { [weak self] (_) in - self?.onSettings?() + + if let settings = self?.onSettings{ + settings() + } else { + let vc = self?.storyboard?.instantiateViewController(withIdentifier: "RegisterTableViewController") as! RegisterTableViewController + vc.delegate = self + vc.userActive = self?.user + + self?.navigationController?.pushViewController(vc, animated: true) + } } optionMenu.addAction(settingsAction) From 12b1bfd07e5d0523092a8666d18357891206889e Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Mon, 13 May 2019 23:37:13 +0300 Subject: [PATCH 30/34] Fixed a few bags. by Ilyas iPhon X --- .../Controller/ News and channels/NewsController.swift | 3 ++- .../Controller/Messages/Dialog/DialogViewController.swift | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/GDproject/Controller/ News and channels/NewsController.swift b/GDproject/Controller/ News and channels/NewsController.swift index 78521ec..0505be3 100644 --- a/GDproject/Controller/ News and channels/NewsController.swift +++ b/GDproject/Controller/ News and channels/NewsController.swift @@ -82,7 +82,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi searchController?.searchBar.placeholder = "Search tags" } - @objc func refreshPostsData( _ ff: UIRefreshControl){ + @objc func refreshPostsData( _ ff: UIRefreshControl) { decideWhatChannelDisplay() } @@ -122,6 +122,7 @@ class NewsController: UIViewController, UISearchControllerDelegate, UpdateableWi } } default: + refreshContr.endRefreshing() break } diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift index 20696d8..ebb050b 100644 --- a/GDproject/Controller/Messages/Dialog/DialogViewController.swift +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -158,7 +158,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat messageSendView.edgesToSuperview(excluding: [.top,.bottom]) - bottomConstraint = NSLayoutConstraint(item: messageSendView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0) + bottomConstraint = NSLayoutConstraint(item: messageSendView, attribute: .bottom, relatedBy: .equal, toItem: self.view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0) view.addConstraint(bottomConstraint) messageSendView.height(60) @@ -172,7 +172,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat messageTextView.edgesToSuperview(excluding: .right) messageTextView.rightToLeft(of: sendButton) - tableView.edgesToSuperview(excluding: .bottom) + tableView.edgesToSuperview(excluding: .bottom, insets: .top(self.navigationController?.navigationBar.frame.size.height ?? 0)) tableView.bottomToTop(of: messageSendView) self.view.layoutSubviews() @@ -191,7 +191,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue - bottomConstraint.constant = notification.name == UIResponder.keyboardWillShowNotification ? -(keyBoardFrame.height) : 0 + bottomConstraint.constant = notification.name == UIResponder.keyboardWillShowNotification ? -(keyBoardFrame.height) + self.view.safeAreaInsets.bottom : 0 UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: { self.view.layoutIfNeeded() From 46b3bdf1cf4ef593c62565a71b892b6f8541a071 Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Tue, 14 May 2019 00:57:01 +0300 Subject: [PATCH 31/34] Slight changes --- .../ News and channels/NewPostViewController.swift | 13 ++++++++----- .../ News and channels/PostViewCell.swift | 2 +- .../Messages/Dialog/DialogViewController.swift | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/GDproject/Controller/ News and channels/NewPostViewController.swift b/GDproject/Controller/ News and channels/NewPostViewController.swift index 00780c8..1aaeae3 100644 --- a/GDproject/Controller/ News and channels/NewPostViewController.swift +++ b/GDproject/Controller/ News and channels/NewPostViewController.swift @@ -41,7 +41,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate, TagsReceiver let textStorage = MarklightTextStorage() static var draft: String = "" - static var hashTagsDraft: [String] = ["tt"] + static var hashTagsDraft: [String] = [ ] var textView: UITextView! @@ -72,7 +72,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate, TagsReceiver setUpMD() setUpTextView() - setUpAccessoryView() + // setUpAccessoryView() //setUpTagsView() } @@ -141,7 +141,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate, TagsReceiver func setUpTextView(){ view.addConstraint(bottomTextViewConstraint) view1.addSubview(textView) - textView.edgesToSuperview(insets: .top(8) + .left(8) + .bottom(40+8) + .right(8)) + textView.edgesToSuperview(insets: .top(8) + .left(8) + .bottom(8) + .right(8)) if #available(iOS 11.0, *) { textView.smartDashesType = .no @@ -210,7 +210,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate, TagsReceiver } func setUpMD(){ - textStorage.marklightTextProcessor.codeColor = UIColor.orange + textStorage.marklightTextProcessor.codeColor = UIColor.gray textStorage.marklightTextProcessor.quoteColor = UIColor.darkGray textStorage.marklightTextProcessor.syntaxColor = UIColor.blue textStorage.marklightTextProcessor.codeFontName = "Courier" @@ -233,6 +233,9 @@ class NewPostViewController: UIViewController, UITextViewDelegate, TagsReceiver // MARK:- new post @objc func newPost(){ Model.createAndPublish(body: [Model.Attachments(markdown: textView!.text)], tags: currentTags) + + NewPostViewController.draft = "" + NewPostViewController.hashTagsDraft = [] // adding row to uiTableView after adding new post // myProtocol?.addPost(post: p) moveBackToParentVC?() @@ -247,7 +250,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate, TagsReceiver [weak self] _ in NewPostViewController.draft = self?.textView.text ?? "" - NewPostViewController.hashTagsDraft = /*self?.tagsField.tags.map { $0.text } ??*/ [] + NewPostViewController.hashTagsDraft = self?.currentTags ?? [] self?.moveBackToParentVC?() } diff --git a/GDproject/Controller/ News and channels/PostViewCell.swift b/GDproject/Controller/ News and channels/PostViewCell.swift index ad2ddcb..8565439 100644 --- a/GDproject/Controller/ News and channels/PostViewCell.swift +++ b/GDproject/Controller/ News and channels/PostViewCell.swift @@ -87,7 +87,7 @@ class PostViewCell: UITableViewCell nameLabel.setTitle("\(post.user?.firstName ?? "") \(post.user?.lastName ?? "")", for: .normal) nameLabel.addTarget(self, action: #selector(displayProfile), for: .touchUpInside) if let user = post.user{ - fullNameLabel.text = "\(user.firstName) \(user.middleName) \(user.lastName)" + fullNameLabel.text = "\(user.faculty.name)" } else { fullNameLabel.text = "\(post.authorId)" diff --git a/GDproject/Controller/Messages/Dialog/DialogViewController.swift b/GDproject/Controller/Messages/Dialog/DialogViewController.swift index ebb050b..434b751 100644 --- a/GDproject/Controller/Messages/Dialog/DialogViewController.swift +++ b/GDproject/Controller/Messages/Dialog/DialogViewController.swift @@ -172,7 +172,7 @@ class DialogViewController: UIViewController, UpdatableGroup, UITableViewDelegat messageTextView.edgesToSuperview(excluding: .right) messageTextView.rightToLeft(of: sendButton) - tableView.edgesToSuperview(excluding: .bottom, insets: .top(self.navigationController?.navigationBar.frame.size.height ?? 0)) + tableView.edgesToSuperview(excluding: .bottom) tableView.bottomToTop(of: messageSendView) self.view.layoutSubviews() From 9a307ababb5342417dc87f4858f1a3b03b911d1b Mon Sep 17 00:00:00 2001 From: Darya Rednikina Date: Tue, 14 May 2019 15:08:15 +0300 Subject: [PATCH 32/34] Added validation of status codes to messages and new post. --- GDproject/Base.lproj/Main.storyboard | 11 ++-------- .../NewPostViewController.swift | 20 +++++++++++++++---- .../Dialog/DialogViewController.swift | 9 +++++++-- GDproject/Simple model/Model.swift | 18 ++++++++++++++--- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/GDproject/Base.lproj/Main.storyboard b/GDproject/Base.lproj/Main.storyboard index 5ed23eb..fd5587e 100644 --- a/GDproject/Base.lproj/Main.storyboard +++ b/GDproject/Base.lproj/Main.storyboard @@ -846,7 +846,7 @@ - + @@ -854,14 +854,7 @@ -