diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index b05092af3af7aed3fd1c8784caf6f2621c9c1c6b..0000000000000000000000000000000000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,8 +0,0 @@
-[submodule "Mobile-SDK-S3"]
- path = externals/mobile-sdk-ext
- url = https://github.com/NYNJA-MC/Mobile-SDK.git
- branch = sprint3
-[submodule "Mobile-SDK-S4"]
- path = externals/mobile-sdk-s4
- url = https://github.com/NYNJA-MC/Mobile-SDK.git
- branch = sprint4
diff --git a/Frameworks/NynjaUIKit/NynjaUIKit.xcodeproj/project.pbxproj b/Frameworks/NynjaUIKit/NynjaUIKit.xcodeproj/project.pbxproj
index 3cfbcb64137cf551dc6c197081c2586a5e8ebc26..d227aa9667c4a83d7a6505aa14378e2c445a08ab 100644
--- a/Frameworks/NynjaUIKit/NynjaUIKit.xcodeproj/project.pbxproj
+++ b/Frameworks/NynjaUIKit/NynjaUIKit.xcodeproj/project.pbxproj
@@ -328,7 +328,6 @@
8514D4BD20EE27080002378A /* Frameworks */,
8514D4BE20EE27080002378A /* Headers */,
8514D4BF20EE27080002378A /* Resources */,
- 46657F61D06BBC36D9E1E7E9 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -381,21 +380,6 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 46657F61D06BBC36D9E1E7E9 /* [CP] Copy Pods Resources */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "[CP] Copy Pods Resources";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit-resources.sh\"\n";
- showEnvVarsInLog = 0;
- };
79BB170804CAF73DF4C1F030 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist
index c11f81732b885ad6c7a511e6e1c9ef4cc9adda54..3b50d6f5d735547eac0978bbeb4eca44e5673d29 100644
--- a/Nynja-Share/Resources/Info.plist
+++ b/Nynja-Share/Resources/Info.plist
@@ -21,7 +21,7 @@
CFBundleShortVersionString
1.0
CFBundleVersion
- 0.2.135
+ 0.2.137
Config
$(Config)
ModelsVersion
diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj
index 8666269d0aa64c28f52a50ebb707c16ca8be8956..f561ce05b4dce59ad418ff8e559c12084d68ef38 100644
--- a/Nynja.xcodeproj/project.pbxproj
+++ b/Nynja.xcodeproj/project.pbxproj
@@ -157,6 +157,7 @@
2632139120D797F500C31144 /* TranslationViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2632139020D797F500C31144 /* TranslationViewProtocol.swift */; };
2633EF6E205212F700DB3868 /* MemberDAOProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2633EF6D205212F700DB3868 /* MemberDAOProtocol.swift */; };
2633EF702052130400DB3868 /* MemberDAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2633EF6F2052130400DB3868 /* MemberDAO.swift */; };
+ 263409342119CFE2002F8D8F /* RecordContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263409332119CFE2002F8D8F /* RecordContainer.swift */; };
26342CA020ECAA0700D2196B /* TranscribeNetworkRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26342C9F20ECAA0700D2196B /* TranscribeNetworkRouter.swift */; };
26342CA920ECBAEF00D2196B /* TranscribeNetworkClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26342CA820ECBAEE00D2196B /* TranscribeNetworkClient.swift */; };
26342CAB20ECBB0100D2196B /* TranscribeNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26342CAA20ECBB0100D2196B /* TranscribeNetworkService.swift */; };
@@ -414,7 +415,7 @@
26EA201520BECDCA00FBB9CA /* ConversationLanguageSettingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EA201420BECDCA00FBB9CA /* ConversationLanguageSettingService.swift */; };
26EAA2CB20D2497F005697CB /* TranslationAutoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EAA2CA20D2497F005697CB /* TranslationAutoView.swift */; };
26ED2C1820042683002DBBE8 /* RepliesDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26ED2C1720042683002DBBE8 /* RepliesDS.swift */; };
- 26ED2C1A2004276B002DBBE8 /* RepliesTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26ED2C192004276B002DBBE8 /* RepliesTableViewDelegate.swift */; };
+ 26ED2C1A2004276B002DBBE8 /* RepliesCollectionViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26ED2C192004276B002DBBE8 /* RepliesCollectionViewDelegate.swift */; };
26EEA5472091F84E0066D3B0 /* CollectionsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EEA5462091F84E0066D3B0 /* CollectionsExtensions.swift */; };
26EEA5482091F84E0066D3B0 /* CollectionsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EEA5462091F84E0066D3B0 /* CollectionsExtensions.swift */; };
26F03C0D20698B0000712CB0 /* ChatWheelItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F03C0C20698B0000712CB0 /* ChatWheelItemModel.swift */; };
@@ -498,7 +499,6 @@
3A2A99831EFAD2FB002749B3 /* PageControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2A99821EFAD2FB002749B3 /* PageControl.swift */; };
3A3FD2831F39E0A000B6958F /* HistoryRequestModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3FD2821F39E0A000B6958F /* HistoryRequestModel.swift */; };
3A62B7D81F4CB9D100F45B51 /* BaseMQTTModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A62B7D71F4CB9D100F45B51 /* BaseMQTTModel.swift */; };
- 3A768F071ED4987300108F7C /* VoxService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A768F061ED4987300108F7C /* VoxService.swift */; };
3A771CAA1F191B38008D968A /* ProfileHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A771CA91F191B38008D968A /* ProfileHandler.swift */; };
3A771CB21F193945008D968A /* UpdateRosterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A771CB11F193945008D968A /* UpdateRosterModel.swift */; };
3A8045D21F60C8E200AED866 /* MQTTServiceFriend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045CE1F60C8E200AED866 /* MQTTServiceFriend.swift */; };
@@ -606,6 +606,7 @@
553819525871F7D28AB90364 /* GroupRulesPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705B62097A99515B3C778F35 /* GroupRulesPresenter.swift */; };
5683555B8382F7F37FEE1AF5 /* ProfileWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA66D21FFC1A74CFD2F63C4 /* ProfileWireframe.swift */; };
5894F4C605B66B55F21D406E /* DateTimePickerInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA2BE900351F21464CE687 /* DateTimePickerInteractor.swift */; };
+ 5A48445F21178E33000657ED /* AlertTextFieldViewControllerLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A48445E21178E33000657ED /* AlertTextFieldViewControllerLayout.swift */; };
5A6237362268CC9BD4792230 /* EditUsernameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B772E08B9E40EB48DD87082 /* EditUsernameViewController.swift */; };
5AD8110B5B87B1AB9F1C5B52 /* CreateGroupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CBACEAABEE65D7EC5572C4E /* CreateGroupPresenter.swift */; };
5B5EE777EF301CFC1FDCF307 /* CreateGroupInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFC76E2B3DD0BCA0A622A5CD /* CreateGroupInteractor.swift */; };
@@ -787,6 +788,8 @@
853FB0702049B396000996C5 /* SupportItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853FB06F2049B396000996C5 /* SupportItemsFactory.swift */; };
853FB0752049B4FF000996C5 /* TextTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853FB0742049B4FF000996C5 /* TextTableViewCell.swift */; };
853FB0772049B7CA000996C5 /* TextCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853FB0762049B7CA000996C5 /* TextCellViewModel.swift */; };
+ 8540A331211B34B4007F65AF /* MessageCollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540A330211B34B4007F65AF /* MessageCollectionViewDataSource.swift */; };
+ 8540A333211B35A4007F65AF /* MessageCollectionViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540A332211B35A4007F65AF /* MessageCollectionViewDelegate.swift */; };
8541BD68206CE0220093EF1E /* ImagePlaceholderWheelItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8541BD67206CE0220093EF1E /* ImagePlaceholderWheelItemModel.swift */; };
8541BD6B206CE3A40093EF1E /* ChatPlaceholderWheelItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8541BD6A206CE3A40093EF1E /* ChatPlaceholderWheelItemModel.swift */; };
85433F22204D596D00B373A7 /* WebFullScreenPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85433F1D204D596D00B373A7 /* WebFullScreenPresenter.swift */; };
@@ -806,6 +809,7 @@
854A4B302080D6C400759152 /* CellWithImageTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 854A4B2E2080D6C400759152 /* CellWithImageTableViewCell.swift */; };
854A4B312080D6C400759152 /* CellWithImageCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 854A4B2F2080D6C400759152 /* CellWithImageCellModel.swift */; };
854CFB08210704AE00FBC133 /* CGRectExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 854CFB07210704AE00FBC133 /* CGRectExtensions.swift */; };
+ 854D13D8211B2E7200E139FC /* MessageCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 854D13D7211B2E7200E139FC /* MessageCollectionViewLayout.swift */; };
854FC1CB204468FC00B12BE5 /* CarouselFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 854FC1CA204468FC00B12BE5 /* CarouselFlowLayout.swift */; };
8557987F2093200D007050B8 /* StickerMenuActionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8557987D2093200D007050B8 /* StickerMenuActionCollectionViewCell.swift */; };
855798802093200D007050B8 /* StickerMenuActionCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8557987E2093200D007050B8 /* StickerMenuActionCellModel.swift */; };
@@ -1397,8 +1401,6 @@
A45F113220B4218D00F45004 /* BaseChatCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F10EF20B4218D00F45004 /* BaseChatCellLayout.swift */; };
A45F113320B4218D00F45004 /* MessageCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F10F020B4218D00F45004 /* MessageCellFactory.swift */; };
A45F113420B4218D00F45004 /* UnreadCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F10F120B4218D00F45004 /* UnreadCell.swift */; };
- A45F113520B4218D00F45004 /* MessageTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F10F220B4218D00F45004 /* MessageTableViewDelegate.swift */; };
- A45F113620B4218D00F45004 /* MessageDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F10F320B4218D00F45004 /* MessageDS.swift */; };
A45F113720B4218D00F45004 /* ReplyPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F10F520B4218D00F45004 /* ReplyPreview.swift */; };
A45F113820B4218D00F45004 /* AvatarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F10F720B4218D00F45004 /* AvatarViewModel.swift */; };
A45F113920B4218D00F45004 /* AvatarViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F10F820B4218D00F45004 /* AvatarViewLayout.swift */; };
@@ -1429,7 +1431,6 @@
A45F116120B422AF00F45004 /* Message+System.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F115D20B422AF00F45004 /* Message+System.swift */; };
A45F59AB205825FC00EAA780 /* RosterDAOProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F59AA205825FC00EAA780 /* RosterDAOProtocol.swift */; };
A45F59AD2058263F00EAA780 /* RosterDAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F59AC2058263F00EAA780 /* RosterDAO.swift */; };
- A45F59B02058317000EAA780 /* VICallExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45F59AF2058317000EAA780 /* VICallExtension.swift */; };
A460324F2105C9A1009783DA /* InputsCachePolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A460324E2105C9A1009783DA /* InputsCachePolicy.swift */; };
A46032502105D357009783DA /* InputsCachePolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A460324E2105C9A1009783DA /* InputsCachePolicy.swift */; };
A46032522105D3E1009783DA /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A46032512105D3E1009783DA /* TextView.swift */; };
@@ -1458,6 +1459,7 @@
A4679BBB20B305360021FE9C /* LinkField.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4679BB820B305360021FE9C /* LinkField.swift */; };
A4688DFA20650FF50013660D /* DBObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4688DF920650FF50013660D /* DBObserver.swift */; };
A4688DFC20652DE30013660D /* StorageChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4688DFB20652DE30013660D /* StorageChange.swift */; };
+ A46CF04321147BAE0072F185 /* HistoryRequestModelTypeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A46CF04221147BAE0072F185 /* HistoryRequestModelTypeProtocol.swift */; };
A47785A220D18D4A0053E0D2 /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E70402BC1FF6972B00182D81 /* BaseView.swift */; };
A47785A420D286680053E0D2 /* ChannelChatItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A47785A320D286680053E0D2 /* ChannelChatItemsFactory.swift */; };
A477CE7D2061236800081D34 /* MessageLinkDAOProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A477CE7C2061236800081D34 /* MessageLinkDAOProtocol.swift */; };
@@ -1574,11 +1576,53 @@
B723C632204D9E5100884FFD /* DataAndStorageOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = B723C631204D9E5100884FFD /* DataAndStorageOption.swift */; };
B723C634204DA54200884FFD /* SettingsDataAndStorageTableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B723C633204DA54200884FFD /* SettingsDataAndStorageTableDataSource.swift */; };
B723C636204DA56600884FFD /* SettingsDataAndStorageTableDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B723C635204DA56600884FFD /* SettingsDataAndStorageTableDelegate.swift */; };
+ B742053F211448BD004FDE16 /* AsigningInterpreterLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B742053E211448BD004FDE16 /* AsigningInterpreterLayout.swift */; };
+ B745F2E42109B9E500488A91 /* LanguagePickerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B745F2E32109B9E500488A91 /* LanguagePickerDelegate.swift */; };
+ B745F2E62109BB0100488A91 /* InterpretationLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B745F2E52109BB0100488A91 /* InterpretationLayout.swift */; };
+ B74BAFFB21076AFA0049CD27 /* CircleMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B74BAFEF21076AFA0049CD27 /* CircleMenu.swift */; };
+ B74BAFFC21076AFA0049CD27 /* SectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B74BAFF121076AFA0049CD27 /* SectionView.swift */; };
+ B74BAFFD21076AFA0049CD27 /* Sector.swift in Sources */ = {isa = PBXBuildFile; fileRef = B74BAFF221076AFA0049CD27 /* Sector.swift */; };
+ B74BAFFF21076AFA0049CD27 /* CircleMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B74BAFF421076AFA0049CD27 /* CircleMenuDelegate.swift */; };
+ B74BB00021076AFA0049CD27 /* UIView+Mask.swift in Sources */ = {isa = PBXBuildFile; fileRef = B74BAFF621076AFA0049CD27 /* UIView+Mask.swift */; };
+ B74BB00121076AFA0049CD27 /* CircleMenuSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B74BAFF821076AFA0049CD27 /* CircleMenuSet.swift */; };
+ B74BB00221076AFA0049CD27 /* CircleMenuFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B74BAFF921076AFA0049CD27 /* CircleMenuFactory.swift */; };
+ B74BB00321076AFA0049CD27 /* CircleMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B74BAFFA21076AFA0049CD27 /* CircleMenuItem.swift */; };
B750EF022046B24D00A99F9C /* TransferInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B750EF012046B24D00A99F9C /* TransferInfo.swift */; };
B750EF042046D69C00A99F9C /* SpeedMesurement.swift in Sources */ = {isa = PBXBuildFile; fileRef = B750EF032046D69C00A99F9C /* SpeedMesurement.swift */; };
B750EF062046D7C700A99F9C /* SpeedStringRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B750EF052046D7C700A99F9C /* SpeedStringRepresentable.swift */; };
B763DD9320AA1C3400A30B63 /* ContactCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B763DD9220AA1C3400A30B63 /* ContactCellLayout.swift */; };
+ B77C11DB2109242200CCB42E /* AssigningInterpreterPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11D62109242200CCB42E /* AssigningInterpreterPresenter.swift */; };
+ B77C11DC2109242200CCB42E /* AssigningInterpreterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11D72109242200CCB42E /* AssigningInterpreterViewController.swift */; };
+ B77C11DD2109242200CCB42E /* AssigningInterpreterProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11D82109242200CCB42E /* AssigningInterpreterProtocols.swift */; };
+ B77C11DE2109242200CCB42E /* AssigningInterpreterInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11D92109242200CCB42E /* AssigningInterpreterInteractor.swift */; };
+ B77C11DF2109242200CCB42E /* AssigningInterpreterWireFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11DA2109242200CCB42E /* AssigningInterpreterWireFrame.swift */; };
+ B77C11E62109254800CCB42E /* InterpretationTypePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11E12109254800CCB42E /* InterpretationTypePresenter.swift */; };
+ B77C11E72109254800CCB42E /* InterpretationTypeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11E22109254800CCB42E /* InterpretationTypeViewController.swift */; };
+ B77C11E82109254800CCB42E /* InterpretationTypeProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11E32109254800CCB42E /* InterpretationTypeProtocols.swift */; };
+ B77C11E92109254800CCB42E /* InterpretationTypeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11E42109254800CCB42E /* InterpretationTypeInteractor.swift */; };
+ B77C11EA2109254800CCB42E /* InterpretationTypeWireFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77C11E52109254800CCB42E /* InterpretationTypeWireFrame.swift */; };
B79B996E20CA88D100BEF5DE /* InviteFriendsMessageComposeDelegateHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79B996D20CA88D100BEF5DE /* InviteFriendsMessageComposeDelegateHandler.swift */; };
+ B79FA02B2107731400F286BF /* MarketplacePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA0262107731400F286BF /* MarketplacePresenter.swift */; };
+ B79FA02C2107731400F286BF /* MarketplaceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA0272107731400F286BF /* MarketplaceViewController.swift */; };
+ B79FA02D2107731400F286BF /* MarketplaceProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA0282107731400F286BF /* MarketplaceProtocols.swift */; };
+ B79FA02E2107731400F286BF /* MarketplaceInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA0292107731400F286BF /* MarketplaceInteractor.swift */; };
+ B79FA02F2107731400F286BF /* MarketplaceWireFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA02A2107731400F286BF /* MarketplaceWireFrame.swift */; };
+ B79FA03621091ED000F286BF /* InterpretationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA03121091ED000F286BF /* InterpretationPresenter.swift */; };
+ B79FA03721091ED000F286BF /* InterpretationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA03221091ED000F286BF /* InterpretationViewController.swift */; };
+ B79FA03821091ED000F286BF /* InterpretationProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA03321091ED000F286BF /* InterpretationProtocols.swift */; };
+ B79FA03921091ED000F286BF /* InterpretationInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA03421091ED000F286BF /* InterpretationInteractor.swift */; };
+ B79FA03A21091ED000F286BF /* InterpretationWireFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79FA03521091ED000F286BF /* InterpretationWireFrame.swift */; };
+ B7B546AE210D9C8C002DCA55 /* CircleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B546AD210D9C8C002DCA55 /* CircleView.swift */; };
+ B7B546B0210DC68E002DCA55 /* CircleLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B546AF210DC68E002DCA55 /* CircleLoadingView.swift */; };
+ B7B546B3210DD1EC002DCA55 /* AlertTextFieldViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B546B2210DD1EC002DCA55 /* AlertTextFieldViewController.swift */; };
+ B7EF8ED0210C501F00E0E981 /* InterpretationTypeTableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7EF8ECF210C501F00E0E981 /* InterpretationTypeTableDataSource.swift */; };
+ B7EF8ED2210C502D00E0E981 /* InterpretationTypeTableDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7EF8ED1210C502D00E0E981 /* InterpretationTypeTableDelegate.swift */; };
+ B7EF8ED4210C511C00E0E981 /* InterpretationTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7EF8ED3210C511C00E0E981 /* InterpretationTypeCell.swift */; };
+ B7EF8ED7210C598800E0E981 /* InterpretationTypeCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7EF8ED6210C598800E0E981 /* InterpretationTypeCellLayout.swift */; };
+ B7EF8ED9210C71E800E0E981 /* InterpretationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7EF8ED8210C71E800E0E981 /* InterpretationType.swift */; };
+ B7EF8EDB210C759400E0E981 /* InterpretationTypeCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7EF8EDA210C759300E0E981 /* InterpretationTypeCellModel.swift */; };
+ B7EF8EDD210CB0A200E0E981 /* InterpretationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7EF8EDC210CB0A200E0E981 /* InterpretationModel.swift */; };
+ B7F4C2AC211995D500E48A98 /* UseCaseValidationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F4C2AB211995D500E48A98 /* UseCaseValidationService.swift */; };
B7F505192061158800C28FA1 /* SettingsArrowCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F505182061158800C28FA1 /* SettingsArrowCellViewModel.swift */; };
B7F5051B20611A0900C28FA1 /* DownloadSettingsArrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F5051A20611A0900C28FA1 /* DownloadSettingsArrowModel.swift */; };
B7F5051D2061252100C28FA1 /* DataAndStorageItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F5051C2061252100C28FA1 /* DataAndStorageItemsFactory.swift */; };
@@ -2105,11 +2149,9 @@
00D7B5C620285BA7004B0E2B /* ScheduleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleView.swift; sourceTree = ""; };
00E4A65E201A287100CEC61F /* MapSearchDS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSearchDS.swift; sourceTree = ""; };
00E8513A2021E96E007DC792 /* GApiResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GApiResponse.swift; sourceTree = ""; };
- 00E864662049840A00844FF1 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; };
00E86468204D519600844FF1 /* LanguageSettingsItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageSettingsItemsFactory.swift; sourceTree = ""; };
00E8646C204D788100844FF1 /* LanguageSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageSettingsCell.swift; sourceTree = ""; };
00E864712052B1BE00844FF1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
- 00E864732052B1C400844FF1 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; };
00E9824B205C1E19008BF03D /* SecurityItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityItemsFactory.swift; sourceTree = ""; };
00E9824D205C2604008BF03D /* SessionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionItemView.swift; sourceTree = ""; };
00E9824F205C2668008BF03D /* SessionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionHeaderView.swift; sourceTree = ""; };
@@ -2208,6 +2250,7 @@
2632139220D7B71200C31144 /* TranslateConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TranslateConfig.xcconfig; sourceTree = ""; };
2633EF6D205212F700DB3868 /* MemberDAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDAOProtocol.swift; sourceTree = ""; };
2633EF6F2052130400DB3868 /* MemberDAO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDAO.swift; sourceTree = ""; };
+ 263409332119CFE2002F8D8F /* RecordContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordContainer.swift; sourceTree = ""; };
26342C9F20ECAA0700D2196B /* TranscribeNetworkRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscribeNetworkRouter.swift; sourceTree = ""; };
26342CA820ECBAEE00D2196B /* TranscribeNetworkClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscribeNetworkClient.swift; sourceTree = ""; };
26342CAA20ECBB0100D2196B /* TranscribeNetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscribeNetworkService.swift; sourceTree = ""; };
@@ -2392,7 +2435,7 @@
26EA201420BECDCA00FBB9CA /* ConversationLanguageSettingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationLanguageSettingService.swift; sourceTree = ""; };
26EAA2CA20D2497F005697CB /* TranslationAutoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslationAutoView.swift; sourceTree = ""; };
26ED2C1720042683002DBBE8 /* RepliesDS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepliesDS.swift; sourceTree = ""; };
- 26ED2C192004276B002DBBE8 /* RepliesTableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepliesTableViewDelegate.swift; sourceTree = ""; };
+ 26ED2C192004276B002DBBE8 /* RepliesCollectionViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepliesCollectionViewDelegate.swift; sourceTree = ""; };
26EEA5462091F84E0066D3B0 /* CollectionsExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionsExtensions.swift; sourceTree = ""; };
26F03C0C20698B0000712CB0 /* ChatWheelItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWheelItemModel.swift; sourceTree = ""; };
26F47051201B7248005D3192 /* ReturnToCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReturnToCallView.swift; sourceTree = ""; };
@@ -2456,7 +2499,6 @@
3A2A99821EFAD2FB002749B3 /* PageControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PageControl.swift; path = PageControl/PageControl.swift; sourceTree = ""; };
3A3FD2821F39E0A000B6958F /* HistoryRequestModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HistoryRequestModel.swift; path = Services/Models/HistoryRequestModel.swift; sourceTree = ""; };
3A62B7D71F4CB9D100F45B51 /* BaseMQTTModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BaseMQTTModel.swift; path = Services/Models/BaseMQTTModel.swift; sourceTree = ""; };
- 3A768F061ED4987300108F7C /* VoxService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VoxService.swift; path = Services/VoxService.swift; sourceTree = ""; wrapsLines = 0; };
3A771CA91F191B38008D968A /* ProfileHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileHandler.swift; path = Services/HandleServices/ProfileHandler.swift; sourceTree = ""; };
3A771CB11F193945008D968A /* UpdateRosterModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UpdateRosterModel.swift; path = Services/Models/UpdateRosterModel.swift; sourceTree = ""; };
3A8045CD1F60C8E200AED866 /* MQTTService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MQTTService.swift; sourceTree = ""; };
@@ -2581,6 +2623,7 @@
55EC130CCF07D992BC6DD435 /* MapSearchPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MapSearchPresenter.swift; sourceTree = ""; };
5957BF589EEC24E6799EB4CF /* TimeZoneSelectorViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TimeZoneSelectorViewController.swift; sourceTree = ""; };
59C99DD8A060B0BE6802110F /* AddContactPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddContactPresenter.swift; sourceTree = ""; };
+ 5A48445E21178E33000657ED /* AlertTextFieldViewControllerLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertTextFieldViewControllerLayout.swift; sourceTree = ""; };
5AEEB3D82E9CF02760DA4CE7 /* Pods-Nynja-Share.channels.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja-Share.channels.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja-Share/Pods-Nynja-Share.channels.xcconfig"; sourceTree = ""; };
5B377AA90A6B6BA0120C31F1 /* EditProfileProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditProfileProtocols.swift; sourceTree = ""; };
5BC1D37220D3B3D8002A44B3 /* NynjaCommunicatorService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NynjaCommunicatorService.swift; path = Services/NynjaCommunicatorService.swift; sourceTree = ""; };
@@ -2756,6 +2799,8 @@
853FB06F2049B396000996C5 /* SupportItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportItemsFactory.swift; sourceTree = ""; };
853FB0742049B4FF000996C5 /* TextTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextTableViewCell.swift; sourceTree = ""; };
853FB0762049B7CA000996C5 /* TextCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCellViewModel.swift; sourceTree = ""; };
+ 8540A330211B34B4007F65AF /* MessageCollectionViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCollectionViewDataSource.swift; sourceTree = ""; };
+ 8540A332211B35A4007F65AF /* MessageCollectionViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCollectionViewDelegate.swift; sourceTree = ""; };
8541BD67206CE0220093EF1E /* ImagePlaceholderWheelItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePlaceholderWheelItemModel.swift; sourceTree = ""; };
8541BD6A206CE3A40093EF1E /* ChatPlaceholderWheelItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPlaceholderWheelItemModel.swift; sourceTree = ""; };
85433F1D204D596D00B373A7 /* WebFullScreenPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebFullScreenPresenter.swift; sourceTree = ""; };
@@ -2775,6 +2820,7 @@
854A4B2E2080D6C400759152 /* CellWithImageTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellWithImageTableViewCell.swift; sourceTree = ""; };
854A4B2F2080D6C400759152 /* CellWithImageCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellWithImageCellModel.swift; sourceTree = ""; };
854CFB07210704AE00FBC133 /* CGRectExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGRectExtensions.swift; sourceTree = ""; };
+ 854D13D7211B2E7200E139FC /* MessageCollectionViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCollectionViewLayout.swift; sourceTree = ""; };
854FC1CA204468FC00B12BE5 /* CarouselFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselFlowLayout.swift; sourceTree = ""; };
8557987D2093200D007050B8 /* StickerMenuActionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerMenuActionCollectionViewCell.swift; sourceTree = ""; };
8557987E2093200D007050B8 /* StickerMenuActionCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerMenuActionCellModel.swift; sourceTree = ""; };
@@ -3240,8 +3286,6 @@
A45F10EF20B4218D00F45004 /* BaseChatCellLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseChatCellLayout.swift; sourceTree = ""; };
A45F10F020B4218D00F45004 /* MessageCellFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCellFactory.swift; sourceTree = ""; };
A45F10F120B4218D00F45004 /* UnreadCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnreadCell.swift; sourceTree = ""; };
- A45F10F220B4218D00F45004 /* MessageTableViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageTableViewDelegate.swift; sourceTree = ""; };
- A45F10F320B4218D00F45004 /* MessageDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageDS.swift; sourceTree = ""; };
A45F10F520B4218D00F45004 /* ReplyPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplyPreview.swift; sourceTree = ""; };
A45F10F720B4218D00F45004 /* AvatarViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarViewModel.swift; sourceTree = ""; };
A45F10F820B4218D00F45004 /* AvatarViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarViewLayout.swift; sourceTree = ""; };
@@ -3261,7 +3305,6 @@
A45F115D20B422AF00F45004 /* Message+System.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Message+System.swift"; sourceTree = ""; };
A45F59AA205825FC00EAA780 /* RosterDAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RosterDAOProtocol.swift; sourceTree = ""; };
A45F59AC2058263F00EAA780 /* RosterDAO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RosterDAO.swift; sourceTree = ""; };
- A45F59AF2058317000EAA780 /* VICallExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VICallExtension.swift; sourceTree = ""; };
A460324E2105C9A1009783DA /* InputsCachePolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputsCachePolicy.swift; sourceTree = ""; };
A46032512105D3E1009783DA /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; };
A4626EAE20D96EF9000F37EE /* MainViewController+Gallery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainViewController+Gallery.swift"; sourceTree = ""; };
@@ -3287,6 +3330,7 @@
A4679BB820B305360021FE9C /* LinkField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkField.swift; sourceTree = ""; };
A4688DF920650FF50013660D /* DBObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBObserver.swift; sourceTree = ""; };
A4688DFB20652DE30013660D /* StorageChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageChange.swift; sourceTree = ""; };
+ A46CF04221147BAE0072F185 /* HistoryRequestModelTypeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryRequestModelTypeProtocol.swift; sourceTree = ""; };
A47785A320D286680053E0D2 /* ChannelChatItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelChatItemsFactory.swift; sourceTree = ""; };
A477CE7C2061236800081D34 /* MessageLinkDAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageLinkDAOProtocol.swift; sourceTree = ""; };
A477CE8120613A0900081D34 /* StarMessageDAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarMessageDAOProtocol.swift; sourceTree = ""; };
@@ -3324,7 +3368,6 @@
A4B544E920EFB1A800EB7B0F /* errors_Spec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = errors_Spec.swift; sourceTree = ""; };
A4B544EE20EFB4DF00EB7B0F /* BertTupleExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BertTupleExtension.swift; sourceTree = ""; };
A4B544F920EFC0AD00EB7B0F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/StatusCodes.strings; sourceTree = ""; };
- A4B544FB20EFC0BC00EB7B0F /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/StatusCodes.strings; sourceTree = ""; };
A4B544FE20EFC1BA00EB7B0F /* StatusCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusCode.swift; sourceTree = ""; };
A4BCEC6B20DBF2A40078B076 /* Link+DB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Link+DB.swift"; sourceTree = ""; };
A4BE4AB42068E98C00C041D1 /* ALTextInputBar+Trim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTextInputBar+Trim.swift"; sourceTree = ""; };
@@ -3392,12 +3435,54 @@
B723C631204D9E5100884FFD /* DataAndStorageOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataAndStorageOption.swift; sourceTree = ""; };
B723C633204DA54200884FFD /* SettingsDataAndStorageTableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDataAndStorageTableDataSource.swift; sourceTree = ""; };
B723C635204DA56600884FFD /* SettingsDataAndStorageTableDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDataAndStorageTableDelegate.swift; sourceTree = ""; };
+ B742053E211448BD004FDE16 /* AsigningInterpreterLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsigningInterpreterLayout.swift; sourceTree = ""; };
+ B745F2E32109B9E500488A91 /* LanguagePickerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguagePickerDelegate.swift; sourceTree = ""; };
+ B745F2E52109BB0100488A91 /* InterpretationLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationLayout.swift; sourceTree = ""; };
+ B74BAFEF21076AFA0049CD27 /* CircleMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMenu.swift; sourceTree = ""; };
+ B74BAFF121076AFA0049CD27 /* SectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionView.swift; sourceTree = ""; };
+ B74BAFF221076AFA0049CD27 /* Sector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sector.swift; sourceTree = ""; };
+ B74BAFF421076AFA0049CD27 /* CircleMenuDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMenuDelegate.swift; sourceTree = ""; };
+ B74BAFF621076AFA0049CD27 /* UIView+Mask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Mask.swift"; sourceTree = ""; };
+ B74BAFF821076AFA0049CD27 /* CircleMenuSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMenuSet.swift; sourceTree = ""; };
+ B74BAFF921076AFA0049CD27 /* CircleMenuFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMenuFactory.swift; sourceTree = ""; };
+ B74BAFFA21076AFA0049CD27 /* CircleMenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMenuItem.swift; sourceTree = ""; };
B750EF012046B24D00A99F9C /* TransferInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransferInfo.swift; sourceTree = ""; };
B750EF032046D69C00A99F9C /* SpeedMesurement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpeedMesurement.swift; sourceTree = ""; };
B750EF052046D7C700A99F9C /* SpeedStringRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpeedStringRepresentable.swift; sourceTree = ""; };
B763DD9220AA1C3400A30B63 /* ContactCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactCellLayout.swift; sourceTree = ""; };
+ B77C11D62109242200CCB42E /* AssigningInterpreterPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssigningInterpreterPresenter.swift; sourceTree = ""; };
+ B77C11D72109242200CCB42E /* AssigningInterpreterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssigningInterpreterViewController.swift; sourceTree = ""; };
+ B77C11D82109242200CCB42E /* AssigningInterpreterProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssigningInterpreterProtocols.swift; sourceTree = ""; };
+ B77C11D92109242200CCB42E /* AssigningInterpreterInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssigningInterpreterInteractor.swift; sourceTree = ""; };
+ B77C11DA2109242200CCB42E /* AssigningInterpreterWireFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssigningInterpreterWireFrame.swift; sourceTree = ""; };
+ B77C11E12109254800CCB42E /* InterpretationTypePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypePresenter.swift; sourceTree = ""; };
+ B77C11E22109254800CCB42E /* InterpretationTypeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypeViewController.swift; sourceTree = ""; };
+ B77C11E32109254800CCB42E /* InterpretationTypeProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypeProtocols.swift; sourceTree = ""; };
+ B77C11E42109254800CCB42E /* InterpretationTypeInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypeInteractor.swift; sourceTree = ""; };
+ B77C11E52109254800CCB42E /* InterpretationTypeWireFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypeWireFrame.swift; sourceTree = ""; };
B79B996D20CA88D100BEF5DE /* InviteFriendsMessageComposeDelegateHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteFriendsMessageComposeDelegateHandler.swift; sourceTree = ""; };
B79D8BCE2020C35300184D5D /* UIEdgeInsets+Adjust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Adjust.swift"; sourceTree = ""; };
+ B79FA0262107731400F286BF /* MarketplacePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketplacePresenter.swift; sourceTree = ""; };
+ B79FA0272107731400F286BF /* MarketplaceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketplaceViewController.swift; sourceTree = ""; };
+ B79FA0282107731400F286BF /* MarketplaceProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketplaceProtocols.swift; sourceTree = ""; };
+ B79FA0292107731400F286BF /* MarketplaceInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketplaceInteractor.swift; sourceTree = ""; };
+ B79FA02A2107731400F286BF /* MarketplaceWireFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketplaceWireFrame.swift; sourceTree = ""; };
+ B79FA03121091ED000F286BF /* InterpretationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationPresenter.swift; sourceTree = ""; };
+ B79FA03221091ED000F286BF /* InterpretationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationViewController.swift; sourceTree = ""; };
+ B79FA03321091ED000F286BF /* InterpretationProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationProtocols.swift; sourceTree = ""; };
+ B79FA03421091ED000F286BF /* InterpretationInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationInteractor.swift; sourceTree = ""; };
+ B79FA03521091ED000F286BF /* InterpretationWireFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationWireFrame.swift; sourceTree = ""; };
+ B7B546AD210D9C8C002DCA55 /* CircleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleView.swift; sourceTree = ""; };
+ B7B546AF210DC68E002DCA55 /* CircleLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleLoadingView.swift; sourceTree = ""; };
+ B7B546B2210DD1EC002DCA55 /* AlertTextFieldViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertTextFieldViewController.swift; sourceTree = ""; };
+ B7EF8ECF210C501F00E0E981 /* InterpretationTypeTableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypeTableDataSource.swift; sourceTree = ""; };
+ B7EF8ED1210C502D00E0E981 /* InterpretationTypeTableDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypeTableDelegate.swift; sourceTree = ""; };
+ B7EF8ED3210C511C00E0E981 /* InterpretationTypeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypeCell.swift; sourceTree = ""; };
+ B7EF8ED6210C598800E0E981 /* InterpretationTypeCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypeCellLayout.swift; sourceTree = ""; };
+ B7EF8ED8210C71E800E0E981 /* InterpretationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationType.swift; sourceTree = ""; };
+ B7EF8EDA210C759300E0E981 /* InterpretationTypeCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationTypeCellModel.swift; sourceTree = ""; };
+ B7EF8EDC210CB0A200E0E981 /* InterpretationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterpretationModel.swift; sourceTree = ""; };
+ B7F4C2AB211995D500E48A98 /* UseCaseValidationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCaseValidationService.swift; sourceTree = ""; };
B7F505182061158800C28FA1 /* SettingsArrowCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsArrowCellViewModel.swift; sourceTree = ""; };
B7F5051A20611A0900C28FA1 /* DownloadSettingsArrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadSettingsArrowModel.swift; sourceTree = ""; };
B7F5051C2061252100C28FA1 /* DataAndStorageItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataAndStorageItemsFactory.swift; sourceTree = ""; };
@@ -4513,7 +4598,7 @@
children = (
264638211FFFE253002590E6 /* RepliesHeaderView */,
26ED2C1720042683002DBBE8 /* RepliesDS.swift */,
- 26ED2C192004276B002DBBE8 /* RepliesTableViewDelegate.swift */,
+ 26ED2C192004276B002DBBE8 /* RepliesCollectionViewDelegate.swift */,
2646381F1FFFD6A2002590E6 /* RepliesVC.swift */,
261F2E2D200EB0AD007D0813 /* RepliesVC+CellDelegate.swift */,
);
@@ -5368,6 +5453,7 @@
3A768DE41ECB3E7600108F7C /* Library */ = {
isa = PBXGroup;
children = (
+ B74BAFED21076ADB0049CD27 /* CircleMenuControl */,
A46679EF20F10B2B00DBC6B4 /* RequestModelFactory */,
A458FAC620ECDAD90075D55E /* Base */,
F11786D520A9AAB1007A9A1B /* Interfaces */,
@@ -5435,7 +5521,6 @@
85082DDB2045A864000AE4B2 /* UserSettings */,
851769D420D584CA008ACF6B /* Amazon */,
B7121EB7205045F300AABBE6 /* MediaDownloadManager.swift */,
- 3A768F061ED4987300108F7C /* VoxService.swift */,
5BC1D37220D3B3D8002A44B3 /* NynjaCommunicatorService.swift */,
E70189BA1F9107AD00CA7005 /* ProximitySensorManager.swift */,
859C42A6205693F100AE3797 /* SoundService */,
@@ -5455,7 +5540,7 @@
8509AC61206A54420089089B /* ResponseResult.swift */,
26D35AB71FD0EFA800A5D513 /* AudioPlayer.swift */,
2631C511207A4C0C00F9AA55 /* AudioRecorder.swift */,
- C921738120BADAFC00519A2D /* TextInputValidationService.swift */,
+ B7F4C2AA211995A500E48A98 /* Validation */,
A42C44E220F340DA00BC3CBB /* StatusCodeManager.swift */,
);
name = Services;
@@ -5618,6 +5703,7 @@
3AC321761EEAC4700068F3C8 /* Models */ = {
isa = PBXGroup;
children = (
+ A46CF04121147B9D0072F185 /* HistoryRequestModel */,
3A62B7D71F4CB9D100F45B51 /* BaseMQTTModel.swift */,
FBCE83D620E523A8003B7558 /* WalletMQTTModel.swift */,
A4F3DAAE2084944900FF71C7 /* RoomModel.swift */,
@@ -5626,7 +5712,6 @@
3A771CB11F193945008D968A /* UpdateRosterModel.swift */,
3AA13C751F2252F900BE5D8F /* SearchModel.swift */,
3A2374DA1F26458300701045 /* FriendRequstModel.swift */,
- 3A3FD2821F39E0A000B6958F /* HistoryRequestModel.swift */,
3A21EFFB1F3B154A00AE61EC /* SendModel.swift */,
265AEA161FE9AFD400AC4806 /* MemberModel.swift */,
263D662C1FE8D03400A509F8 /* TypingModel.swift */,
@@ -5835,6 +5920,10 @@
975DB2471671357A9EEBF65B /* TimeZoneSelector */,
E1BF3560A2E8EE8B02A9A9FB /* DateTimePicker */,
859B86352048224B003272B2 /* Settings */,
+ B79FA025210772CF00F286BF /* Marketplace */,
+ B79FA03021091EA400F286BF /* Interpretation */,
+ B77C11E0210924F800CCB42E /* InterpretationType */,
+ B77C11D5210923F200CCB42E /* AssigningInterpreter */,
);
path = Modules;
sourceTree = "";
@@ -6235,6 +6324,15 @@
path = WireFrame;
sourceTree = "";
};
+ 5A48446021178E6B000657ED /* AlertTextFieldViewController */ = {
+ isa = PBXGroup;
+ children = (
+ B7B546B2210DD1EC002DCA55 /* AlertTextFieldViewController.swift */,
+ 5A48445E21178E33000657ED /* AlertTextFieldViewControllerLayout.swift */,
+ );
+ path = AlertTextFieldViewController;
+ sourceTree = "";
+ };
5B2B64C658BEC3CCC90B0DEF /* WireFrame */ = {
isa = PBXGroup;
children = (
@@ -7396,6 +7494,16 @@
path = Image;
sourceTree = "";
};
+ 854D13D6211B2E6200E139FC /* CollectionView */ = {
+ isa = PBXGroup;
+ children = (
+ 854D13D7211B2E7200E139FC /* MessageCollectionViewLayout.swift */,
+ 8540A330211B34B4007F65AF /* MessageCollectionViewDataSource.swift */,
+ 8540A332211B35A4007F65AF /* MessageCollectionViewDelegate.swift */,
+ );
+ path = CollectionView;
+ sourceTree = "";
+ };
8557987C20931FE8007050B8 /* Menu */ = {
isa = PBXGroup;
children = (
@@ -8861,6 +8969,7 @@
8580BAE020BD99D200239D9D /* InputContent.swift */,
A43B257520AB1DFA00FF8107 /* InputContentProtocol.swift */,
A43B257820AB1DFA00FF8107 /* TextInputContent.swift */,
+ 263409332119CFE2002F8D8F /* RecordContainer.swift */,
A43B257620AB1DFA00FF8107 /* RecordDisplayInputContent.swift */,
A43B257720AB1DFA00FF8107 /* RecordingInputContent.swift */,
A458FABA20EB87BF0075D55E /* ActionContainerContent.swift */,
@@ -9202,6 +9311,7 @@
A45F10C820B4218D00F45004 /* Views */ = {
isa = PBXGroup;
children = (
+ 854D13D6211B2E6200E139FC /* CollectionView */,
5BC1D37E20D3B54B002A44B3 /* CallInfoView */,
A45F10C920B4218D00F45004 /* TableView */,
A45F10F420B4218D00F45004 /* ReplyPreview */,
@@ -9216,8 +9326,6 @@
isa = PBXGroup;
children = (
A45F10CA20B4218D00F45004 /* Cells */,
- A45F10F220B4218D00F45004 /* MessageTableViewDelegate.swift */,
- A45F10F320B4218D00F45004 /* MessageDS.swift */,
);
path = TableView;
sourceTree = "";
@@ -9394,14 +9502,6 @@
name = Roster;
sourceTree = "";
};
- A45F59AE2058316100EAA780 /* VoxImplant */ = {
- isa = PBXGroup;
- children = (
- A45F59AF2058317000EAA780 /* VICallExtension.swift */,
- );
- path = VoxImplant;
- sourceTree = "";
- };
A460324B2105C2CE009783DA /* TextField */ = {
isa = PBXGroup;
children = (
@@ -9518,6 +9618,15 @@
path = LinkField;
sourceTree = "";
};
+ A46CF04121147B9D0072F185 /* HistoryRequestModel */ = {
+ isa = PBXGroup;
+ children = (
+ 3A3FD2821F39E0A000B6958F /* HistoryRequestModel.swift */,
+ A46CF04221147BAE0072F185 /* HistoryRequestModelTypeProtocol.swift */,
+ );
+ name = HistoryRequestModel;
+ sourceTree = "";
+ };
A477CE7B2061235700081D34 /* MessageLink */ = {
isa = PBXGroup;
children = (
@@ -9926,6 +10035,113 @@
path = TableView;
sourceTree = "";
};
+ B745F2DF2109B94300488A91 /* View */ = {
+ isa = PBXGroup;
+ children = (
+ B79FA03221091ED000F286BF /* InterpretationViewController.swift */,
+ B745F2E52109BB0100488A91 /* InterpretationLayout.swift */,
+ B745F2E32109B9E500488A91 /* LanguagePickerDelegate.swift */,
+ 5A48446021178E6B000657ED /* AlertTextFieldViewController */,
+ );
+ path = View;
+ sourceTree = "";
+ };
+ B745F2E02109B99F00488A91 /* Presenter */ = {
+ isa = PBXGroup;
+ children = (
+ B79FA03121091ED000F286BF /* InterpretationPresenter.swift */,
+ );
+ path = Presenter;
+ sourceTree = "";
+ };
+ B745F2E12109B9A800488A91 /* Interactor */ = {
+ isa = PBXGroup;
+ children = (
+ B79FA03421091ED000F286BF /* InterpretationInteractor.swift */,
+ );
+ path = Interactor;
+ sourceTree = "";
+ };
+ B745F2E22109B9B600488A91 /* Wireframe */ = {
+ isa = PBXGroup;
+ children = (
+ B79FA03521091ED000F286BF /* InterpretationWireFrame.swift */,
+ );
+ path = Wireframe;
+ sourceTree = "";
+ };
+ B74BAFED21076ADB0049CD27 /* CircleMenuControl */ = {
+ isa = PBXGroup;
+ children = (
+ B74BAFEE21076AFA0049CD27 /* Core */,
+ B74BAFF521076AFA0049CD27 /* Extension */,
+ B74BAFF721076AFA0049CD27 /* Model */,
+ );
+ path = CircleMenuControl;
+ sourceTree = "";
+ };
+ B74BAFEE21076AFA0049CD27 /* Core */ = {
+ isa = PBXGroup;
+ children = (
+ B74BAFEF21076AFA0049CD27 /* CircleMenu.swift */,
+ B74BAFF021076AFA0049CD27 /* Sector */,
+ B74BAFF421076AFA0049CD27 /* CircleMenuDelegate.swift */,
+ );
+ path = Core;
+ sourceTree = "";
+ };
+ B74BAFF021076AFA0049CD27 /* Sector */ = {
+ isa = PBXGroup;
+ children = (
+ B74BAFF121076AFA0049CD27 /* SectionView.swift */,
+ B74BAFF221076AFA0049CD27 /* Sector.swift */,
+ );
+ path = Sector;
+ sourceTree = "";
+ };
+ B74BAFF521076AFA0049CD27 /* Extension */ = {
+ isa = PBXGroup;
+ children = (
+ B74BAFF621076AFA0049CD27 /* UIView+Mask.swift */,
+ );
+ path = Extension;
+ sourceTree = "";
+ };
+ B74BAFF721076AFA0049CD27 /* Model */ = {
+ isa = PBXGroup;
+ children = (
+ B74BAFF821076AFA0049CD27 /* CircleMenuSet.swift */,
+ B74BAFF921076AFA0049CD27 /* CircleMenuFactory.swift */,
+ B74BAFFA21076AFA0049CD27 /* CircleMenuItem.swift */,
+ );
+ path = Model;
+ sourceTree = "";
+ };
+ B77C11D5210923F200CCB42E /* AssigningInterpreter */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11D82109242200CCB42E /* AssigningInterpreterProtocols.swift */,
+ B7EF8EE0210CDCCE00E0E981 /* View */,
+ B7EF8EE1210CDCDF00E0E981 /* Presenter */,
+ B7EF8EE2210CDCF200E0E981 /* Interactor */,
+ B7EF8EE3210CDCFF00E0E981 /* Wireframe */,
+ );
+ path = AssigningInterpreter;
+ sourceTree = "";
+ };
+ B77C11E0210924F800CCB42E /* InterpretationType */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11E32109254800CCB42E /* InterpretationTypeProtocols.swift */,
+ B7EF8ED8210C71E800E0E981 /* InterpretationType.swift */,
+ B7EF8ECA210C4E1400E0E981 /* View */,
+ B7EF8ECB210C4E1E00E0E981 /* Presenter */,
+ B7EF8ECC210C4E2800E0E981 /* Interactor */,
+ B7EF8ECD210C4E3000E0E981 /* Wireframe */,
+ );
+ path = InterpretationType;
+ sourceTree = "";
+ };
B79B996F20CA89BA00BEF5DE /* MessageComposeHandler */ = {
isa = PBXGroup;
children = (
@@ -9934,6 +10150,31 @@
path = MessageComposeHandler;
sourceTree = "";
};
+ B79FA025210772CF00F286BF /* Marketplace */ = {
+ isa = PBXGroup;
+ children = (
+ B79FA0282107731400F286BF /* MarketplaceProtocols.swift */,
+ B79FA0272107731400F286BF /* MarketplaceViewController.swift */,
+ B79FA0262107731400F286BF /* MarketplacePresenter.swift */,
+ B79FA0292107731400F286BF /* MarketplaceInteractor.swift */,
+ B79FA02A2107731400F286BF /* MarketplaceWireFrame.swift */,
+ );
+ path = Marketplace;
+ sourceTree = "";
+ };
+ B79FA03021091EA400F286BF /* Interpretation */ = {
+ isa = PBXGroup;
+ children = (
+ B79FA03321091ED000F286BF /* InterpretationProtocols.swift */,
+ B7EF8EDC210CB0A200E0E981 /* InterpretationModel.swift */,
+ B745F2DF2109B94300488A91 /* View */,
+ B745F2E02109B99F00488A91 /* Presenter */,
+ B745F2E12109B9A800488A91 /* Interactor */,
+ B745F2E22109B9B600488A91 /* Wireframe */,
+ );
+ path = Interpretation;
+ sourceTree = "";
+ };
B7B3AAAF204D3F1400756B77 /* TableHeaderFooter */ = {
isa = PBXGroup;
children = (
@@ -9942,6 +10183,111 @@
path = TableHeaderFooter;
sourceTree = "";
};
+ B7B546B1210DCE24002DCA55 /* CircleLoadingView */ = {
+ isa = PBXGroup;
+ children = (
+ B7B546AD210D9C8C002DCA55 /* CircleView.swift */,
+ B7B546AF210DC68E002DCA55 /* CircleLoadingView.swift */,
+ );
+ path = CircleLoadingView;
+ sourceTree = "";
+ };
+ B7EF8ECA210C4E1400E0E981 /* View */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11E22109254800CCB42E /* InterpretationTypeViewController.swift */,
+ B7EF8ECE210C4FE500E0E981 /* TableView */,
+ );
+ path = View;
+ sourceTree = "";
+ };
+ B7EF8ECB210C4E1E00E0E981 /* Presenter */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11E12109254800CCB42E /* InterpretationTypePresenter.swift */,
+ );
+ path = Presenter;
+ sourceTree = "";
+ };
+ B7EF8ECC210C4E2800E0E981 /* Interactor */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11E42109254800CCB42E /* InterpretationTypeInteractor.swift */,
+ );
+ path = Interactor;
+ sourceTree = "";
+ };
+ B7EF8ECD210C4E3000E0E981 /* Wireframe */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11E52109254800CCB42E /* InterpretationTypeWireFrame.swift */,
+ );
+ path = Wireframe;
+ sourceTree = "";
+ };
+ B7EF8ECE210C4FE500E0E981 /* TableView */ = {
+ isa = PBXGroup;
+ children = (
+ B7EF8ECF210C501F00E0E981 /* InterpretationTypeTableDataSource.swift */,
+ B7EF8ED1210C502D00E0E981 /* InterpretationTypeTableDelegate.swift */,
+ B7EF8ED5210C593E00E0E981 /* Cell */,
+ );
+ path = TableView;
+ sourceTree = "";
+ };
+ B7EF8ED5210C593E00E0E981 /* Cell */ = {
+ isa = PBXGroup;
+ children = (
+ B7EF8ED3210C511C00E0E981 /* InterpretationTypeCell.swift */,
+ B7EF8EDA210C759300E0E981 /* InterpretationTypeCellModel.swift */,
+ B7EF8ED6210C598800E0E981 /* InterpretationTypeCellLayout.swift */,
+ );
+ path = Cell;
+ sourceTree = "";
+ };
+ B7EF8EE0210CDCCE00E0E981 /* View */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11D72109242200CCB42E /* AssigningInterpreterViewController.swift */,
+ B742053E211448BD004FDE16 /* AsigningInterpreterLayout.swift */,
+ B7B546B1210DCE24002DCA55 /* CircleLoadingView */,
+ );
+ path = View;
+ sourceTree = "";
+ };
+ B7EF8EE1210CDCDF00E0E981 /* Presenter */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11D62109242200CCB42E /* AssigningInterpreterPresenter.swift */,
+ );
+ path = Presenter;
+ sourceTree = "";
+ };
+ B7EF8EE2210CDCF200E0E981 /* Interactor */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11D92109242200CCB42E /* AssigningInterpreterInteractor.swift */,
+ );
+ path = Interactor;
+ sourceTree = "";
+ };
+ B7EF8EE3210CDCFF00E0E981 /* Wireframe */ = {
+ isa = PBXGroup;
+ children = (
+ B77C11DA2109242200CCB42E /* AssigningInterpreterWireFrame.swift */,
+ );
+ path = Wireframe;
+ sourceTree = "";
+ };
+ B7F4C2AA211995A500E48A98 /* Validation */ = {
+ isa = PBXGroup;
+ children = (
+ C921738120BADAFC00519A2D /* TextInputValidationService.swift */,
+ B7F4C2AB211995D500E48A98 /* UseCaseValidationService.swift */,
+ );
+ path = Validation;
+ sourceTree = "";
+ };
B8DCBB4ACE8A650987F2D234 /* Presenter */ = {
isa = PBXGroup;
children = (
@@ -10889,7 +11235,6 @@
A4213AF120D923DD00B6BE7D /* Photos */,
A43B25DD20AB1F5C00FF8107 /* RawRepresentable+Localized.swift */,
A4597E0F20A4651600F7F0DE /* Formatters */,
- A45F59AE2058316100EAA780 /* VoxImplant */,
F105C6AA20A0DCB70091786A /* UIImagePickerControllerCameraCaptureMode */,
A416DA5E207533FB00FBF1BA /* CoreLocation */,
2648C3E52069B48F00863614 /* UITextField+Extension.swift */,
@@ -12178,7 +12523,6 @@
3578099F1F9765CF00C9680C /* Sources */,
357809A01F9765CF00C9680C /* Frameworks */,
357809A11F9765CF00C9680C /* Resources */,
- 61CA315D0DB89815DE10E66F /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -12314,8 +12658,6 @@
knownRegions = (
en,
Base,
- uk,
- ru,
);
mainGroup = 3ABCE8E41EC9330D00A80B15;
productRefGroup = 3ABCE8EE1EC9330D00A80B15 /* Products */;
@@ -12446,21 +12788,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
- 61CA315D0DB89815DE10E66F /* [CP] Copy Pods Resources */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "[CP] Copy Pods Resources";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Nynja-Share/Pods-Nynja-Share-resources.sh\"\n";
- showEnvVarsInLog = 0;
- };
6A031053FD7153DBCCD6098C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -12504,11 +12831,8 @@
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
"${BUILT_PRODUCTS_DIR}/SQLCipher/SQLCipher.framework",
"${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework",
- "${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework",
"${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework",
"${BUILT_PRODUCTS_DIR}/SwiftyTimer/SwiftyTimer.framework",
- "${PODS_ROOT}/VoxImplantSDK/VoxImplant.framework",
- "${PODS_ROOT}/VoxImplantWebRTC/WebRTC.framework",
"${BUILT_PRODUCTS_DIR}/libPhoneNumber-iOS/libPhoneNumber_iOS.framework",
);
name = "[CP] Embed Pods Frameworks";
@@ -12531,11 +12855,8 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SQLCipher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyTimer.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/VoxImplant.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libPhoneNumber_iOS.framework",
);
runOnlyForDeploymentPostprocessing = 0;
@@ -12564,10 +12885,7 @@
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
"${BUILT_PRODUCTS_DIR}/SQLCipher/SQLCipher.framework",
"${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework",
- "${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework",
"${BUILT_PRODUCTS_DIR}/SwiftyTimer/SwiftyTimer.framework",
- "${PODS_ROOT}/VoxImplantSDK/VoxImplant.framework",
- "${PODS_ROOT}/VoxImplantWebRTC/WebRTC.framework",
"${BUILT_PRODUCTS_DIR}/libPhoneNumber-iOS/libPhoneNumber_iOS.framework",
);
name = "[CP] Embed Pods Frameworks";
@@ -12586,10 +12904,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SQLCipher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyTimer.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/VoxImplant.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libPhoneNumber_iOS.framework",
);
runOnlyForDeploymentPostprocessing = 0;
@@ -12987,6 +13302,7 @@
buildActionMask = 2147483647;
files = (
A432CF1B20B4347D00993AFB /* MaterialTextView.swift in Sources */,
+ B7EF8ED0210C501F00E0E981 /* InterpretationTypeTableDataSource.swift in Sources */,
F11DF06620BD96D000F3E005 /* GalleryFilterGroupType.swift in Sources */,
F11786F120AC5482007A9A1B /* ServiceFactory.swift in Sources */,
4B7B81C62044790700C2EFCF /* TimeZoneLocal.swift in Sources */,
@@ -13043,6 +13359,7 @@
E7EED2341F740BF3005DAE20 /* ChatsItem.swift in Sources */,
260313A620A0A4BA009AC66D /* SwitchableActionCellViewModel.swift in Sources */,
A42D51AE206A361400EEB952 /* Message.swift in Sources */,
+ B79FA02C2107731400F286BF /* MarketplaceViewController.swift in Sources */,
C9405159204C91E000D72B04 /* DataAndStorageTableDataSource.swift in Sources */,
A45F112920B4218D00F45004 /* MessageContentAppearance.swift in Sources */,
C9C694F9201FA4AB00A57297 /* SlideAnimatedTransitioning.swift in Sources */,
@@ -13083,6 +13400,7 @@
859B862C204820DC003272B2 /* ThemePickerPresenter.swift in Sources */,
2603139B20A0A4BA009AC66D /* LanguageSelectorViewController.swift in Sources */,
2686D3201FC3E39C0079CB75 /* ContentNavigationVC.swift in Sources */,
+ B7EF8EDB210C759400E0E981 /* InterpretationTypeCellModel.swift in Sources */,
0062D93F2062EC4100B915AC /* InviteFriendsCellLayout.swift in Sources */,
001F0CF5202C38FA006B4304 /* TimeZoneCell.swift in Sources */,
C9C694FD201FA55800A57297 /* SwipeBackHelper.swift in Sources */,
@@ -13110,7 +13428,6 @@
A42D51B4206A361400EEB952 /* container.swift in Sources */,
265F5D29209B6E1F008ACCC8 /* ParticipantsViewControllerLayout.swift in Sources */,
00E86469204D519600844FF1 /* LanguageSettingsItemsFactory.swift in Sources */,
- 3A768F071ED4987300108F7C /* VoxService.swift in Sources */,
A4F3DAB22084949F00FF71C7 /* MessageExtension+BERT.swift in Sources */,
F105C6BA20A1347E0091786A /* PhotoPreviewProtocols.swift in Sources */,
E721306F1F9A384900D88103 /* AlignableLabel.swift in Sources */,
@@ -13189,10 +13506,12 @@
859C42AA2056B05D00AE3797 /* SoundBundle.swift in Sources */,
265AEA151FE9AFA700AC4806 /* MemberHandler.swift in Sources */,
E7C36C311FC4399B00740630 /* DBService.swift in Sources */,
+ B77C11DE2109242200CCB42E /* AssigningInterpreterInteractor.swift in Sources */,
C940514D204C7FAF00D72B04 /* DataAndStorageWireFrame.swift in Sources */,
26DCB22D2064B872001EF0AB /* ContactsProtocols.swift in Sources */,
A42D52CA206A53AB00EEB952 /* muc_Spec.swift in Sources */,
850C301D204DA87A00DB26C2 /* PrivacyListProtocols.swift in Sources */,
+ B77C11DF2109242200CCB42E /* AssigningInterpreterWireFrame.swift in Sources */,
8520040D20D513B8007C0036 /* OpponentMessageStickerRepliedView.swift in Sources */,
A458FAC420EBA58A0075D55E /* MuteChatService.swift in Sources */,
2661D12F1F373D1700F3E125 /* BorderView.swift in Sources */,
@@ -13224,6 +13543,7 @@
26C1A3F02031D9E60009F7F0 /* OtherUserTableViewDS.swift in Sources */,
A45F110D20B4218D00F45004 /* SendingStatus.swift in Sources */,
A45F114720B421AB00F45004 /* MessageExtension.swift in Sources */,
+ B79FA02E2107731400F286BF /* MarketplaceInteractor.swift in Sources */,
F10B0E1B20B4412100528E7A /* GalleryViewController.swift in Sources */,
E78EFB891FC867B200C44975 /* DBMuc.swift in Sources */,
8EE53A12200543F40079CCA8 /* GroupCollectionConstants.swift in Sources */,
@@ -13265,7 +13585,6 @@
2648C41E2069B5B300863614 /* ChangeNumberItemsFactory.swift in Sources */,
260313AC20A0A4BA009AC66D /* LanguageSettings+Helper.swift in Sources */,
A45F112D20B4218D00F45004 /* MessageContentView.swift in Sources */,
- A45F113520B4218D00F45004 /* MessageTableViewDelegate.swift in Sources */,
A42D52C3206A53AA00EEB952 /* Star_Spec.swift in Sources */,
26D35AB81FD0EFA800A5D513 /* AudioPlayer.swift in Sources */,
853FB0702049B396000996C5 /* SupportItemsFactory.swift in Sources */,
@@ -13279,11 +13598,13 @@
0062D9472062EC4100B915AC /* InviteFriendsViewController.swift in Sources */,
4B2D063C202E1A1500010A0C /* ContactsExpandedItemsFactory.swift in Sources */,
85D66A2120BD970400FBD803 /* BBCodeEntity.swift in Sources */,
+ B74BB00221076AFA0049CD27 /* CircleMenuFactory.swift in Sources */,
4B8996EC204EF35200DCB183 /* MessageActionDAO.swift in Sources */,
E791178A1F97874D00462D68 /* GradientView.swift in Sources */,
E79117901F97A3BC00462D68 /* ProfileDetailsView.swift in Sources */,
265F5D24209B6987008ACCC8 /* Place.swift in Sources */,
E734831A1F9F39400090A4DB /* CellModel.swift in Sources */,
+ B74BB00321076AFA0049CD27 /* CircleMenuItem.swift in Sources */,
6D485DDF1F0ACA4700E12FB1 /* UIImageView+Rounded.swift in Sources */,
26CD3FDB2104D19D00597E62 /* TranscribeShortAudioOperation.swift in Sources */,
40C2631343E285717633ADFA /* AuthPresenter.swift in Sources */,
@@ -13292,6 +13613,7 @@
C940514A204C7FAF00D72B04 /* DataAndStorageViewController.swift in Sources */,
26C1A3E32031A95D0009F7F0 /* OtherUserProtocols.swift in Sources */,
8503B528205046A6006F0593 /* NotificationSettingsInteractor.swift in Sources */,
+ B79FA02F2107731400F286BF /* MarketplaceWireFrame.swift in Sources */,
A4C9300520B323B700D6FB0F /* Room+BaseChatModel.swift in Sources */,
E73483211F9F78DC0090A4DB /* ProfileSectionFooterView.swift in Sources */,
3AE0A84B1F20321A008A04F3 /* Wheel.swift in Sources */,
@@ -13309,6 +13631,7 @@
FBCE83D520E52397003B7558 /* MQTTServiceWallet.swift in Sources */,
853D55B820CE72C60080659F /* StickerContentDataSource.swift in Sources */,
85CB25DC20D723D300D5E565 /* StickerPackDAOProtocol.swift in Sources */,
+ 8540A333211B35A4007F65AF /* MessageCollectionViewDelegate.swift in Sources */,
4B06D3152028A537003B275B /* ContactsItemsFactory.swift in Sources */,
A48C154420EF7A15002DA994 /* LinkHandler.swift in Sources */,
A45F113320B4218D00F45004 /* MessageCellFactory.swift in Sources */,
@@ -13371,6 +13694,7 @@
4B4266C3204D923400194BC1 /* Array+UIView.swift in Sources */,
A4CB15232103735200C3B68B /* JDFileBasedMechanism.swift in Sources */,
3A771CAA1F191B38008D968A /* ProfileHandler.swift in Sources */,
+ B74BAFFC21076AFA0049CD27 /* SectionView.swift in Sources */,
E78EFB871FC867A900C44975 /* DBP2p.swift in Sources */,
8ED0F3CF1FBC5CF2004916AB /* GroupsListPresenter.swift in Sources */,
850A2BB22035AE5E00D68FDF /* ForwardCellViewModel.swift in Sources */,
@@ -13379,6 +13703,7 @@
A45F112E20B4218D00F45004 /* MessageContentProtocol.swift in Sources */,
0008E9132032D5AC003E316E /* MQTTServiceSchedule.swift in Sources */,
A432CF1A20B4347D00993AFB /* MaterialTextInput.swift in Sources */,
+ B77C11E62109254800CCB42E /* InterpretationTypePresenter.swift in Sources */,
B750EF062046D7C700A99F9C /* SpeedStringRepresentable.swift in Sources */,
8580BAE220BD99D200239D9D /* InputContent.swift in Sources */,
A43B25AD20AB1DFA00FF8107 /* MyTextField.swift in Sources */,
@@ -13458,6 +13783,7 @@
E7C1D3681F683A7D007D4E1E /* MainNavigationItem.swift in Sources */,
E7DD28311F8B6CB200174650 /* LoginViewLayout.swift in Sources */,
E749C5671FD4490E0048DEAC /* DefaultMessageProcessingManager.swift in Sources */,
+ B7B546B0210DC68E002DCA55 /* CircleLoadingView.swift in Sources */,
85482848204EA56600DCBEC8 /* PrivacyListDataSource.swift in Sources */,
2600DAD6203479D000A2D4F7 /* ReturnToHomeHeaderView.swift in Sources */,
F117FBD520FF9DAF00BA1F82 /* MediaInfoView.swift in Sources */,
@@ -13573,7 +13899,6 @@
FBCE841420E525A6003B7558 /* NetworkService.swift in Sources */,
A409B1CF2108D48E0051C20B /* QueryFactory.swift in Sources */,
A42D52B7206A53AA00EEB952 /* reader_Spec.swift in Sources */,
- A45F113620B4218D00F45004 /* MessageDS.swift in Sources */,
F119E66A20D24B960043A532 /* MultiplePreviewProtocols.swift in Sources */,
850FC611203312FA00832D87 /* ForwardSelectorViewControllerLayout.swift in Sources */,
0062D93D2062EC4100B915AC /* InviteFriendsWireframe.swift in Sources */,
@@ -13583,6 +13908,7 @@
A42D52CB206A53AB00EEB952 /* CDR_Spec.swift in Sources */,
2603139220A0A4B9009AC66D /* LanguageSettingProtocol.swift in Sources */,
F105C6BD20A1347E0091786A /* PhotoPreviewViewController.swift in Sources */,
+ B79FA03921091ED000F286BF /* InterpretationInteractor.swift in Sources */,
26A856242074C50D00C642EA /* ActionsView+ScheduleAction.swift in Sources */,
B1B8ED3EDB12866323C9EE74 /* QRCodeGeneratorInteractor.swift in Sources */,
85F0866220D6412300A7762E /* RemoteStorageDestination.swift in Sources */,
@@ -13600,6 +13926,7 @@
A45F112120B4218D00F45004 /* InfoDateView.swift in Sources */,
9BD8E3F820EF8874001384EC /* CallInProgressNavigationController.swift in Sources */,
A4688DFA20650FF50013660D /* DBObserver.swift in Sources */,
+ 263409342119CFE2002F8D8F /* RecordContainer.swift in Sources */,
853FB0682049B193000996C5 /* SupportProtocols.swift in Sources */,
A42D51B1206A361400EEB952 /* Typing.swift in Sources */,
A42D52D3206A53AB00EEB952 /* Test_Spec.swift in Sources */,
@@ -13623,6 +13950,7 @@
26C0C1EE2073DE1600C530DA /* ForwardSelectorProtocols+ShareExt.swift in Sources */,
8511D3742034596E00B2A620 /* Collection+ViewLayout.swift in Sources */,
F10B0E1720B4401500528E7A /* GalleryPresenter.swift in Sources */,
+ B7EF8ED9210C71E800E0E981 /* InterpretationType.swift in Sources */,
6D5157D21F30B822002A27DB /* MicrophoneView.swift in Sources */,
260313AF20A0A50D009AC66D /* TranslationService.swift in Sources */,
A42D51AD206A361400EEB952 /* cur.swift in Sources */,
@@ -13728,6 +14056,7 @@
263D66271FE829CC00A509F8 /* RoomExtension+BERT.swift in Sources */,
A45F112320B4218D00F45004 /* MessageVoiceView.swift in Sources */,
A44B4D5520CE9BDF00CA700A /* SwitchCellViewModel.swift in Sources */,
+ B74BAFFB21076AFA0049CD27 /* CircleMenu.swift in Sources */,
F117872520ACF2DB007A9A1B /* QualityName.swift in Sources */,
A45F111B20B4218D00F45004 /* MessagePlaceView.swift in Sources */,
A42D52C4206A53AA00EEB952 /* error2_Spec.swift in Sources */,
@@ -13739,6 +14068,8 @@
A42D52B9206A53AA00EEB952 /* Profile_Spec.swift in Sources */,
26DCB25420692237001EF0AB /* Array+Feature.swift in Sources */,
F1607B2E20B2DE8A00BDF60A /* CameraQRPreviewInteractor.swift in Sources */,
+ B77C11EA2109254800CCB42E /* InterpretationTypeWireFrame.swift in Sources */,
+ B7F4C2AC211995D500E48A98 /* UseCaseValidationService.swift in Sources */,
850571222050B0AD00EDF794 /* NotificationAlertSoundsViewController.swift in Sources */,
6D6731101F29E1F4003E8F8F /* BottomCallView.swift in Sources */,
26245F40204EF58E00C8D3DD /* BaseViewProtocol.swift in Sources */,
@@ -13752,6 +14083,7 @@
00E9825E205FDB1A008BF03D /* AuthExtension.swift in Sources */,
A45F116020B422AF00F45004 /* Message+DB.swift in Sources */,
8E23E086200614AB00A59B8C /* GroupVideosCell.swift in Sources */,
+ B74BB00121076AFA0049CD27 /* CircleMenuSet.swift in Sources */,
A4679B8E20B2DA610021FE9C /* SelectorAvatarCell.swift in Sources */,
2648C41B2069B52100863614 /* ChangeNumberView.swift in Sources */,
2648C40E2069B52100863614 /* ChangeNumberStep1Interactor.swift in Sources */,
@@ -13810,7 +14142,7 @@
3A1AAFCE1F3DF0470098780A /* DateExtensions.swift in Sources */,
26C061C01FEAA04A00A2EBE4 /* FeatureExtension+BERT.swift in Sources */,
2651093F20ADB81100F1B38B /* NotificationSettingProtocol.swift in Sources */,
- 26ED2C1A2004276B002DBBE8 /* RepliesTableViewDelegate.swift in Sources */,
+ 26ED2C1A2004276B002DBBE8 /* RepliesCollectionViewDelegate.swift in Sources */,
8514D52220EE48930002378A /* NynjaContextMenuItemsFactory+Design.swift in Sources */,
A45F112820B4218D00F45004 /* MessageViewFactory.swift in Sources */,
F117871D20ACF1D0007A9A1B /* CameraSettingsService.swift in Sources */,
@@ -13841,6 +14173,8 @@
26610F5B2015476C00609F77 /* LocationFullWheelItemModel.swift in Sources */,
F117872920ACF2DB007A9A1B /* CameraSetting.swift in Sources */,
E77D58971F98B91600FBE926 /* ProfileTableViewDelegate.swift in Sources */,
+ B79FA02D2107731400F286BF /* MarketplaceProtocols.swift in Sources */,
+ B77C11DC2109242200CCB42E /* AssigningInterpreterViewController.swift in Sources */,
A45F111420B4218D00F45004 /* SystemCell.swift in Sources */,
26C1A3E92031AAA30009F7F0 /* OtherUserViewController.swift in Sources */,
2606F3BC20BFE20500CF7F15 /* MessageInteractor+Translation.swift in Sources */,
@@ -13862,6 +14196,7 @@
F10AFEB420F7B1B000C7CE83 /* WheelPreviewProtocol.swift in Sources */,
A45F111220B4218D00F45004 /* MessageVC.swift in Sources */,
26DCB256206924B3001EF0AB /* FeatureFactory.swift in Sources */,
+ B74BAFFF21076AFA0049CD27 /* CircleMenuDelegate.swift in Sources */,
A432CF1420B4347D00993AFB /* InputInfo.swift in Sources */,
85D66A0720BD963C00FBD803 /* MessagePayloadRenderer.swift in Sources */,
8EE9BC181FFBE0F800ECBBC7 /* Array+Message.swift in Sources */,
@@ -13888,6 +14223,7 @@
E72906E72011156B007C5C5B /* UITableViewExtensions.swift in Sources */,
2603139320A0A4B9009AC66D /* LanguageSelectorProtocols.swift in Sources */,
E7EE893B1F83CEF5009D37F9 /* MainViewControllerLayout.swift in Sources */,
+ 5A48445F21178E33000657ED /* AlertTextFieldViewControllerLayout.swift in Sources */,
32868DDD1F31CB5D0028B260 /* ChatsListPresenter.swift in Sources */,
A42D519D206A361400EEB952 /* muc.swift in Sources */,
A407348C20B712E9005762D5 /* UIView+Hierarchy.swift in Sources */,
@@ -13926,6 +14262,7 @@
A4679B8620B2DA550021FE9C /* Array+Grouped.swift in Sources */,
A432CF1D20B4427000993AFB /* MTIConfigProtocol.swift in Sources */,
4B06D3102028A472003B275B /* P2pChatsItemsFactory.swift in Sources */,
+ B77C11E92109254800CCB42E /* InterpretationTypeInteractor.swift in Sources */,
26DCB23E2064B9A7001EF0AB /* ContactsWireframe.swift in Sources */,
853E594F20D6AED2007799B9 /* Desc+Messages.swift in Sources */,
A460324F2105C9A1009783DA /* InputsCachePolicy.swift in Sources */,
@@ -13951,6 +14288,7 @@
FE58F9B3208F0583004AFDD3 /* DBMessageEditAction.swift in Sources */,
26FA420C2017AE3300E6F6EC /* StarMessageCellLayout.swift in Sources */,
2633EF6E205212F700DB3868 /* MemberDAOProtocol.swift in Sources */,
+ B7EF8EDD210CB0A200E0E981 /* InterpretationModel.swift in Sources */,
E79117921F97A48900462D68 /* ProfileDetailsViewLayout.swift in Sources */,
8595E0DC204863DB00178171 /* CarouselPickerCellModel.swift in Sources */,
2648C41C2069B52100863614 /* ChangeNumberStep2Interactor.swift in Sources */,
@@ -13970,9 +14308,11 @@
3AE0A84C1F20321A008A04F3 /* WheelItemModel.swift in Sources */,
A42D51BB206A361400EEB952 /* userTask.swift in Sources */,
2686D3271FC640440079CB75 /* DBSyncFile.swift in Sources */,
+ B742053F211448BD004FDE16 /* AsigningInterpreterLayout.swift in Sources */,
A40F18B920BFD81B0091B09E /* EmptyStateView.swift in Sources */,
06E67B3C3ED6CE5F6A913762 /* MainProtocols.swift in Sources */,
85082DDF2045A8C2000AE4B2 /* WheelPosition.swift in Sources */,
+ B79FA03721091ED000F286BF /* InterpretationViewController.swift in Sources */,
F1607B2820B2DE4D00BDF60A /* CameraQRPreviewPresenter.swift in Sources */,
A42D51AB206A361400EEB952 /* Person.swift in Sources */,
A415131E20DBD10400C2C01F /* MessageLinkDAO.swift in Sources */,
@@ -13986,6 +14326,7 @@
4B8996CD204ED33400DCB183 /* StarDAOProtocol.swift in Sources */,
85D669EC20BD962800FBD803 /* MessageVCLayout.swift in Sources */,
8EDDB08A200529C6000B7EC2 /* GroupStorageCollectionVC.swift in Sources */,
+ B74BB00021076AFA0049CD27 /* UIView+Mask.swift in Sources */,
A42D51CD206A361400EEB952 /* operation.swift in Sources */,
265F5D25209B6987008ACCC8 /* LocationType.swift in Sources */,
4B2D063A202DDA2000010A0C /* BackSwipable.swift in Sources */,
@@ -14001,6 +14342,7 @@
A408A0BA20C174040029F54B /* ChannelsListPresenter.swift in Sources */,
850FC60F203310D200832D87 /* SelectionAvatarView.swift in Sources */,
8E6C4BE41FF6A7AD009C8374 /* GroupStorageListItems.swift in Sources */,
+ B7B546B3210DD1EC002DCA55 /* AlertTextFieldViewController.swift in Sources */,
F117871220ACF018007A9A1B /* CameraSettingsProtocols.swift in Sources */,
8503B526205046A6006F0593 /* NotificationSettingsViewController.swift in Sources */,
8580BAC820BD983400239D9D /* MentionCounterInteractive.swift in Sources */,
@@ -14045,12 +14387,15 @@
8557988720932401007050B8 /* StickerStaticMenuActionCollectionViewCell.swift in Sources */,
00E9825A205FC324008BF03D /* SessionDescView.swift in Sources */,
E7229A4A1F8CAD72003AEE04 /* TutorialViewControllerLayout.swift in Sources */,
+ B79FA03821091ED000F286BF /* InterpretationProtocols.swift in Sources */,
85CB25DA20D723B900D5E565 /* StickerPackDAO.swift in Sources */,
2603068D20FFF9CA00C10DD9 /* MessageCallView.swift in Sources */,
E70402BD1FF6972B00182D81 /* BaseView.swift in Sources */,
A45F59AD2058263F00EAA780 /* RosterDAO.swift in Sources */,
4C5EEA13EBC6A8398F08DCD1 /* MainWireframe.swift in Sources */,
A42D52CF206A53AB00EEB952 /* Person_Spec.swift in Sources */,
+ B77C11E82109254800CCB42E /* InterpretationTypeProtocols.swift in Sources */,
+ B77C11E72109254800CCB42E /* InterpretationTypeViewController.swift in Sources */,
005886C92030F13100FE2E89 /* NynjaTimeHoursDelegate.swift in Sources */,
4BAB9CE02035CAE700385520 /* ScheduleInfo.swift in Sources */,
8ECC067E1FC5BCC6002CF225 /* TransferManager.swift in Sources */,
@@ -14118,7 +14463,6 @@
851EBD7F20B418890065C644 /* StickersInputView.swift in Sources */,
267C1D5920404EDB0087808F /* AlertImageViewController.swift in Sources */,
0008E91B20333A38003E316E /* JobHandler.swift in Sources */,
- A45F59B02058317000EAA780 /* VICallExtension.swift in Sources */,
B750EF042046D69C00A99F9C /* SpeedMesurement.swift in Sources */,
853D55B220CE66180080659F /* StickersInputData.swift in Sources */,
260313A320A0A4BA009AC66D /* ActionCellViewModel.swift in Sources */,
@@ -14155,6 +14499,7 @@
E7E6E3DE1FB2F37900401D9E /* ParticipantsDelegate.swift in Sources */,
A42D51AA206A361400EEB952 /* error.swift in Sources */,
628E2C26BE0854DB1DF64990 /* SplashWireframe.swift in Sources */,
+ B77C11DD2109242200CCB42E /* AssigningInterpreterProtocols.swift in Sources */,
26FA420E201812D600E6F6EC /* StarTableDS.swift in Sources */,
F10B0E2820B519B700528E7A /* GalleryPhotoItemCollectionViewCell.swift in Sources */,
E7C36C351FC448CB00740630 /* DBFeature.swift in Sources */,
@@ -14187,6 +14532,7 @@
73BFE52F809536A538E6A55E /* ImagePreviewViewController.swift in Sources */,
850C0B2620E00C3E003341D0 /* UIScreen+Keyboard.swift in Sources */,
8503B51B205036F2006F0593 /* BaseNynjaButton.swift in Sources */,
+ A46CF04321147BAE0072F185 /* HistoryRequestModelTypeProtocol.swift in Sources */,
8538012C2052E29D002C6960 /* SoundTableHeaderView.swift in Sources */,
3A8045D31F60C8E200AED866 /* MQTTServiceProfile.swift in Sources */,
B7F505192061158800C28FA1 /* SettingsArrowCellViewModel.swift in Sources */,
@@ -14195,6 +14541,7 @@
E09CECE79892CABEF8793389 /* ImagePreviewInteractor.swift in Sources */,
2603139820A0A4B9009AC66D /* LangCellViewModel.swift in Sources */,
A4BCEC6C20DBF2A40078B076 /* Link+DB.swift in Sources */,
+ B79FA02B2107731400F286BF /* MarketplacePresenter.swift in Sources */,
0008E9282036F480003E316E /* ScheduledMessage.swift in Sources */,
851872C120CD45B3007CD6CA /* StickersProvider.swift in Sources */,
26E7D04C1FCB8A72001C69B7 /* UIImageView+SetImage.swift in Sources */,
@@ -14255,6 +14602,7 @@
54FFFD58388E2B660C1E5A05 /* MapPresenter.swift in Sources */,
A42D52D6206A53AB00EEB952 /* serviceTask_Spec.swift in Sources */,
0AB08BA89A51118248FA3233 /* MapInteractor.swift in Sources */,
+ 8540A331211B34B4007F65AF /* MessageCollectionViewDataSource.swift in Sources */,
A48C154220EF76EE002DA994 /* LinkExtension.swift in Sources */,
A42D52AD206A53AA00EEB952 /* log_Spec.swift in Sources */,
F6150A15F8A3E399EEB2C724 /* MapWireframe.swift in Sources */,
@@ -14275,6 +14623,7 @@
A45F110320B4218D00F45004 /* MessagePresenter.swift in Sources */,
26541F742007B9A200AAEACF /* MessageActionTable.swift in Sources */,
8502DB532061030100613C8C /* WheelPositionCollectionViewCell.swift in Sources */,
+ B79FA03621091ED000F286BF /* InterpretationPresenter.swift in Sources */,
8FD597B66534DDBD1DDB58AC /* FavoritesInteractor.swift in Sources */,
82FCF48AA4A8C04CC8B0B5B6 /* FavoritesWireframe.swift in Sources */,
26AD28391FFB0AF9009E4580 /* StorageObserver.swift in Sources */,
@@ -14338,6 +14687,7 @@
2689CDEA20C48AD8007816B9 /* TranslationManualView.swift in Sources */,
6C25C4720B043D98729C02C8 /* TopUpAccountPresenter.swift in Sources */,
A42D51CE206A361400EEB952 /* Search.swift in Sources */,
+ B77C11DB2109242200CCB42E /* AssigningInterpreterPresenter.swift in Sources */,
26342CB220ECDDC400D2196B /* Encodable+Dictionary.swift in Sources */,
3CDA490701EC3FEAAC2E9AFE /* TopUpAccountInteractor.swift in Sources */,
26B32B691FE1715500888A0A /* WeakRef.swift in Sources */,
@@ -14348,6 +14698,7 @@
8580BAC620BD983400239D9D /* MessageProtocols.swift in Sources */,
2683F77A203F38E30003181A /* UIPickerView.swift in Sources */,
A4679BAB20B2DD100021FE9C /* SubscribersCollectionDataSource.swift in Sources */,
+ B7EF8ED7210C598800E0E981 /* InterpretationTypeCellLayout.swift in Sources */,
2648C40D2069B52100863614 /* ChangeNumberStep1Protocols.swift in Sources */,
A43B25B920AB1E7600FF8107 /* String+Range.swift in Sources */,
E7C9CEC51FCC245F0090C2E0 /* P2pExtension.swift in Sources */,
@@ -14378,6 +14729,7 @@
87DE79674FF430A52D2A0BB7 /* MyGroupAliasProtocols.swift in Sources */,
A45F111E20B4218D00F45004 /* FileTransferInfoView.swift in Sources */,
0B79E13E95305A80847AA99F /* MyGroupAliasViewController.swift in Sources */,
+ B745F2E42109B9E500488A91 /* LanguagePickerDelegate.swift in Sources */,
990A25B2C84CE09B4CE64533 /* MyGroupAliasPresenter.swift in Sources */,
D839883F9B7A8CD245A85701 /* MyGroupAliasInteractor.swift in Sources */,
4B06D3202028A9B1003B275B /* P2pChatItemsFactory.swift in Sources */,
@@ -14428,6 +14780,7 @@
85D669E420BD956000FBD803 /* Int+AnyObject.swift in Sources */,
A4569873060C49904EF8C555 /* EditGroupPhotoViewController.swift in Sources */,
8502DB502061030000613C8C /* WheelPositionPickerWireFrame.swift in Sources */,
+ 854D13D8211B2E7200E139FC /* MessageCollectionViewLayout.swift in Sources */,
0062D9452062EC4100B915AC /* InviteFriendsDS.swift in Sources */,
F117872620ACF2DB007A9A1B /* VideoQuality.swift in Sources */,
FBDA34E920921079009F4FB6 /* KeyboardLayoutGuide.swift in Sources */,
@@ -14438,6 +14791,7 @@
896D51F07E2F79C8B5502DBF /* EditGroupPhotoInteractor.swift in Sources */,
A4ED79AA20C704F500A41F67 /* MyChannelsItemsFactory.swift in Sources */,
5BC1D38420D3B670002A44B3 /* CallCreatorMediator.swift in Sources */,
+ B7EF8ED2210C502D00E0E981 /* InterpretationTypeTableDelegate.swift in Sources */,
1325429A6216D23E2E67B6B7 /* EditGroupPhotoWireframe.swift in Sources */,
2603139720A0A4B9009AC66D /* LangCell.swift in Sources */,
99B9D27D2F0EFE051E6581ED /* CreateGroupProtocols.swift in Sources */,
@@ -14461,6 +14815,7 @@
8562853220D140FC000C9739 /* InputBar+ButtonType.swift in Sources */,
A43B25AC20AB1DFA00FF8107 /* BaseInputView.swift in Sources */,
F127F3BA20BF03BF007A6F87 /* DateFormatterExtension.swift in Sources */,
+ B7B546AE210D9C8C002DCA55 /* CircleView.swift in Sources */,
E79061B81FBF2243009FD83A /* FeatureTable.swift in Sources */,
5B5EE777EF301CFC1FDCF307 /* CreateGroupInteractor.swift in Sources */,
8580BAD820BD98E700239D9D /* CounterView.swift in Sources */,
@@ -14533,9 +14888,11 @@
5ED473EC698E99DC021E553A /* MapSearchInteractor.swift in Sources */,
F1607B2A20B2DE6500BDF60A /* CameraQRPreviewWireframe.swift in Sources */,
E3BE59F069959DA2523EF3DC /* MapSearchWireframe.swift in Sources */,
+ B7EF8ED4210C511C00E0E981 /* InterpretationTypeCell.swift in Sources */,
8572C3BE2092368600E4840C /* StickerDataSource.swift in Sources */,
F1313B0220888FE600E04092 /* ThirdPartyServices.swift in Sources */,
9BD8E40720F3576F001384EC /* CallInProgressWireframe.swift in Sources */,
+ B745F2E62109BB0100488A91 /* InterpretationLayout.swift in Sources */,
8548340E207769E800604051 /* DocumentInteractionInput.swift in Sources */,
BDC42BA204F86F13E9FE24FA /* ScheduleMessageProtocols.swift in Sources */,
0062D9462062EC4100B915AC /* InviteFriendsViewControllerLayout.swift in Sources */,
@@ -14555,6 +14912,7 @@
8F4E135F7485898D06EEABAB /* ScheduleMessageWireframe.swift in Sources */,
B83D907D324553FB792968EE /* TimeZoneSelectorProtocols.swift in Sources */,
06084879DD92F39E637C21F2 /* TimeZoneSelectorViewController.swift in Sources */,
+ B79FA03A21091ED000F286BF /* InterpretationWireFrame.swift in Sources */,
2648C4142069B52100863614 /* ChangeNumberStep3Protocols.swift in Sources */,
A839130C3B1AEFC6EBDA71A4 /* TimeZoneSelectorPresenter.swift in Sources */,
A81BC507BA308AEE05A9B5E1 /* TimeZoneSelectorInteractor.swift in Sources */,
@@ -14585,6 +14943,7 @@
8580BAED20BD9A7100239D9D /* LinkLongPressGestureRecognizer.swift in Sources */,
C6B308C6734EFB77892832A0 /* SecurityPresenter.swift in Sources */,
A42D52B4206A53AA00EEB952 /* ok_Spec.swift in Sources */,
+ B74BAFFD21076AFA0049CD27 /* Sector.swift in Sources */,
8E54E93EA25B11D417A6100E /* SecurityInteractor.swift in Sources */,
A43B259420AB1DFA00FF8107 /* InputBar.swift in Sources */,
F11DF06820BD996200F3E005 /* NavigationProtocol.swift in Sources */,
@@ -14681,7 +15040,6 @@
isa = PBXVariantGroup;
children = (
00E864712052B1BE00844FF1 /* en */,
- 00E864732052B1C400844FF1 /* ru */,
);
name = InfoPlist.strings;
sourceTree = "";
@@ -14698,7 +15056,6 @@
isa = PBXVariantGroup;
children = (
6D36F8E31F0ADBD300FA1AC8 /* en */,
- 00E864662049840A00844FF1 /* ru */,
);
name = Localizable.strings;
sourceTree = "";
@@ -14707,7 +15064,6 @@
isa = PBXVariantGroup;
children = (
A4B544F920EFC0AD00EB7B0F /* en */,
- A4B544FB20EFC0BC00EB7B0F /* ru */,
);
name = StatusCodes.strings;
sourceTree = "";
@@ -15112,7 +15468,7 @@
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DSHARE_EXTENSION";
PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "3d8b88dd-22c4-457b-864c-32d6378f5f64";
+ PROVISIONING_PROFILE = "69f3dc99-df33-4a29-8a8a-8d93926a3535";
PROVISIONING_PROFILE_SPECIFIER = DevBundle_DevExt;
SKIP_INSTALL = YES;
SWIFT_VERSION = 4.0;
@@ -15285,7 +15641,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "8a9243cd-515b-4739-b9d6-f73ba97c3403";
+ PROVISIONING_PROFILE = "dc81b7c8-7ca8-4e18-9fee-d0f512f7c8ca";
PROVISIONING_PROFILE_SPECIFIER = DevBundle_Dev;
SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -15405,7 +15761,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "056407e9-6a56-4005-9a4c-8c142132e54f";
+ PROVISIONING_PROFILE = "dc81b7c8-7ca8-4e18-9fee-d0f512f7c8ca";
PROVISIONING_PROFILE_SPECIFIER = DevBundle_Dev;
SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -15958,8 +16314,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconRC;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Nynja/Resources/Nynja.entitlements;
- CODE_SIGN_IDENTITY = "iPhone Developer";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 9GKQ5AMF2B;
@@ -15970,8 +16326,8 @@
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "d22f9bd2-3299-4c7a-ac33-6a12c4c3cdaa";
- PROVISIONING_PROFILE_SPECIFIER = NynjaRC_dev;
+ PROVISIONING_PROFILE = "3d9e361e-de0d-4189-be64-4bea82318684";
+ PROVISIONING_PROFILE_SPECIFIER = NynjaRC_adhoc;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -15989,8 +16345,8 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = "Nynja-Share/Resources/Nynja-Share.entitlements";
- CODE_SIGN_IDENTITY = "iPhone Developer";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 9GKQ5AMF2B;
@@ -16002,8 +16358,8 @@
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DSHARE_EXTENSION";
PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE = "1fc6c35e-7400-4bfc-8831-e2bba8e2d5e0";
- PROVISIONING_PROFILE_SPECIFIER = NynjaRC_devExt;
+ PROVISIONING_PROFILE = "f232c367-7000-49c5-b32c-0efb36c5f2e3";
+ PROVISIONING_PROFILE_SPECIFIER = NynjaRC_adhocExt;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
diff --git a/Nynja/AppDelegate.swift b/Nynja/AppDelegate.swift
index e000e94ca2984364de0cdbcf4b9316e98aed628e..ade8b9e232d644aaf1122e2ff3e462a69d2b69b3 100644
--- a/Nynja/AppDelegate.swift
+++ b/Nynja/AppDelegate.swift
@@ -94,9 +94,8 @@ private extension AppDelegate {
self.window?.rootViewController = navigation
self.window?.makeKeyAndVisible()
}
-
-
- func configureDependencies() {
+
+ private func configureDependencies() {
setupTestFairy()
setupCrashlytics()
setupGoogleMaps()
diff --git a/Nynja/ChatService.swift b/Nynja/ChatService.swift
index 3fa4ac54c00146bc8d0148b3406c927bf51e674c..b6e279d3f4661af1000445493555290476d45a22 100644
--- a/Nynja/ChatService.swift
+++ b/Nynja/ChatService.swift
@@ -50,7 +50,7 @@ final class ChatService {
}
@discardableResult
- static func updateLastMessage(_ message: Message, shouldUpdate: ((Int64) -> Bool)? = nil) -> Bool {
+ static func updateLastMessage(_ message: Message, shouldChangeUnread: Bool = true, shouldUpdate: ((Int64) -> Bool)? = nil) -> Bool {
guard let chatModel = ChatService.fetchChatModel(from: message) else { return false }
if let shouldUpdate = shouldUpdate, let lastId = chatModel.last_msg?.id, !shouldUpdate(lastId) {
@@ -58,8 +58,8 @@ final class ChatService {
}
chatModel.last_msg = message
-
- let shouldIncrementUnread = (!message.isOwn || message.isCursor) && !message.isEdited
+
+ let shouldIncrementUnread = (!message.isOwn || message.isCursor) && !message.isEdited && shouldChangeUnread
if shouldIncrementUnread {
let counter = chatModel.unreadCount + 1
chatModel.unread = counter
@@ -81,15 +81,50 @@ final class ChatService {
}
}
+ // MARK: - Remove message
+ static func removeMessages(_ messages: [Message]) {
+ messages.forEach { removeMessage($0, shouldUpdateChat: false) }
+ }
+
+ static func removeMessage(_ message: Message, shouldUpdateChat: Bool = true) {
+ if MessageDAO.removeMessage(using: message) {
+ guard let id = message.link else {
+ return
+ }
+
+ if let action = MessageActionDAO.fetchMessageAction(by: id) {
+ try? StorageService.sharedInstance.perform(action: .delete, with: action)
+ }
+
+ if let fetchType = self.fetchType(from: message),
+ let lastMessage = MessageDAO.fetchLastMessage(of: fetchType) {
+
+ ChatService.updateLastMessage(lastMessage, shouldChangeUnread: false) { (lastMessageId) -> Bool in
+ return id == lastMessageId
+ }
+ }
+ }
+ }
+
+ private static func fetchType(from message: Message) -> FetchType? {
+ if let p2p = message.p2pFeed, let from = p2p.from, let to = p2p.to {
+ return .p2p(from: from, to: to)
+ } else if let muc = message.mucFeed, let name = muc.name {
+ return .muc(name: name)
+ }
+
+ return nil
+ }
+
// MARK: - Clear History
static func clearHistory(_ message: Message) {
do {
if let muc = message.feed_id as? muc, let name = muc.name {
try MessageDAO.clearHistory(FetchType.muc(name: name))
- updateChatModel(message)
+ updateChatModelAfterClear(with: message)
} else if let p2p = message.feed_id as? p2p, let from = p2p.from, let to = p2p.to {
try MessageDAO.clearHistory(FetchType.p2p(from: from, to: to))
- updateChatModel(message)
+ updateChatModelAfterClear(with: message)
}
} catch {}
}
@@ -109,7 +144,7 @@ final class ChatService {
} catch {}
}
- private static func updateChatModel(_ message: Message) {
+ private static func updateChatModelAfterClear(with message: Message) {
do {
try storageService.perform(action: .save, with: message)
diff --git a/Nynja/CircleMenuControl/Core/CircleMenu.swift b/Nynja/CircleMenuControl/Core/CircleMenu.swift
new file mode 100644
index 0000000000000000000000000000000000000000..7dc1e98d7de7f64f530abd55bbc880f063fa1548
--- /dev/null
+++ b/Nynja/CircleMenuControl/Core/CircleMenu.swift
@@ -0,0 +1,270 @@
+//
+// CircleMenu.swift
+// CircleMenuControl
+//
+// Created by Roman Chopovenko on 7/23/18.
+// Copyright © 2018 Roman Chopovenko. All rights reserved.
+//
+
+import UIKit
+
+class CircleMenu: UIControl {
+
+ weak var delegate: CircleMenuDelegate?
+
+ private var container: UIView!
+ private var sectors = [Sector]()
+ private(set) var currentSectorIndex: Int?
+ private let separatorPadding: CGFloat = 30
+
+ private var menuNavigationStack = [CircleMenuSet]() {
+ didSet {
+ if let data = self.dataSource {
+ self.delegate?.didChangeCurrentSet(self, title: data.title)
+ }
+ self.sectors = [Sector]()
+ self.currentSectorIndex = nil
+ self.container.removeFromSuperview()
+ self.container = nil
+ self.setNeedsDisplay()
+ }
+ }
+
+ private var dataSource: CircleMenuSet? {
+ return self.menuNavigationStack.last
+ }
+
+ private var containerCenter: CGPoint {
+ return CGPoint(x: self.container.bounds.width/2, y: self.container.bounds.height/2)
+ }
+
+ private var segmentAngle: CGFloat {
+ guard let items = self.dataSource?.items else { return 0 }
+ return (2 * .pi) / CGFloat(items.count)
+ }
+
+
+ //MARK: Public methods
+
+ public func updateMenu(with newMenuSet: CircleMenuSet) {
+ self.menuNavigationStack.append(newMenuSet)
+ }
+
+ public func navigateBackInMenuStack() -> Bool {
+ if self.menuNavigationStack.count > 1 {
+ let lastIndex = self.menuNavigationStack.count - 1
+ self.menuNavigationStack.remove(at: lastIndex)
+ return true
+ }
+ return false
+ }
+
+ //MARK: - Init
+
+ required init(rect: CGRect, delegate: CircleMenuDelegate, menuSet: CircleMenuSet) {
+ super.init(frame: rect)
+ self.delegate = delegate
+ self.menuNavigationStack.append(menuSet)
+ self.backgroundColor = .clear
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ //MARK: - Override
+
+ override func draw(_ rect: CGRect) {
+ guard self.container == nil else { return }
+ guard let items = self.dataSource?.items else { return }
+ self.container = UIView(frame: rect)
+ self.container.backgroundColor = .clear
+
+ for index in 0.. Bool {
+ let touchPoint: CGPoint = touch.location(in: self)
+
+ if let sector = self.detectSelectedSegment(from: touchPoint), let data = self.dataSource {
+ if data.items[sector.index].isEnabled {
+ self.selectSegment(at: sector.index)
+ }
+ }
+ return self.ignoreTaps(for: touchPoint)
+ }
+
+ override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
+ if let selectedIndex = self.currentSectorIndex, let section = self.sectors[selectedIndex].section {
+ self.delegate?.didSelectItem(self, type: section.model.type)
+ }
+ self.deSelectSegment(at: self.currentSectorIndex)
+ }
+
+
+ //MARK: - Construct Menu UI
+
+ private func createSector(at index: Int, itemsCount: Int, angle: CGFloat) -> Sector {
+ var endAngle = self.segmentAngle * CGFloat(index) + angle/2
+
+ if itemsCount == 2 {
+ endAngle = angle * CGFloat(index)
+ }
+
+ let startAngle = endAngle - angle
+ let midAngle = startAngle + angle/2
+
+ let sector = Sector(index: index,
+ min: startAngle,
+ mid: midAngle,
+ max: endAngle)
+ return sector
+ }
+
+ private func createSeparator(padding: CGFloat, height: CGFloat, angle: CGFloat, center: CGPoint, color: UIColor) -> UIView {
+ let separator = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 1, height: height)))
+ separator.backgroundColor = color
+ separator.layer.anchorPoint = CGPoint(x: 0.5, y: 1)
+
+ let separatorStartPoint = self.getPointCoordinate(withCenterIn: center,
+ radius: padding,
+ angle: angle - .pi/2)
+ separator.layer.position = separatorStartPoint
+ separator.transform = CGAffineTransform(rotationAngle: angle)
+ return separator
+ }
+
+ private func createSection(in view: UIView, rotationAngle: CGFloat, model: CircleMenuItem) -> SectionView {
+ let sectionRect = CGRect(origin: .zero, size: CGSize(width: view.bounds.width, height: view.bounds.height/2))
+ let section = SectionView(frame: sectionRect, model: model, angle: 2 * .pi - rotationAngle)
+
+ section.backgroundColor = .clear
+ section.isHighlighted = false
+
+ section.layer.anchorPoint = CGPoint(x: 0.5, y: 1)
+ section.layer.position = view.center
+ section.transform = CGAffineTransform(rotationAngle: rotationAngle)
+ return section
+ }
+
+ //MARK: Private methods
+
+ private func selectSegment(at index: Int) {
+ if let section = self.sectors[index].section {
+ self.currentSectorIndex = index
+ section.isHighlighted = true
+ }
+ }
+
+ private func deSelectSegment(at index: Int?) {
+ if let thisIndex = index, let section = self.sectors[thisIndex].section {
+ self.currentSectorIndex = nil
+ section.isHighlighted = false
+ }
+ }
+
+ private func detectSelectedSegment(from touchPoint: CGPoint) -> Sector? {
+ // Get inverted touch angle value
+ let dx = container.center.x - touchPoint.x
+ let dy = container.center.y - touchPoint.y
+ let angle = -atan2(dx, dy)
+
+ for sector in self.sectors {
+ let newAngle = angle.toPositiveRadians
+
+ let min = sector.minValue.toPositiveRadians
+ let max = sector.maxValue.toPositiveRadians
+
+ let regularCondition = newAngle > min && newAngle < max
+ let firstElementCondition = min > max && (newAngle > min && newAngle <= 2 * .pi || newAngle < max)
+
+ if regularCondition || firstElementCondition {
+ return sector
+ }
+ }
+ return nil
+ }
+
+ private func calculateDistance(fromCenter point: CGPoint) -> CGFloat {
+ let center = CGPoint(x: bounds.size.width/2, y: bounds.size.height/2)
+ let dx = CGFloat(point.x - center.x)
+ let dy = CGFloat(point.y - center.y)
+
+ return sqrt(pow(dx, 2) + pow(dy, 2))
+ }
+
+ private func ignoreTaps(for touchPoint: CGPoint) -> Bool {
+ let dist: CGFloat = calculateDistance(fromCenter: touchPoint)
+
+ if dist < self.separatorPadding || dist > self.container.bounds.width/2 {
+ self.deSelectSegment(at: self.currentSectorIndex)
+ return false
+ }
+ return true
+ }
+
+ private func getPointCoordinate(withCenterIn center: CGPoint, radius: CGFloat, angle: CGFloat) -> CGPoint {
+ return CGPoint(x: center.x + radius*cos(angle), y: center.y + radius*sin(angle))
+ }
+
+ private func drawSegmentPoints(inView view: UIView, innerPadding: CGFloat, side: CGFloat, leadingAngle: CGFloat, trailingAngle: CGFloat) {
+ let bottomCenter = CGPoint(x: view.bounds.width/2, y: view.bounds.height)
+
+ let leadingBottomPoint = self.getPointCoordinate(withCenterIn: bottomCenter,
+ radius: innerPadding,
+ angle: leadingAngle)
+ let leadingTopPoint = self.getPointCoordinate(withCenterIn: bottomCenter,
+ radius: innerPadding + side,
+ angle: leadingAngle)
+
+ let trailingBottomPoint = self.getPointCoordinate(withCenterIn: bottomCenter,
+ radius: innerPadding,
+ angle: trailingAngle)
+ let path = UIBezierPath()
+ path.move(to: leadingBottomPoint)
+ path.addLine(to: leadingTopPoint)
+ path.addArc(withCenter: bottomCenter, radius: innerPadding + side, startAngle: leadingAngle, endAngle: trailingAngle, clockwise: true)
+ path.addLine(to: trailingBottomPoint)
+ path.addArc(withCenter: bottomCenter, radius: innerPadding, startAngle: trailingAngle, endAngle: leadingAngle, clockwise: false)
+ path.close()
+
+ view.applyMask(withPath: path, inverse: false)
+ }
+}
diff --git a/Nynja/CircleMenuControl/Core/CircleMenuDelegate.swift b/Nynja/CircleMenuControl/Core/CircleMenuDelegate.swift
new file mode 100644
index 0000000000000000000000000000000000000000..135f692ca1e5ea6b7c9b097248390ce8640a62b4
--- /dev/null
+++ b/Nynja/CircleMenuControl/Core/CircleMenuDelegate.swift
@@ -0,0 +1,14 @@
+//
+// CircleMenuDelegate.swift
+// CircleMenuControl
+//
+// Created by Roman Chopovenko on 7/24/18.
+// Copyright © 2018 Roman Chopovenko. All rights reserved.
+//
+
+import Foundation
+
+protocol CircleMenuDelegate: class {
+ func didSelectItem(_ menu: CircleMenu, type: CircleMenuItemType)
+ func didChangeCurrentSet(_ menu: CircleMenu, title: String)
+}
diff --git a/Nynja/CircleMenuControl/Core/Sector/SectionView.swift b/Nynja/CircleMenuControl/Core/Sector/SectionView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..761af4af652245239d5b3aa6d2aca803abb555be
--- /dev/null
+++ b/Nynja/CircleMenuControl/Core/Sector/SectionView.swift
@@ -0,0 +1,138 @@
+//
+// SectionView.swift
+// rrrrrr
+//
+// Created by Roman Chopovenko on 7/23/18.
+// Copyright © 2018 Roman Chopovenko. All rights reserved.
+//
+
+import UIKit
+
+fileprivate let iconSide: CGFloat = 34
+fileprivate let padding: CGFloat = 8
+fileprivate let labelHeight: CGFloat = 22
+
+fileprivate let height = iconSide + padding + labelHeight
+
+class SectionView: UIView {
+
+ var angle: CGFloat!
+ var model: CircleMenuItem!
+
+ var isHighlighted: Bool = false {
+ didSet {
+ self.backgroundView.alpha = isHighlighted ? 1.0 : 0
+ self.backgroundView.setNeedsDisplay()
+ }
+ }
+
+ // MARK: - Views
+ lazy var backgroundView: UIView = {
+ let view = GradientView(colors: [Colors.gradientView.start,
+ Colors.gradientView.end])
+ self.addSubview(view)
+ view.snp.makeConstraints({ (make) in
+ make.edges.equalToSuperview()
+ })
+ return view
+ }()
+
+ private lazy var buttonContentHolder: UIView = {
+ let view = UIView()
+
+ view.backgroundColor = .clear
+ view.transform = CGAffineTransform(rotationAngle: self.angle)
+
+ let shiftCenterY: CGFloat = 10.0
+ let width: CGFloat = 2*height
+
+ self.addSubview(view)
+ view.snp.makeConstraints({ (make) in
+ make.centerX.equalToSuperview()
+ make.centerY.equalToSuperview().offset(-shiftCenterY)
+ make.width.equalTo(width)
+ make.height.equalTo(height)
+ })
+ return view
+ }()
+
+ private lazy var iconView: UIImageView = {
+ let icon = self.createIconView(with: self.model)
+
+ buttonContentHolder.addSubview(icon)
+ icon.snp.makeConstraints({ (make) in
+ make.width.height.equalTo(iconSide)
+ make.top.centerX.equalToSuperview()
+ })
+ return icon
+ }()
+
+ private lazy var titleLabel: UILabel = {
+ let label = self.createTitleLabel(with: self.model)
+
+ buttonContentHolder.addSubview(label)
+ label.snp.makeConstraints({ (make) in
+ make.height.equalTo(labelHeight)
+ make.top.equalTo(self.iconView.snp.bottom).offset(padding)
+ make.left.right.bottom.equalToSuperview()
+ })
+ return label
+ }()
+
+ // MARK: - Init
+ required init(frame: CGRect, model: CircleMenuItem, angle: CGFloat) {
+ super.init(frame: frame)
+ self.model = model
+ self.angle = angle
+ self.setupUI()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ func setupUI() {
+ self.backgroundView.isHidden = false
+ self.titleLabel.isHidden = false
+ }
+
+ // MARK: Private methods
+ private func createIconView(with model: CircleMenuItem) -> UIImageView {
+ let imageView = UIImageView(frame: .zero)
+ imageView.contentMode = .scaleAspectFit
+ imageView.image = model.icon.withRenderingMode(.alwaysTemplate)
+ imageView.tintColor = model.isEnabled ? Colors.iconView.enabled : Colors.iconView.disabled
+ return imageView
+ }
+
+ private func createTitleLabel(with model: CircleMenuItem) -> UILabel {
+ let label = UILabel(frame: .zero)
+
+ label.numberOfLines = 1
+ label.textAlignment = .center
+ let font = UIFont(name: Constants.fonts.medium, size: 12)
+ label.font = font
+ label.text = model.title
+ label.textColor = model.isEnabled ? Colors.titleLabel.enabled : Colors.titleLabel.disabled
+ return label
+ }
+}
+
+extension SectionView {
+ struct Colors {
+ struct titleLabel {
+ static let enabled: UIColor = Constants.colors.marketplaceMunu.activeTitle.getColor()
+ static let disabled: UIColor = Constants.colors.marketplaceMunu.inActiveTitle.getColor()
+ }
+
+ struct iconView {
+ static let enabled: UIColor = Constants.colors.marketplaceMunu.activeIcon.getColor()
+ static let disabled: UIColor = Constants.colors.marketplaceMunu.inactiveIcon.getColor()
+ }
+
+ struct gradientView {
+ static let start: UIColor = Constants.colors.marketplaceMunu.gradient.getColor(withAlpha: 0.1)
+ static let end: UIColor = Constants.colors.marketplaceMunu.gradient.getColor(withAlpha: 0.5)
+ }
+ }
+}
diff --git a/Nynja/CircleMenuControl/Core/Sector/Sector.swift b/Nynja/CircleMenuControl/Core/Sector/Sector.swift
new file mode 100644
index 0000000000000000000000000000000000000000..7b72100653ce58247932869bf7f08d9f7f95d6ad
--- /dev/null
+++ b/Nynja/CircleMenuControl/Core/Sector/Sector.swift
@@ -0,0 +1,25 @@
+//
+// Sector.swift
+// rrrrrr
+//
+// Created by Roman Chopovenko on 7/19/18.
+// Copyright © 2018 Roman Chopovenko. All rights reserved.
+//
+
+import UIKit
+
+class Sector {
+ var minValue: CGFloat
+ var midValue: CGFloat
+ var maxValue: CGFloat
+ var index: Int
+
+ var section: SectionView?
+
+ required init(index: Int, min: CGFloat, mid: CGFloat, max: CGFloat) {
+ self.index = index
+ self.minValue = min
+ self.midValue = mid
+ self.maxValue = max
+ }
+}
diff --git a/Nynja/CircleMenuControl/Core/Sector/TheGradientView.swift b/Nynja/CircleMenuControl/Core/Sector/TheGradientView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..336adce7e1d38ffcd4f9654b677422baa0113356
--- /dev/null
+++ b/Nynja/CircleMenuControl/Core/Sector/TheGradientView.swift
@@ -0,0 +1,43 @@
+//
+// TheGradientView.swift
+// rrrrrr
+//
+// Created by Roman Chopovenko on 7/21/18.
+// Copyright © 2018 Roman Chopovenko. All rights reserved.
+//
+
+import UIKit
+
+class TheGradientView: UIView {
+
+ var startColor: UIColor = Constants.colors.marketplaceMunu.gradient.getColor(withAlpha: 0.1)
+ var endColor: UIColor = Constants.colors.marketplaceMunu.gradient.getColor(withAlpha: 0.5)
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ self.backgroundColor = .clear
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func draw(_ rect: CGRect) {
+ let context = UIGraphicsGetCurrentContext()!
+ let colors = [startColor.cgColor, endColor.cgColor]
+
+ let colorSpace = CGColorSpaceCreateDeviceRGB()
+
+ let colorLocations: [CGFloat] = [0.0, 0.8]
+
+ let gradient = CGGradient(colorsSpace: colorSpace,
+ colors: colors as CFArray,
+ locations: colorLocations)!
+
+ let startPoint = CGPoint.zero
+ let endPoint = CGPoint(x: 0, y: bounds.height)
+ context.drawLinearGradient(gradient,
+ start: startPoint,
+ end: endPoint,
+ options: [])
+ }
+}
diff --git a/Nynja/CircleMenuControl/Extension/UIView+Mask.swift b/Nynja/CircleMenuControl/Extension/UIView+Mask.swift
new file mode 100644
index 0000000000000000000000000000000000000000..deb0a6efc3a6bcd51098dc296205e61f6c9480da
--- /dev/null
+++ b/Nynja/CircleMenuControl/Extension/UIView+Mask.swift
@@ -0,0 +1,50 @@
+//
+// UIView+Mask.swift
+// rrrrrr
+//
+// Created by Roman Chopovenko on 7/23/18.
+// Copyright © 2018 Roman Chopovenko. All rights reserved.
+//
+
+import UIKit
+
+extension UIView {
+ func applyMask(withRect rect: CGRect, inverse: Bool = false) {
+ let path = UIBezierPath(rect: rect)
+ let maskLayer = CAShapeLayer()
+
+ if inverse {
+ path.append(UIBezierPath(rect: self.bounds))
+ maskLayer.fillRule = kCAFillRuleEvenOdd
+ }
+
+ maskLayer.path = path.cgPath
+
+ self.layer.mask = maskLayer
+ }
+
+ func applyMask(withPath path: UIBezierPath, inverse: Bool = false) {
+ let path = path
+ let maskLayer = CAShapeLayer()
+
+ if inverse {
+ path.append(UIBezierPath(rect: self.bounds))
+ maskLayer.fillRule = kCAFillRuleEvenOdd
+ }
+
+ maskLayer.path = path.cgPath
+
+ self.layer.mask = maskLayer
+ }
+}
+
+extension BinaryInteger {
+ var degreesToRadians: CGFloat { return CGFloat(Int(self)) * .pi / 180 }
+}
+
+extension FloatingPoint {
+ var degreesToRadians: Self { return self * .pi / 180 }
+ var radiansToDegrees: Self { return self * 180 / .pi }
+
+ var toPositiveRadians: Self { return self < 0 ? 2 * .pi + self : self}
+}
diff --git a/Nynja/CircleMenuControl/Model/CircleMenuFactory.swift b/Nynja/CircleMenuControl/Model/CircleMenuFactory.swift
new file mode 100644
index 0000000000000000000000000000000000000000..5f76e7c61c1f3001cb7a5dc330a9635bb8a8218f
--- /dev/null
+++ b/Nynja/CircleMenuControl/Model/CircleMenuFactory.swift
@@ -0,0 +1,81 @@
+//
+// CircleMenuFactory.swift
+// CircleMenuControl
+//
+// Created by Roman Chopovenko on 7/23/18.
+// Copyright © 2018 Roman Chopovenko. All rights reserved.
+//
+
+import UIKit
+
+
+enum CircleMenuItemType: String {
+ case marketplace
+ case freelance
+ case accessMarketplace
+ case virtualGoods
+ case stickers
+ case mediaContent
+ case groupsAndChannels
+ case bots
+ case apps
+ case interpretation
+ case nynjaSupport
+ case design
+ var icon: UIImage {
+
+ switch self {
+ case .freelance: return #imageLiteral(resourceName: "marketplace_freelance")
+ case .accessMarketplace: return #imageLiteral(resourceName: "marketplace_access")
+ case .virtualGoods: return #imageLiteral(resourceName: "marketplace_virtual_goods")
+ case .stickers: return #imageLiteral(resourceName: "marketplace_sticker")
+ case .mediaContent: return #imageLiteral(resourceName: "marketplace_media_content")
+ case .groupsAndChannels: return #imageLiteral(resourceName: "marketplace_groups_channels")
+ case .bots: return #imageLiteral(resourceName: "marketplace_bots")
+ case .apps: return #imageLiteral(resourceName: "marketplace_apps")
+ case .interpretation: return #imageLiteral(resourceName: "marketplace_interpretation")
+ case .nynjaSupport: return #imageLiteral(resourceName: "marketplace_support")
+ case .design: return #imageLiteral(resourceName: "marketplace_design")
+ default:return #imageLiteral(resourceName: "ava_placeholder")
+ }
+ }
+}
+
+class CircleMenuFactory {
+
+ var marketplace: CircleMenuSet {
+ let freelance = CircleMenuItem(type: .freelance, isEnabled: true)
+ let accessMarketPlace = CircleMenuItem(type: .accessMarketplace, isEnabled: true)
+ let virtualGoods = CircleMenuItem(type: .virtualGoods, isEnabled: true)
+
+ return CircleMenuSet(title: CircleMenuItemType.marketplace.localized.uppercased(), items: [freelance, accessMarketPlace, virtualGoods])
+ }
+
+ var virtualGoods: CircleMenuSet {
+ let stickers = CircleMenuItem(type: .stickers, isEnabled: false)
+ let mediaContent = CircleMenuItem(type: .mediaContent, isEnabled: false)
+ return CircleMenuSet(title: CircleMenuItemType.virtualGoods.localized.uppercased(), items: [mediaContent, stickers])
+ }
+
+ var accessMarketPlace: CircleMenuSet {
+ let groupsAndChannels = CircleMenuItem(type: .groupsAndChannels, isEnabled: false)
+ let apps = CircleMenuItem(type: .apps, isEnabled: false)
+ let bots = CircleMenuItem(type: .bots, isEnabled: false)
+ return CircleMenuSet(title: CircleMenuItemType.accessMarketplace.localized.uppercased(), items: [groupsAndChannels, apps, bots])
+ }
+
+ var freelance: CircleMenuSet {
+ let interpretation = CircleMenuItem(type: .interpretation, isEnabled: true)
+ let nynjaSupport = CircleMenuItem(type: .nynjaSupport, isEnabled: false)
+ let design = CircleMenuItem(type: .design, isEnabled: false)
+ let title = CircleMenuItemType.freelance.localized + " " + CircleMenuItemType.marketplace.localized
+ return CircleMenuSet(title: title.uppercased(), items: [interpretation, nynjaSupport, design])
+ }
+
+ var allElementsDictionary: [CircleMenuItemType : CircleMenuSet] {
+ return [.marketplace : self.marketplace,
+ .virtualGoods : self.virtualGoods,
+ .accessMarketplace : self.accessMarketPlace,
+ .freelance : self.freelance]
+ }
+}
diff --git a/Nynja/CircleMenuControl/Model/CircleMenuItem.swift b/Nynja/CircleMenuControl/Model/CircleMenuItem.swift
new file mode 100644
index 0000000000000000000000000000000000000000..ec488799c9fd1efb2d50c7628fcecd2f484c1769
--- /dev/null
+++ b/Nynja/CircleMenuControl/Model/CircleMenuItem.swift
@@ -0,0 +1,17 @@
+//
+// CircleMenuItem.swift
+// CircleMenuControl
+//
+// Created by Roman Chopovenko on 7/24/18.
+// Copyright © 2018 Roman Chopovenko. All rights reserved.
+//
+
+import UIKit
+
+struct CircleMenuItem {
+ let type: CircleMenuItemType
+ let isEnabled: Bool
+
+ var icon: UIImage { return type.icon }
+ var title: String { return type.localized }
+}
diff --git a/Nynja/CircleMenuControl/Model/CircleMenuSet.swift b/Nynja/CircleMenuControl/Model/CircleMenuSet.swift
new file mode 100644
index 0000000000000000000000000000000000000000..11fd3cae6b23ae29e88c9bd4596842ddcdb7e3c6
--- /dev/null
+++ b/Nynja/CircleMenuControl/Model/CircleMenuSet.swift
@@ -0,0 +1,14 @@
+//
+// CircleMenuSet.swift
+// CircleMenuControl
+//
+// Created by Roman Chopovenko on 7/24/18.
+// Copyright © 2018 Roman Chopovenko. All rights reserved.
+//
+
+import Foundation
+
+struct CircleMenuSet {
+ let title: String
+ let items: [CircleMenuItem]
+}
diff --git a/Nynja/DB/Models/DBMessage.swift b/Nynja/DB/Models/DBMessage.swift
index d345acc4ce67342be001be36475cd271e0599d6c..e936ee5966efa425d5bd0f9d2361f38fb0538ed5 100644
--- a/Nynja/DB/Models/DBMessage.swift
+++ b/Nynja/DB/Models/DBMessage.swift
@@ -8,6 +8,8 @@
import GRDBCipher
+private let tableName = MessageTable.name
+
class DBMessage: Record, DBModelProtocol {
var id: Int64?
@@ -63,7 +65,7 @@ class DBMessage: Record, DBModelProtocol {
// MARK: - Record
override static var databaseTableName: String {
- return MessageTable.name
+ return tableName
}
required init(row: Row) {
@@ -179,41 +181,39 @@ class DBMessage: Record, DBModelProtocol {
return message
}
+ static func penultimateMessage(_ db: Database, ofType fetchType: FetchType) throws -> DBMessage? {
+ guard let lastMessage = try lastMessage(db, ofType: fetchType),
+ let lastMessageServerId = lastMessage.serverId else {
+ return nil
+ }
+
+ let condition = conditionBefore(messageServerId: lastMessageServerId)
+
+ var sql: String
+
+ switch fetchType {
+ case let .p2p(from, to):
+ sql = sqlP2p(from: from, to: to, conditions: condition, ordered: .desc)
+ case let .muc(mucName):
+ sql = sqlMuc(name: mucName, conditions: condition, ordered: .desc)
+ }
+
+ let message = try SQLRequest(sql).asRequest(of: DBMessage.self).fetchOne(db)
+ try message?.construct(db)
+ return message
+ }
+
static func lastMessage(_ db: Database, ofType fetchType: FetchType) throws -> DBMessage? {
- let message: DBMessage?
+ var sql: String
switch fetchType {
case let .p2p(from, to):
- let messageTable = MessageTable.name
- let p2pTable = P2pTable.name
-
- let sql = """
- select \(messageTable).*
- from \(messageTable)
- inner join \(p2pTable) on \(messageTable).[\(MessageTable.Column.feedId.title)] = \(p2pTable).[\(P2pTable.Column.id.title)]
- where \(p2pTable).[\(P2pTable.Column.from.title)] = '\(from)'
- and \(p2pTable).[\(P2pTable.Column.to.title)] = '\(to)'
- and \(messageTable).[\(MessageTable.Column.feedType.title)] = \(FeedType.p2p.rawValue)
- and \(messageTable).[\(MessageTable.Column.status.title)] != 'delete'
- order by \(messageTable).[\(MessageTable.Column.created.title)] DESC
- """
-
- message = try SQLRequest(sql).asRequest(of: DBMessage.self).fetchOne(db)
-
+ sql = sqlP2p(from: from, to: to, ordered: .desc)
case let .muc(mucName):
- guard let feedId = try DBMuc.muc(db, name: mucName)?.id else { return nil }
-
- let sql = """
- select *
- from \(MessageTable.name)
- where \(MessageTable.Column.feedId) = \(feedId)
- and \(MessageTable.Column.feedType.title) = '\(FeedType.muc.rawValue)'
- and \(MessageTable.Column.status.title) != 'delete'
- order by \(MessageTable.Column.created.title) DESC
- """
-
- message = try SQLRequest(sql).asRequest(of: DBMessage.self).fetchOne(db)
+ sql = sqlMuc(name: mucName, ordered: .desc)
}
+
+ let message = try SQLRequest(sql).asRequest(of: DBMessage.self).fetchOne(db)
try message?.construct(db)
return message
}
@@ -227,7 +227,7 @@ class DBMessage: Record, DBModelProtocol {
if !serverIds.isEmpty {
let ids = serverIds.joinedByComma()
- conditions = "and \(MessageTable.name).\(MessageTable.Column.serverId.title) in (\(ids))"
+ conditions = "and \(tableName).\(MessageTable.Column.serverId.title) in (\(ids))"
return try messages(db, fetchType: fetchType, conditions: conditions)
} else {
return []
@@ -235,7 +235,7 @@ class DBMessage: Record, DBModelProtocol {
}
static func messages(_ db: Database, fetchType: FetchType, before messageServerId: Int64) throws -> [DBMessage] {
- let condition = "and \(MessageTable.name).\(MessageTable.Column.serverId.title) < \(messageServerId)"
+ let condition = conditionBefore(messageServerId: messageServerId)
return try messages(db, fetchType: fetchType, conditions: condition)
}
@@ -305,12 +305,6 @@ class DBMessage: Record, DBModelProtocol {
}
self.id = id
- let rooms = try DBRoom.filter(Column(RoomTable.Column.messageId.title) == id).fetchAll(db)
- try rooms.forEach { $0.messageId = nil; try $0.saveAggregate(db) }
-
- let contacts = try DBContact.filter(Column(ContactTable.Column.messageId.title) == id).fetchAll(db)
- try contacts.forEach { $0.messageId = nil; try $0.saveAggregate(db) }
-
try DBDesc.deleteAll(db, targetId: String(id), targetType: .message)
return try delete(db)
@@ -323,7 +317,7 @@ class DBMessage: Record, DBModelProtocol {
}
static private func requestUnsentMessages(from: String) -> AnyTypedRequest {
- let messageTable = MessageTable.name
+ let messageTable = tableName
let sql = """
select \(messageTable).*
@@ -331,24 +325,40 @@ class DBMessage: Record, DBModelProtocol {
where \(messageTable).[\(MessageTable.Column.from.title)] = '\(from)'
and \(messageTable).[\(MessageTable.Column.serverId.title)] is null
and \(messageTable).\(MessageTable.Column.feedType.title) = \(FeedType.p2p.rawValue)
- order by \(messageTable).[\(MessageTable.Column.created.title)]
+ \(orderedBy(.asc))
"""
return SQLRequest(sql).asRequest(of: DBMessage.self)
}
static private func requestP2pMessages(from: String, to: String, conditions: String = "") -> AnyTypedRequest {
- let sql = String(format: sqlP2p(from: from, to: to), conditions)
+ let sql = sqlP2p(from: from, to: to, conditions: conditions, ordered: .asc)
return SQLRequest(sql).asRequest(of: DBMessage.self)
}
static private func requestMucMessages(name: String, conditions: String = "") -> AnyTypedRequest {
- let sql = String(format: sqlMuc(name: name), conditions)
+ let sql = sqlMuc(name: name, conditions: conditions, ordered: .asc)
return SQLRequest(sql).asRequest(of: DBMessage.self)
}
- static private func sqlP2p(from: String, to: String) -> String {
- let messageTable = MessageTable.name
+
+ // MARK: - Raw SQL
+
+ static private var skippedStatuses: String {
+ let statusColumn = "\(tableName).\(MessageTable.Column.status.title)"
+ return "(\(statusColumn) is null or \(statusColumn) not in ('delete', 'edit', 'deleted'))"
+ }
+
+ static private func conditionBefore(messageServerId: MessageServerId) -> String {
+ return "and \(tableName).\(MessageTable.Column.serverId.title) < \(messageServerId)"
+ }
+
+ static private func orderedBy(_ ordered: Ordered) -> String {
+ return "order by \(tableName).[\(MessageTable.Column.created.title)] \(ordered.rawValue)"
+ }
+
+ static private func sqlP2p(from: String, to: String, conditions: String = "", ordered: Ordered) -> String {
+ let messageTable = tableName
let p2pTable = P2pTable.name
let sql = """
@@ -358,15 +368,16 @@ class DBMessage: Record, DBModelProtocol {
where \(p2pTable).[\(P2pTable.Column.from.title)] = '\(from)'
and \(p2pTable).[\(P2pTable.Column.to.title)] = '\(to)'
and \(messageTable).\(MessageTable.Column.feedType.title) = \(FeedType.p2p.rawValue)
+ and \(skippedStatuses)
%@
- order by \(messageTable).[\(MessageTable.Column.created.title)]
+ \(orderedBy(ordered))
"""
- return sql
+ return String(format: sql, conditions)
}
- static private func sqlMuc(name: String) -> String {
- let messageTable = MessageTable.name
+ static private func sqlMuc(name: String, conditions: String = "", ordered: Ordered) -> String {
+ let messageTable = tableName
let mucTable = MucTable.name
let sql = """
@@ -375,10 +386,20 @@ class DBMessage: Record, DBModelProtocol {
left join \(messageTable) on \(mucTable).id = \(messageTable).feedId
where \(mucTable).[\(MucTable.Column.name.title)] = '\(name)'
and \(messageTable).\(MessageTable.Column.feedType.title) = \(FeedType.muc.rawValue)
+ and \(skippedStatuses)
%@
- order by \(messageTable).[\(MessageTable.Column.created.title)]
+ \(orderedBy(ordered))
"""
- return sql
+ return String(format: sql, conditions)
}
}
+
+private extension DBMessage {
+
+ enum Ordered: String {
+ case asc
+ case desc
+ }
+
+}
diff --git a/Nynja/DB/Tables/ContactTable.swift b/Nynja/DB/Tables/ContactTable.swift
index d69ed6d915ce886d3c3042be5862052d9f7c4f0a..9285b90ee22b3e66de4ed0d471730f6f036c6337 100644
--- a/Nynja/DB/Tables/ContactTable.swift
+++ b/Nynja/DB/Tables/ContactTable.swift
@@ -15,7 +15,7 @@ class ContactTable: Table {
}
static func create(in db: Database) throws {
- try db.create(table: ContactTable.name, body: { (t) in
+ try db.create(table: ContactTable.name) { t in
t.column(Column.phoneId, .text).notNull().primaryKey()
t.column(Column.avatar, .text)
t.column(Column.names, .text)
@@ -28,9 +28,9 @@ class ContactTable: Table {
t.column(Column.presence, .text)
t.column(Column.status, .text)
- t.column(Column.messageId, .integer).references(MessageTable.name)
+ t.column(Column.messageId, .integer).references(MessageTable.name, onDelete: .setNull)
t.column(Column.rosterId, .integer).references(RosterTable.name, onDelete: .cascade)
- })
+ }
}
}
diff --git a/Nynja/DB/Tables/LinkTable.swift b/Nynja/DB/Tables/LinkTable.swift
index 5b2b743b00e7d450a7da82a658324851bfbbc4dc..36884f8bd6b9193416c6c70b0de14e7a62c7bfd7 100644
--- a/Nynja/DB/Tables/LinkTable.swift
+++ b/Nynja/DB/Tables/LinkTable.swift
@@ -15,13 +15,13 @@ final class LinkTable: Table {
}
static func create(in db: Database) throws {
- try db.create(table: LinkTable.name, body: { (table) in
+ try db.create(table: LinkTable.name) { table in
table.column(Column.id, .text).notNull().primaryKey()
table.column(Column.name, .text).notNull()
- table.column(Column.roomId, .text).notNull().references(RoomTable.name)
+ table.column(Column.roomId, .text).notNull().references(RoomTable.name, onDelete: .cascade)
table.column(Column.created, .integer).notNull()
table.column(Column.status, .text)
- })
+ }
}
}
diff --git a/Nynja/DB/Tables/MessageActionTable.swift b/Nynja/DB/Tables/MessageActionTable.swift
index b558a55deda5a5d9ea04d5edc7835877dfbe5aff..816b7e56175d12d6a55ca8a74aea014f7c71c7ff 100644
--- a/Nynja/DB/Tables/MessageActionTable.swift
+++ b/Nynja/DB/Tables/MessageActionTable.swift
@@ -16,15 +16,10 @@ class MessageActionTable: Table {
static func create(in db: Database) throws {
try db.create(table: MessageActionTable.name, body: { (t) in
- t.column(Column.messageId, .integer)
+ t.column(Column.messageId, .integer).primaryKey(onConflict: .replace, autoincrement: false)
t.column(Column.seenBy, .integer)
t.column(Column.phoneId, .text).defaults(to: "")
t.column(Column.action, .text).defaults(to: false)
-
- t.uniqueKey([Column.messageId.title], onConflict: .replace)
-// t.uniqueKey([Column.messageId.title, Column.seenBy.title, Column.action.title], onConflict: .replace)
-
-// t.foreignKey([Column.messageId.title], references: MessageTable.name, columns: nil, onDelete: .cascade, onUpdate: nil, deferred: false)
})
}
diff --git a/Nynja/DB/Tables/RoomMemberTable.swift b/Nynja/DB/Tables/RoomMemberTable.swift
index dafffaaf6e04de6f382c99a0ee7a0b56f5d8db7e..9c27d55337f8b8e9bd33b6601766ab2bb34196ee 100644
--- a/Nynja/DB/Tables/RoomMemberTable.swift
+++ b/Nynja/DB/Tables/RoomMemberTable.swift
@@ -15,16 +15,16 @@ class RoomMemberTable: Table {
}
static func create(in db: Database) throws {
- try db.create(table: RoomMemberTable.name, body: { (t) in
+ try db.create(table: RoomMemberTable.name) { t in
t.column(Column.roomId, .text)
t.column(Column.memberId, .integer)
t.column(Column.isAdmin, .boolean).defaults(to: false)
t.uniqueKey([Column.roomId.title, Column.memberId.title, Column.isAdmin.title], onConflict: .replace)
- t.foreignKey([Column.roomId.title], references: RoomTable.name, columns: nil, onDelete: .cascade, onUpdate: nil, deferred: false)
- t.foreignKey([Column.memberId.title], references: MemberTable.name, columns: nil, onDelete: .cascade, onUpdate: nil, deferred: false)
- })
+ t.foreignKey([Column.roomId.title], references: RoomTable.name, onDelete: .cascade)
+ t.foreignKey([Column.memberId.title], references: MemberTable.name, onDelete: .cascade)
+ }
}
}
diff --git a/Nynja/DB/Tables/RoomTable.swift b/Nynja/DB/Tables/RoomTable.swift
index 035c4b71801d279528a61849dec11e15fe9e994b..8cea93bff816eaccb984f72c7d45ed967fb3215e 100644
--- a/Nynja/DB/Tables/RoomTable.swift
+++ b/Nynja/DB/Tables/RoomTable.swift
@@ -15,7 +15,7 @@ class RoomTable: Table {
}
static func create(in db: Database) throws {
- try db.create(table: RoomTable.name) { (t) in
+ try db.create(table: RoomTable.name) { t in
t.column(Column.id, .text).primaryKey()
t.column(Column.name, .text).notNull()
t.column(Column.description, .text)
@@ -31,7 +31,7 @@ class RoomTable: Table {
t.column(Column.created, .integer).notNull().defaults(to: 0)
t.column(Column.status, .text)
- t.column(Column.messageId, .integer).references(MessageTable.name)
+ t.column(Column.messageId, .integer).references(MessageTable.name, onDelete: .setNull)
t.column(Column.rosterId, .integer).references(RosterTable.name)
}
}
diff --git a/Nynja/DatabaseManager.swift b/Nynja/DatabaseManager.swift
index 545449390d72e89802643fb802b2701035c7ac1d..98d1d65329094a7a0bdfc795f18d3902f0979f3a 100644
--- a/Nynja/DatabaseManager.swift
+++ b/Nynja/DatabaseManager.swift
@@ -139,13 +139,6 @@ final class DatabaseManager: DBManagerProtocol {
try values.forEach { (value) in
try database.execute("insert into grdb_migrations (identifier) values('\(value)')")
}
- //let statement = "INSERT INTO \(tableName) (\(identifierColumn)) VALUES (\();"
-
-// let statements = values.map {
-// return "INSERT INTO \(tableName) (\(identifierColumn)) VALUES (`\($0)`)"
-// }
-//
-// try database.execute(statement, arguments: values)
}
}
diff --git a/Nynja/Extensions/SwiftLibrary/String/String+Search.swift b/Nynja/Extensions/SwiftLibrary/String/String+Search.swift
index 7e7afc53010f5b18e00b339122ce30956841d46d..b17ec6e7b225baf4930c85e1334c7948761d3093 100644
--- a/Nynja/Extensions/SwiftLibrary/String/String+Search.swift
+++ b/Nynja/Extensions/SwiftLibrary/String/String+Search.swift
@@ -7,26 +7,21 @@
//
extension String {
-
func isIn(string: String, options: String.CompareOptions) -> Bool {
- return isIn(strings: [string], options: options)
+ return string.contains(substring: self, options: options)
}
func isIn(strings: [String], options: String.CompareOptions) -> Bool {
return strings.contains(substring: self, options: options)
}
+ func contains(substring: String, options: String.CompareOptions) -> Bool {
+ return range(of: substring, options: options) != nil
+ }
}
-
extension Array where Element == String {
-
func contains(substring: String, options: String.CompareOptions) -> Bool {
- guard let _ = self.first(where: { $0.range(of: substring, options: options) != nil }) else {
- return false
- }
-
- return true
+ return contains { $0.contains(substring: substring, options: options) }
}
-
}
diff --git a/Nynja/Extensions/UIEdgeInsets+Adjust.swift b/Nynja/Extensions/UIEdgeInsets+Adjust.swift
index f1b8ecc2df1ce6de796873fae245a7cb23228504..b507e9d7da9958f6d6d419971a52cce5dbf4885c 100644
--- a/Nynja/Extensions/UIEdgeInsets+Adjust.swift
+++ b/Nynja/Extensions/UIEdgeInsets+Adjust.swift
@@ -43,4 +43,7 @@ extension UIEdgeInsets {
right: lhs.right + rhs.right)
}
+ func flippedVertically() -> UIEdgeInsets {
+ return UIEdgeInsets(top: bottom, left: left, bottom: top, right: right)
+ }
}
diff --git a/Nynja/Extensions/VoxImplant/VICallExtension.swift b/Nynja/Extensions/VoxImplant/VICallExtension.swift
deleted file mode 100644
index 09bc26899fd251e4b5d53e12f79ab5745fd38a7d..0000000000000000000000000000000000000000
--- a/Nynja/Extensions/VoxImplant/VICallExtension.swift
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// VICallExtension.swift
-// Nynja
-//
-// Created by Volodymyr Hryhoriev on 3/13/18.
-// Copyright © 2018 TecSynt Solutions. All rights reserved.
-//
-
-import VoxImplant
-
-extension VICall {
-
- var voxId: String? {
- return self.endpoints?.first?.user
- }
-
-}
diff --git a/Nynja/HistoryRequestModelTypeProtocol.swift b/Nynja/HistoryRequestModelTypeProtocol.swift
new file mode 100644
index 0000000000000000000000000000000000000000..99c88972c0d9cbac61555bba2e502b19744a6c9f
--- /dev/null
+++ b/Nynja/HistoryRequestModelTypeProtocol.swift
@@ -0,0 +1,24 @@
+//
+// HistoryRequestModelTypeProtocol.swift
+// Nynja
+//
+// Created by Volodymyr Hryhoriev on 8/3/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+protocol HistoryRequestModelTypeProtocol {
+ var historyRequestModelType: HistoryRequestModel.RequestInput.HistoryType? { get }
+}
+
+extension Contact: HistoryRequestModelTypeProtocol {
+ var historyRequestModelType: HistoryRequestModel.RequestInput.HistoryType? {
+ return phone_id.flatMap { .p2p(opponentId: $0) }
+ }
+}
+
+extension Room: HistoryRequestModelTypeProtocol {
+ var historyRequestModelType: HistoryRequestModel.RequestInput.HistoryType? {
+ return id.flatMap { .muc(mucId: $0) }
+ }
+}
+
diff --git a/Nynja/Library/Interfaces/NavigationProtocol.swift b/Nynja/Library/Interfaces/NavigationProtocol.swift
index 46486c57346c70317d1d5089cc0e31b5d9787478..81a48066a8b63a590f3209a5718107802b6e5ac7 100644
--- a/Nynja/Library/Interfaces/NavigationProtocol.swift
+++ b/Nynja/Library/Interfaces/NavigationProtocol.swift
@@ -9,6 +9,6 @@
import Foundation
-protocol NavigationProtocol {
+protocol NavigationProtocol: class {
func back()
}
diff --git a/Nynja/Library/UI/AlertManager.swift b/Nynja/Library/UI/AlertManager.swift
index 0de42f44c05dc26547dc5059a2162ef2d5cdcdbd..52b3dab16cd5de2973c9fe343f2c0bf87e7a3f05 100644
--- a/Nynja/Library/UI/AlertManager.swift
+++ b/Nynja/Library/UI/AlertManager.swift
@@ -223,3 +223,12 @@ extension AlertManager {
}
}
}
+
+//MARK: - TimeFieldViewController
+extension AlertManager {
+ func showTimeFieldAlert(with initialValue: Int, completion: TimeCallback?) {
+ let alertControler = AlertTextFieldViewController(initialValue: initialValue)
+ alertControler.completion = completion
+ presentingController?.present(alertControler, animated: true, completion: nil)
+ }
+}
diff --git a/Nynja/Library/UI/ContextMenu/NynjaContextMenuItemsFactory+Messages.swift b/Nynja/Library/UI/ContextMenu/NynjaContextMenuItemsFactory+Messages.swift
index 02f8934885012218515ed38b905a3135d54e388b..b9106e6717027989fb8d03aeff408a389e5c4c5b 100644
--- a/Nynja/Library/UI/ContextMenu/NynjaContextMenuItemsFactory+Messages.swift
+++ b/Nynja/Library/UI/ContextMenu/NynjaContextMenuItemsFactory+Messages.swift
@@ -180,7 +180,7 @@ extension NynjaContextMenuItemsFactory {
}
private static func audioCallMessageItems(for model: BaseChatCellModel) -> [ContextMenuRow] {
- return [ ContextMenuRow(items: [marketPlace()])]
+ return [ ContextMenuRow(items: [marketplace()])]
}
private static func translatedMessageItems(for model: BaseChatCellModel) -> [ContextMenuRow] {
@@ -256,8 +256,8 @@ extension NynjaContextMenuItemsFactory {
layout: layout)
}
- static func marketPlace(with layout: ContextMenuItem.LayoutRepresentation = .default) -> ContextMenuItem {
- return ContextMenuItem(content: .item(title: Strings.marketPlace.localized, icon: #imageLiteral(resourceName: "ic_marketplace_context_menu"), action: .marketPlace),
+ static func marketplace(with layout: ContextMenuItem.LayoutRepresentation = .double) -> ContextMenuItem {
+ return ContextMenuItem(content: .item(title: Strings.marketplace.localized, icon: #imageLiteral(resourceName: "ic_marketplace_context_menu"), action: .marketPlace),
layout: layout)
}
@@ -362,7 +362,7 @@ private enum Strings: String {
case share = "share"
case saveToGallery = "save_to_gallery"
case saveToDownloads = "save_to_downloads"
- case marketPlace = "Market Place"
+ case marketplace = "marketplace"
var localized: String {
return rawValue.localized
diff --git a/Nynja/Library/UI/Extensions/Localizable.swift b/Nynja/Library/UI/Extensions/Localizable.swift
index 521411cb3417d8cf0c29b3fdff3b67b6f7c929f3..57dd6a27659e2319135f15bcf539867953cffbd2 100644
--- a/Nynja/Library/UI/Extensions/Localizable.swift
+++ b/Nynja/Library/UI/Extensions/Localizable.swift
@@ -26,6 +26,7 @@ enum Language: String {
case chinese_simplified = "zh-Hans"
case japanese = "ja"
case korean = "ko"
+ case empty_default = "empty"
static let allValues = [english, german, spanish,
french, italian, portuguese,
@@ -46,6 +47,12 @@ enum Language: String {
}
}
+ static var allValuesWithDefault: [Language] {
+ var languages = Language.allValues
+ languages.append(.empty_default)
+ return languages
+ }
+
var longValue : String {
switch self {
case .english: return "English"
@@ -59,6 +66,7 @@ enum Language: String {
case .chinese_simplified: return "Chinese Simplified (简体中文)"
case .japanese: return "Japanese (日本語)"
case .korean: return "Korean (한국어)"
+ case .empty_default: return "-"
}
}
diff --git a/Nynja/Library/UI/ReturnToCallContentView.swift b/Nynja/Library/UI/ReturnToCallContentView.swift
index 68e828e360b193103f53a01bc87fe09deb93c7a7..540dbb750acc07456d77e1f565d99b2b0a7dbea5 100644
--- a/Nynja/Library/UI/ReturnToCallContentView.swift
+++ b/Nynja/Library/UI/ReturnToCallContentView.swift
@@ -7,7 +7,6 @@
//
import Foundation
-import VoxImplant
extension ReturnToCallContentView {
struct Constraints {
@@ -28,7 +27,6 @@ extension ReturnToCallContentView {
class ReturnToCallContentView: UIView {
var timer:Timer?
- weak var call: VICall?
weak var nynCall: NYNCall?
@@ -77,40 +75,35 @@ class ReturnToCallContentView: UIView {
})
return lbl
}()
-
- func setup(call: VICall) {
- self.call = call
- self.startTimer()
- self.backgroundColor = Constants.colors.greenForReturnToCallColor.getColor()
- img.isHidden = false
- hing.isHidden = false
- }
func setup(call: NYNCall) {
self.nynCall = call
- self.startTimer()
+// self.startTimer()
self.backgroundColor = Constants.colors.greenForReturnToCallColor.getColor()
img.isHidden = false
hing.isHidden = false
}
- func startTimer() {
- timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimedCode), userInfo: nil, repeats: true)
- }
- @objc func runTimedCode() {
- if let call = self.call {
- let durationInt = Int(call.duration())
- let minutes = durationInt / 60
- let seconds = durationInt % 60
-
- if let status = call.callStatus {
- if status == .ongoing {
- self.time.text = String.localizedStringWithFormat("%02u:%02u", minutes,seconds)
- } else {
- self.time.text = status.rawValue
- }
- }
- }
- }
+ //TODO: ASK ANGEL
+
+// func startTimer() {
+// timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimedCode), userInfo: nil, repeats: true)
+// }
+
+// @objc func runTimedCode() {
+// if let call = self.call {
+// let durationInt = Int(call.duration())
+// let minutes = durationInt / 60
+// let seconds = durationInt % 60
+//
+// if let status = call.callStatus {
+// if status == .ongoing {
+// self.time.text = String.localizedStringWithFormat("%02u:%02u", minutes,seconds)
+// } else {
+// self.time.text = status.rawValue
+// }
+// }
+// }
+// }
}
diff --git a/Nynja/Library/UI/SeparatorView/SeparatorView.swift b/Nynja/Library/UI/SeparatorView/SeparatorView.swift
index 22bc1591250badb3c6b62edd8906f5f380fa0687..4a620639ee9d9c930925ccfc5aca3f8954113053 100644
--- a/Nynja/Library/UI/SeparatorView/SeparatorView.swift
+++ b/Nynja/Library/UI/SeparatorView/SeparatorView.swift
@@ -36,6 +36,7 @@ final class SeparatorView: UIView {
// MARK: - Setup
private func setup() {
+ backgroundColor = color
snp.makeConstraints { maker in
maker.height.equalTo(SeparatorView.height)
}
diff --git a/Nynja/Library/UI/TextInput/InputBar/Extensions/InputBar+ContentType.swift b/Nynja/Library/UI/TextInput/InputBar/Extensions/InputBar+ContentType.swift
index cf2101d0325e5f48f786d54500e5b3ff976686b0..bf773eafc6394d9ce3d2ca0e53e2cae0d7ea793b 100644
--- a/Nynja/Library/UI/TextInput/InputBar/Extensions/InputBar+ContentType.swift
+++ b/Nynja/Library/UI/TextInput/InputBar/Extensions/InputBar+ContentType.swift
@@ -41,6 +41,15 @@ extension InputBar {
static func ==(lhs: ContentType, rhs: ContentType) -> Bool {
return lhs.hashValue == rhs.hashValue
}
+
+ var shouldRemovePreviousContent: Bool {
+ switch self {
+ case .recording, .recordDisplay:
+ return false
+ default:
+ return true
+ }
+ }
}
}
diff --git a/Nynja/Library/UI/TextInput/InputBar/InputBar.swift b/Nynja/Library/UI/TextInput/InputBar/InputBar.swift
index 825a03733256ecffe0d87e634ee0919d6bbb6e5b..645d2669c104d4746f3b7ed516c3819026947804 100644
--- a/Nynja/Library/UI/TextInput/InputBar/InputBar.swift
+++ b/Nynja/Library/UI/TextInput/InputBar/InputBar.swift
@@ -86,7 +86,7 @@ final class InputBar: UIView {
private var contentType: ContentType = .text(nil) {
didSet {
if oldValue != contentType {
- setupContent(contentType)
+ setupContent(contentType, oldType: oldValue)
setupTestingKeys()
} else {
updateContent(contentType)
@@ -135,6 +135,8 @@ final class InputBar: UIView {
return view
}()
+ private var recordingContainer: RecordContainer?
+
private lazy var rightButton: ScheduleButton = {
let width = Constraints.Main.RightButton.width
let height = Constraints.Main.RightButton.height
@@ -203,16 +205,39 @@ final class InputBar: UIView {
backgroundColor = .clear
setupButton(.voice)
- setupContent(.text(nil))
+ setupContent(.text(nil), oldType: .text(nil))
+
setupTestingKeys()
}
// MARK: - Setup
// MARK: - Content
- private func setupContent(_ type: ContentType) {
- inputContentContainer.subviews.removeFromSuperview()
-
+ private func setupContent(_ type: ContentType, oldType: ContentType) {
+ //TODO: - Keep calm and don't read that shit
+ if type.shouldRemovePreviousContent {
+ if oldType.shouldRemovePreviousContent {
+ inputContentContainer.subviews.removeFromSuperview()
+ } else {
+ recordingContainer?.removeFromSuperview()
+ recordingContainer = nil
+
+ if let textInput = inputContentContainer.subviews.first as? TextInputContent {
+ textInput.isHidden = false
+ textInput.delegate = self
+ textInput.textView.textViewDidChange()
+ inputContent = textInput
+ inputContent?.startInteraction()
+ return
+ }
+ }
+ } else {
+ if let textInput = inputContent as? TextInputContent {
+ textInput.isHidden = true
+ textInput.delegate = nil
+ }
+ }
+
guard let inputContentView = makeInputContentView(for: type) else {
return
}
@@ -260,6 +285,28 @@ final class InputBar: UIView {
return content
}
+ private func makeRecordingContainer(_ view: InputContentView) -> RecordContainer {
+ let containerMaker: () -> RecordContainer = {
+ let container = RecordContainer()
+ container.content = view
+ return container
+ }
+ return recordingContainer ?? containerMaker()
+ }
+
+ private func wrapRecordingContentToContainer(_ view: InputContentView) -> RecordContainer {
+ let container = makeRecordingContainer(view)
+ container.subviews.removeFromSuperview()
+ container.addSubview(view)
+ view.snp.makeConstraints { maker in
+ maker.edges.equalToSuperview()
+ }
+
+ recordingContainer = container
+
+ return container
+ }
+
private func makeRecordingContent() -> InputContentView? {
guard let content = RecordingInputContent(recordingStatusHandler: { [weak self] status in
self?.sendTypingHandler?(status)
@@ -281,13 +328,7 @@ final class InputBar: UIView {
return nil
}
- inputContentContainer.addSubview(content)
- content.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
- inputContent = content
-
- return content
+ return wrapRecordingContentToContainer(content)
}
private func makeRecordDisplayContent(with url: URL) -> InputContentView {
@@ -299,7 +340,7 @@ final class InputBar: UIView {
self?.buttonType = .voice
}
- return content
+ return wrapRecordingContentToContainer(content)
}
private func makeNynjaAction(with info: DisplayMode.ActionInfo) -> InputContentView {
@@ -622,12 +663,10 @@ final class InputBar: UIView {
rightButton.backgroundColor = Constants.colors.red.getColor()
rightButton.isEnabled = true
}
- }
-
-
- // MARK: - ALTextInputBarDelegate
+}
- extension InputBar: ALTextInputBarDelegate {
+//MARK: - ALTextInputBarDelegate
+extension InputBar: ALTextInputBarDelegate {
func textViewDidChange(textView: ALTextView) {
if case .edit(let text) = displayMode {
@@ -704,8 +743,7 @@ final class InputBar: UIView {
}
-// MARK: - Layout
-
+//MARK: - Layout
extension InputBar {
enum Constraints {
enum Main {
@@ -732,8 +770,7 @@ extension InputBar {
}
-// MARK: - Testable
-
+//MARK: - Testable
extension InputBar: TestableViewProtocol {
private enum Keys: String {
case sendButton = "input_bar_send_button"
@@ -753,4 +790,3 @@ extension InputBar: TestableViewProtocol {
actionButton?.accessibilityIdentifier = Keys.actionButton.rawValue
}
}
-
diff --git a/Nynja/Library/UI/TextInput/InputBar/InputContent/RecordContainer.swift b/Nynja/Library/UI/TextInput/InputBar/InputContent/RecordContainer.swift
new file mode 100644
index 0000000000000000000000000000000000000000..5e29e56cb3878abeb11d917a1a500bc4ed0fc76d
--- /dev/null
+++ b/Nynja/Library/UI/TextInput/InputBar/InputContent/RecordContainer.swift
@@ -0,0 +1,20 @@
+//
+// RecordContainer.swift
+// Nynja
+//
+// Created by Andrey Reznik on 07.08.2018.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+class RecordContainer: UIView, InputContentProtocol {
+ var content: InputContentProtocol?
+
+ func startInteraction() {
+ content?.startInteraction()
+ }
+ func cancelInteraction() {
+ content?.cancelInteraction()
+ }
+}
diff --git a/Nynja/Library/UI/TextInput/Material/MaterialTextField.swift b/Nynja/Library/UI/TextInput/Material/MaterialTextField.swift
index 21b13e4d40fdbbf693107244e7d6aa2418c03f41..22015ccb3985bc86e9ba103a70f85790d9a8f664 100644
--- a/Nynja/Library/UI/TextInput/Material/MaterialTextField.swift
+++ b/Nynja/Library/UI/TextInput/Material/MaterialTextField.swift
@@ -62,6 +62,10 @@ class MaterialTextField: MaterialTextContainer {
textField.accessibilityIdentifier = "material_text_field"
}
+ override func becomeFirstResponder() -> Bool {
+ return self.textField.becomeFirstResponder()
+ }
+
// MARK: - Actions
diff --git a/Nynja/Library/UI/UITableViewCells/ChatSettingCell/AvatarCell.swift b/Nynja/Library/UI/UITableViewCells/ChatSettingCell/AvatarCell.swift
index 62739e65facf191db38f6759f571c9cb41bf934b..dc89bfc4ec7d601eb5b497b7dd34a14b3b281800 100644
--- a/Nynja/Library/UI/UITableViewCells/ChatSettingCell/AvatarCell.swift
+++ b/Nynja/Library/UI/UITableViewCells/ChatSettingCell/AvatarCell.swift
@@ -114,7 +114,7 @@ class AvatarCell: BaseSettingCell, ConfigurableCell {
// MARK: - Actions
@objc private func avatarTapped(_ recognizer: UITapGestureRecognizer) {
- delegate?.didTapSetting(.Avatar)
+ delegate?.didTapSetting(.Avatar(avatar))
}
@objc private func nameGroupTapped(_ recognizer: UITapGestureRecognizer) {
diff --git a/Nynja/Library/UI/UITableViewCells/ChatSettingCell/SettingViewModelProtocol.swift b/Nynja/Library/UI/UITableViewCells/ChatSettingCell/SettingViewModelProtocol.swift
index d45ffc9bf11c7984aea4ace37add2e35a4fb0652..4e45469c806f6f5f42f74314f37321756802ad07 100644
--- a/Nynja/Library/UI/UITableViewCells/ChatSettingCell/SettingViewModelProtocol.swift
+++ b/Nynja/Library/UI/UITableViewCells/ChatSettingCell/SettingViewModelProtocol.swift
@@ -8,7 +8,7 @@
enum SettingViewModelType {
- case Avatar
+ case Avatar(UIImageView)
case Name
case Alias
case Rules
diff --git a/Nynja/Library/UI/View/NavigationView/NavigationView.swift b/Nynja/Library/UI/View/NavigationView/NavigationView.swift
index 8789e7a190ece44faaf1bc37016de242a50835fc..e2cc6a7c051ef137e14efc143645cbf47f0cd3cd 100644
--- a/Nynja/Library/UI/View/NavigationView/NavigationView.swift
+++ b/Nynja/Library/UI/View/NavigationView/NavigationView.swift
@@ -44,7 +44,7 @@ class NavigationView: BaseView, Configurable {
}
private var title: String?
- private var navigationHandler: NavigationProtocol?
+ private weak var navigationHandler: NavigationProtocol?
private var backButtonImage: UIImage? = UIImage.backButtonImage {
didSet { backButton?.setImage(backButtonImage, for: .normal) }
}
diff --git a/Nynja/MQTTModels/MessageExtension+BERT.swift b/Nynja/MQTTModels/MessageExtension+BERT.swift
index 484da32c76d6e44654885d5acac82244fad331ca..970f0ec6467201463f0c3d1b762fd3deb52f9d00 100644
--- a/Nynja/MQTTModels/MessageExtension+BERT.swift
+++ b/Nynja/MQTTModels/MessageExtension+BERT.swift
@@ -10,7 +10,7 @@ import Foundation
extension Message {
- func getBert(chain : String? = "chain") -> BertObject {
+ func getBert(chain: String? = "chain") -> BertObject {
let topic = BertAtom(fromString: "Message")
let _from = Bert.getBin(self.from)
let _to = Bert.getBin(self.to)
diff --git a/Nynja/MessageDAO.swift b/Nynja/MessageDAO.swift
index 7ffbb77fd534b4e69d200b42a2bdfb6292d06b16..67b936cbb5b312e9a9a80f189c28c3b50f91f846 100644
--- a/Nynja/MessageDAO.swift
+++ b/Nynja/MessageDAO.swift
@@ -18,38 +18,44 @@ class MessageDAO: MessageDAOProtocol {
// MARK: - Fetch
// MARK: -- Message
static func fetchMessage(by rowId: Int64) -> Message? {
- guard let message = dbManager.fetch({ db in
+ return fetchMessage { db in
return try DBMessage.message(from: db, rowId: rowId)
- }) else {
- return nil
}
-
- return Message(message: message)
}
static func fetchMessage(primaryId: Int64) -> Message? {
- guard let message = dbManager.fetch({ db in
+ return fetchMessage { db in
return try DBMessage.message(db, id: primaryId)
- }) else {
- return nil
}
-
- return Message(message: message)
}
static func fetchMessage(serverId: Int64) -> Message? {
- guard let message = dbManager.fetch({ db in
+ return fetchMessage { db in
return try DBMessage.message(db, serverId: serverId)
- }) else {
- return nil
}
-
- return Message(message: message)
}
static func fetchMessage(localId: String) -> Message? {
- guard let message = dbManager.fetch({ db in
+ return fetchMessage { db in
return try DBMessage.message(db, localId: localId)
+ }
+ }
+
+ static func fetchLastMessage(of type: FetchType) -> Message? {
+ return fetchMessage { db in
+ return try DBMessage.lastMessage(db, ofType: type)
+ }
+ }
+
+ static func fetchPenultimateMessage(of type: FetchType) -> Message? {
+ return fetchMessage { db in
+ return try DBMessage.penultimateMessage(db, ofType: type)
+ }
+ }
+
+ private static func fetchMessage(using closure: (Database) throws -> DBMessage?) -> Message? {
+ guard let message = dbManager.fetch({ db in
+ return try closure(db)
}) else {
return nil
}
@@ -125,48 +131,50 @@ class MessageDAO: MessageDAOProtocol {
}
// MARK: - Remove
- static func removeMessage(message: Message, notify: ((_ message: Message) -> Void)? = nil) {
- let msg = Message()
- msg.id = message.link
- msg.feed_id = message.feed_id
- if message.feed_id as? p2p != nil {
- if let ids = message.seenby as? [Int64] {
- let res = ids.filter { (value) -> Bool in
- return value == -1
- }.count
- if res > 0 {
- removeMessage(msg: msg)
- notify?(msg)
- return
- }
- }
- if let ids = message.seenby as? [String], let phoneId = ids.first {
- if phoneId == StorageService.sharedInstance.phoneId {
- removeMessage(msg: msg)
- notify?(msg)
- return
- }
- }
- return
+
+ static func removeMessage(using message: Message) -> Bool {
+ guard let id = message.link,
+ let deletedMessage = fetchMessage(serverId: id) else {
+ return false
+ }
+
+ let shouldMarkMessageAsDelete = self.shouldMarkMessageAsDelete(message)
+ if shouldMarkMessageAsDelete {
+ deletedMessage.status = StringAtom(string: "deleted")
+ }
+ deletedMessage.seenby = message.seenby
+
+ do {
+ try dbManager.perform(action: .save, with: deletedMessage)
+ } catch {
+ return false
+ }
+
+ return shouldMarkMessageAsDelete
+ }
+
+ private static func shouldMarkMessageAsDelete(_ message: Message) -> Bool {
+ let stringIds = message.seenby as? [String] ?? []
+ let intIds = message.seenby as? [Int64] ?? []
+
+ guard !stringIds.contains("-1"),
+ !intIds.contains(-1) else {
+ return true
+ }
+
+ guard let phoneId = StorageService.sharedInstance.phoneId else {
+ assertionFailure("Check phoneId")
+ return false
}
- if let id = (message.feed_id as? muc)?.name {
- if let ids = message.seenby as? [Int64] {
- let res = ids.filter { (value) -> Bool in
- return value == -1
- }.count
- if res > 0 {
- removeMessage(msg: msg)
- notify?(msg)
- return
- }
- if let isNeedRemove = RoomDAO.containsMembers(with: ids, roomId: id), isNeedRemove {
- removeMessage(msg: msg)
- notify?(msg)
- return
- }
- }
- return
+
+ if let _ = message.p2pFeed {
+ return stringIds.contains(phoneId)
+ } else if let roomId = message.mucFeed?.name,
+ let _ = MemberDAO.findMemberBy(roomId: roomId, phoneId: phoneId) {
+ return true
}
+
+ return false
}
private static func findLocalId(serverId: Int64) -> String? {
@@ -182,58 +190,6 @@ class MessageDAO: MessageDAOProtocol {
}
}
- private static func removeMessage(msg: Message) {
- var message = msg
- message = updateLocalMessageId(for: message)
- try? StorageService.sharedInstance.perform(action: .delete, with: message)
- if let feed = msg.feed_id {
- updateLastMessage(feed: feed)
- }
- }
-
- private static func updateLastMessage(feed: AnyObject) {
- if let m = feed as? muc, let name = m.name {
- guard let lastMessage = lastMessage(of: .muc(name: name)),
- let room = RoomDAO.fetchRoom(by: name) else {
- return
- }
-
- room.messageId = lastMessage.id
-
- let action: DatabaseAction = .updateColumns([RoomTable.Column.messageId.title])
- try? StorageService.sharedInstance.perform(action: action, with: room)
- }
- if let p = feed as? p2p, let to = p.to, let fr = p.from {
- guard let target = p.opponentId,
- let lastMessage = lastMessage(of: .p2p(from: fr, to: to)),
- let contact = ContactDAO.fetchContact(by: target) else {
- return
- }
-
- contact.messageId = lastMessage.id
-
- let action: DatabaseAction = .updateColumns([ContactTable.Column.messageId.title])
- try? StorageService.sharedInstance.perform(action: action, with: contact)
- }
- }
-
- private static func lastMessage(of fetchType: FetchType) -> DBMessage? {
- return dbManager.fetch { db in
- return try DBMessage.lastMessage(db, ofType: fetchType)
- }
- }
-
- // MARK: - Update
- static func updateLocalMessageId(for message: Message) -> Message {
- var localID: String? = nil
-
- if let id = message.id, let res = findLocalId(serverId: id) {
- localID = res
- }
-
- message.msg_id = localID
- return message
- }
// MARK: - Helpers
diff --git a/Nynja/MessageDAOProtocol.swift b/Nynja/MessageDAOProtocol.swift
index 831db64c8b4abb7532d91541aab3cb90bbf3f969..0db692d17c78d116c0a38c8ad6929c4027dc5138 100644
--- a/Nynja/MessageDAOProtocol.swift
+++ b/Nynja/MessageDAOProtocol.swift
@@ -23,6 +23,9 @@ protocol MessageDAOProtocol: DAOProtocol {
static func fetchMessage(serverId: Int64) -> Message?
static func fetchMessage(localId: String) -> Message?
+ static func fetchLastMessage(of type: FetchType) -> Message?
+ static func fetchPenultimateMessage(of type: FetchType) -> Message?
+
// MARK: -- Unsent
static func fetchUnsentMessages(from: String) -> [Message]
@@ -40,9 +43,6 @@ protocol MessageDAOProtocol: DAOProtocol {
static func clearHistory(_ type: FetchType) throws
// MARK: - Remove
- static func removeMessage(message: Message, notify: ((_ message: Message) -> Void)?)
-
- // MARK: - Update
- static func updateLocalMessageId(for message: Message) -> Message
+ static func removeMessage(using message: Message) -> Bool
}
diff --git a/Nynja/MigrationManager.swift b/Nynja/MigrationManager.swift
index 5e32d88dceff843ae6a9eb36b34419dbf7c59e6a..238d326c1fb3520c5bfbd1a4b43fcd60e879eee1 100644
--- a/Nynja/MigrationManager.swift
+++ b/Nynja/MigrationManager.swift
@@ -19,6 +19,8 @@ enum Migration: Int, Describable {
case renameLinksToMessageLink
case createLinkForRoom
case renameChannelFeatureKeys
+ case updateMessageIdForeignKeyOnContactRoomTables
+ case primaryKeyMessageAction
static var allTitles: [String] = {
var i = 0
@@ -202,6 +204,33 @@ class MigrationManager {
try self.replaceFeatureKey("SubscribersCount", newKey: .subscribersCount, db: db)
try self.replaceFeatureKey("AdminsCount", newKey: .adminsCount, db: db)
}
+
+ migrator.registerMigration(Migration.updateMessageIdForeignKeyOnContactRoomTables.title) { db in
+ let rooms = try DBRoom.fetchAll(db)
+ let contacts = try DBContact.fetchAll(db)
+ let roomMembers = try DBRoomMember.fetchAll(db)
+ let links = try DBLink.fetchAll(db)
+
+ try db.drop(table: LinkTable.name)
+ try db.drop(table: RoomTable.name)
+ try db.drop(table: ContactTable.name)
+
+ try RoomTable.create(in: db)
+ try ContactTable.create(in: db)
+ try LinkTable.create(in: db)
+
+ try rooms.forEach { try $0.save(db) }
+ try contacts.forEach { try $0.save(db) }
+ try links.forEach { try $0.save(db) }
+ try roomMembers.forEach { try $0.save(db) }
+ }
+
+ migrator.registerMigration(Migration.primaryKeyMessageAction.title) { db in
+ let actions = try DBMessageAction.fetchAll(db)
+ try db.drop(table: MessageActionTable.name)
+ try MessageActionTable.create(in: db)
+ try actions.forEach { try $0.saveAggregate(db) }
+ }
}
private func recreateDescTable(_ db: Database, closure: ((DBDesc) -> Void)? = nil) throws {
diff --git a/Nynja/Models/ChatModel.swift b/Nynja/Models/ChatModel.swift
index 56cea723d1e835ac54eea8119d62165685b94ce1..bb50bb25c7291c109c018a972d4b9fbdc2b6670a 100644
--- a/Nynja/Models/ChatModel.swift
+++ b/Nynja/Models/ChatModel.swift
@@ -6,22 +6,4 @@
// Copyright © 2018 TecSynt Solutions. All rights reserved.
//
-protocol HistoryRequestModelTypeProtocol {
- var historyRequestModelType: HistoryRequestModel.RequestInput.HistoryType? { get }
-}
-
typealias ChatModel = BaseChatModel & DBModelConvertible & HistoryRequestModelTypeProtocol
-
-extension Contact: HistoryRequestModelTypeProtocol {
- var historyRequestModelType: HistoryRequestModel.RequestInput.HistoryType? {
- guard let phone_id = phone_id else { return nil }
- return .p2p(opponentId: phone_id)
- }
-}
-
-extension Room: HistoryRequestModelTypeProtocol {
- var historyRequestModelType: HistoryRequestModel.RequestInput.HistoryType? {
- guard let id = id else { return nil }
- return .muc(mucId: id)
- }
-}
diff --git a/Nynja/Modules/AddParticipants/View/AddParticipantsViewController.swift b/Nynja/Modules/AddParticipants/View/AddParticipantsViewController.swift
index 3fba25e6885c23418c7742effbd86f57b1d49c61..f130142853afa5af381e685a1a420accce18ad2d 100644
--- a/Nynja/Modules/AddParticipants/View/AddParticipantsViewController.swift
+++ b/Nynja/Modules/AddParticipants/View/AddParticipantsViewController.swift
@@ -61,7 +61,7 @@ class AddParticipantsViewController: BaseVC, AddParticipantsViewProtocol {
return button
}()
- // MARK: Views
+ // MARK: - Views
private lazy var avatarsView: UICollectionView = {
let collView = UICollectionView(frame: CGRect.zero, collectionViewLayout: createLayout())
@@ -77,8 +77,20 @@ class AddParticipantsViewController: BaseVC, AddParticipantsViewProtocol {
return collView
}()
+
+ private lazy var separatorView: SeparatorView = {
+ let separatorView = SeparatorView()
+
+ view.addSubview(separatorView)
+ separatorView.snp.makeConstraints { maker in
+ maker.left.right.equalToSuperview()
+ maker.top.equalTo(avatarsView.snp.bottom).offset(Constraints.separator.topInset.adjustedByWidth)
+ }
+
+ return separatorView
+ }()
- lazy var tableView: UITableView = {
+ private lazy var tableView: UITableView = {
let tblView = UITableView.default
tblView.rowHeight = SelectorCell.Constraints.height
@@ -86,11 +98,16 @@ class AddParticipantsViewController: BaseVC, AddParticipantsViewProtocol {
tblView.sectionHeaderHeight = ParticipantsHeaderView.height
tblView.estimatedSectionHeaderHeight = tblView.sectionHeaderHeight
+ tblView.contentInset = UIEdgeInsets(top: CGFloat(Constraints.tableView.topInset.adjustedByWidth),
+ left: 0,
+ bottom: 0,
+ right: 0)
+
self.view.addSubview(tblView)
- tblView.snp.makeConstraints({ (make) in
- make.top.equalTo(avatarsView.snp.bottom).offset(Constraints.tableView.topInset.adjustedByHeight)
+ tblView.snp.makeConstraints { (make) in
+ make.top.equalTo(separatorView.snp.bottom)
make.left.right.equalTo(avatarsView)
- })
+ }
return tblView
}()
@@ -154,17 +171,17 @@ class AddParticipantsViewController: BaseVC, AddParticipantsViewProtocol {
title = "delete_participants".localized.uppercased()
} else if presenter.participantsMode == .createGroupCall {
title = "add_members_to_call".localized.uppercased()
- backBtnImage = UIImage(named:"ic_close_clear")
+ backBtnImage = .closeImage
navHandler = presenter
self.selectAllButtonVisible = true
} else if presenter.participantsMode == .updateGroupCall {
title = "add_members_to_call".localized.uppercased()
- backBtnImage = UIImage(named:"ic_close_clear")
+ backBtnImage = .closeImage
navHandler = presenter
self.selectAllButtonVisible = true
} else if presenter.participantsMode == .createConferenceCall {
title = "add_members_to_call".localized.uppercased()
- backBtnImage = UIImage(named:"ic_close_clear")
+ backBtnImage = .closeImage
navHandler = presenter
self.selectAllButtonVisible = true
} else if presenter.participantsMode == .admins {
@@ -229,6 +246,10 @@ class AddParticipantsViewController: BaseVC, AddParticipantsViewProtocol {
tableView.register(headerFooter: ParticipantsHeaderView.self)
participantsDataSource = ParticipantsDataSource()
+ if self.presenter.participantsMode == .create {
+ participantsDataSource.selectedParticipantsDelegate = self
+ }
+
tableView.dataSource = participantsDataSource
avatarsView.dataSource = participantsDataSource
@@ -458,3 +479,9 @@ extension AddParticipantsViewController: TestableViewControllerProtocol {
}
}
}
+
+extension AddParticipantsViewController: NotifySelectedParticipantsProtocol {
+ func notify(selectedParticipants count: Int) {
+ self.doneButton.isEnabled = count != 0
+ }
+}
diff --git a/Nynja/Modules/AddParticipants/View/AddPaticipantsViewControllerLayout.swift b/Nynja/Modules/AddParticipants/View/AddPaticipantsViewControllerLayout.swift
index d53d76df2e5ec122fe753666877265f8ddd52501..d452402adcd2a17d83bddf35afe1a1b0514d9343 100644
--- a/Nynja/Modules/AddParticipants/View/AddPaticipantsViewControllerLayout.swift
+++ b/Nynja/Modules/AddParticipants/View/AddPaticipantsViewControllerLayout.swift
@@ -8,24 +8,28 @@
extension AddParticipantsViewController {
- struct Constraints {
+ enum Constraints {
- struct avatarsView {
+ enum avatarsView {
static let height: CGFloat = 44.0
static let topInset = 8.0
static let horizontalInset: CGFloat = 16.0
}
- struct tableView {
+ enum tableView {
static let topInset = 16.0
}
- struct controlsContainerView {
+ enum separator {
+ static let topInset = 8.0
+ }
+
+ enum controlsContainerView {
static let bottomInset: CGFloat = 28.0
}
- struct doneButton {
+ enum doneButton {
static let width: CGFloat = 82.0
static let height: CGFloat = 44.0
@@ -33,11 +37,10 @@ extension AddParticipantsViewController {
static let contentRightInset: CGFloat = 16.0
}
- struct SelectAllButton {
+ enum SelectAllButton {
static let side = 44.0.adjustedByWidth
static let leftInset = -2.0.adjustedByWidth
static let rightInset = 6.0.adjustedByWidth
}
-
}
}
diff --git a/Nynja/Modules/AddParticipants/View/TableView/ParticipantsDataSource.swift b/Nynja/Modules/AddParticipants/View/TableView/ParticipantsDataSource.swift
index 7f91530f939c3617257d922ef7534093c6e1a99c..5f0f62d5e854c374936a6a052f0529ea1f96010e 100644
--- a/Nynja/Modules/AddParticipants/View/TableView/ParticipantsDataSource.swift
+++ b/Nynja/Modules/AddParticipants/View/TableView/ParticipantsDataSource.swift
@@ -5,8 +5,14 @@
// Created by Volodymyr Hryhoriev on 11/8/17.
//
+protocol NotifySelectedParticipantsProtocol: class {
+ func notify(selectedParticipants count: Int)
+}
+
class ParticipantsDataSource: NSObject, UITableViewDataSource, UICollectionViewDataSource {
+ weak var selectedParticipantsDelegate: NotifySelectedParticipantsProtocol?
+
var groupedParticipants: GroupedParticipants = [:] {
didSet {
sortedLetters = Array(groupedParticipants.keys).sorted()
@@ -15,7 +21,11 @@ class ParticipantsDataSource: NSObject, UITableViewDataSource, UICollectionViewD
var sortedLetters: [String] = []
- var selectedParticipants: [Participant] = []
+ var selectedParticipants: [Participant] = [] {
+ didSet {
+ self.selectedParticipantsDelegate?.notify(selectedParticipants: self.selectedParticipants.count)
+ }
+ }
var emptySelectionText: String = "no_participant_selected".localized {
didSet {
diff --git a/Nynja/Modules/AssigningInterpreter/AssigningInterpreterInteractor.swift b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterInteractor.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2f7d232476c403b7f02a20084c50803ea4b10116
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterInteractor.swift
@@ -0,0 +1,24 @@
+//
+// AssigningInterpreterInteractor.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class AssigningInterpreterInteractor: AssigningInterpreterInteractorInputProtocol {
+
+ weak var presenter: AssigningInterpreterInteractorOutputProtocol!
+}
+
+extension AssigningInterpreterInteractor: SetInjectable {
+ func inject(dependencies: AssigningInterpreterInteractor.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: AssigningInterpreterInteractorOutputProtocol
+ }
+}
diff --git a/Nynja/Modules/AssigningInterpreter/AssigningInterpreterPresenter.swift b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterPresenter.swift
new file mode 100644
index 0000000000000000000000000000000000000000..70094e9e995e6d89975344038eff68ccdfaa85fa
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterPresenter.swift
@@ -0,0 +1,34 @@
+//
+// AssigningInterpreterPresenter.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class AssigningInterpreterPresenter: BasePresenter, AssigningInterpreterPresenterProtocol, AssigningInterpreterInteractorOutputProtocol {
+
+ weak var view: AssigningInterpreterViewProtocol!
+ var interactor: AssigningInterpreterInteractorInputProtocol!
+ var wireFrame: AssigningInterpreterWireFrameProtocol!
+
+ func showed() {
+
+ }
+}
+
+extension AssigningInterpreterPresenter: SetInjectable {
+ func inject(dependencies: AssigningInterpreterPresenter.Dependencies) {
+ self.view = dependencies.view
+ self.interactor = dependencies.interactor
+ self.wireFrame = dependencies.wireFrame
+ }
+
+ struct Dependencies {
+ let view: AssigningInterpreterViewProtocol
+ let interactor: AssigningInterpreterInteractorInputProtocol
+ let wireFrame: AssigningInterpreterWireFrameProtocol
+ }
+}
diff --git a/Nynja/Modules/AssigningInterpreter/AssigningInterpreterProtocols.swift b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterProtocols.swift
new file mode 100644
index 0000000000000000000000000000000000000000..74cadbfa0cedfbb349c53a743d82332c55567101
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterProtocols.swift
@@ -0,0 +1,57 @@
+//
+// AssigningInterpreterProtocols.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+protocol AssigningInterpreterWireFrameProtocol: class {
+
+ func presentAssigningInterpreter(navigation: UINavigationController)
+
+ /**
+ * Add here your methods for communication PRESENTER -> WIREFRAME
+ */
+ func dismiss()
+}
+
+protocol AssigningInterpreterViewProtocol: class {
+
+ var presenter: AssigningInterpreterPresenterProtocol! { get set }
+
+ /**
+ * Add here your methods for communication PRESENTER -> VIEW
+ */
+
+}
+
+protocol AssigningInterpreterPresenterProtocol: BasePresenterProtocol {
+
+ var view: AssigningInterpreterViewProtocol! { get set }
+ var interactor: AssigningInterpreterInteractorInputProtocol! { get set }
+ var wireFrame: AssigningInterpreterWireFrameProtocol! { get set }
+
+ /**
+ * Add here your methods for communication VIEW -> PRESENTER
+ */
+ func dismiss()
+}
+
+protocol AssigningInterpreterInteractorOutputProtocol: class {
+
+ /**
+ * Add here your methods for communication INTERACTOR -> PRESENTER
+ */
+}
+
+protocol AssigningInterpreterInteractorInputProtocol: class {
+
+ var presenter: AssigningInterpreterInteractorOutputProtocol! { get set }
+
+ /**
+ * Add here your methods for communication PRESENTER -> INTERACTOR
+ */
+}
diff --git a/Nynja/Modules/AssigningInterpreter/AssigningInterpreterViewController.swift b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterViewController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..40fc10c209ad53350d543c8835222c8e89ddfa1e
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterViewController.swift
@@ -0,0 +1,45 @@
+//
+// AssigningInterpreterViewController.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+import SnapKit
+
+final class AssigningInterpreterViewController: BaseVC, AssigningInterpreterViewProtocol {
+
+ var presenter: AssigningInterpreterPresenterProtocol! {
+ didSet {
+ _presenter = presenter
+ }
+ }
+
+
+ // MARK: - Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ setupUI()
+ presenter.showed()
+ }
+
+
+ // MARK: - UI Setup
+
+ private func setupUI() {
+ }
+
+}
+
+extension AssigningInterpreterViewController: SetInjectable {
+ func inject(dependencies: AssigningInterpreterViewController.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: AssigningInterpreterPresenterProtocol
+ }
+}
diff --git a/Nynja/Modules/AssigningInterpreter/AssigningInterpreterWireFrame.swift b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterWireFrame.swift
new file mode 100644
index 0000000000000000000000000000000000000000..951c71b200bbecab3184da1633c94e3023c3f9bf
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/AssigningInterpreterWireFrame.swift
@@ -0,0 +1,35 @@
+//
+// AssigningInterpreterWireFrame.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+final class AssigningInterpreterWireFrame: AssigningInterpreterWireFrameProtocol {
+
+ weak var navigation: UINavigationController?
+
+ func presentAssigningInterpreter(navigation: UINavigationController) {
+ self.navigation = navigation
+
+ let view = AssigningInterpreterViewController()
+ let presenter = AssigningInterpreterPresenter()
+ let interactor = AssigningInterpreterInteractor()
+
+ // Connecting
+ let viewDependencies = AssigningInterpreterViewController.Dependencies(presenter: presenter)
+ view.inject(dependencies: viewDependencies)
+ let presenterDependencies = AssigningInterpreterPresenter.Dependencies(view: view, interactor: interactor, wireFrame: self)
+ presenter.inject(dependencies: presenterDependencies)
+ let interactorDependencies = AssigningInterpreterInteractor.Dependencies(presenter: presenter)
+ interactor.inject(dependencies: interactorDependencies)
+
+ view.modalTransitionStyle = .crossDissolve
+ view.modalPresentationStyle = .overCurrentContext
+ navigation.pushViewController(view as UIViewController, animated: true)
+ }
+
+}
diff --git a/Nynja/Modules/AssigningInterpreter/Interactor/AssigningInterpreterInteractor.swift b/Nynja/Modules/AssigningInterpreter/Interactor/AssigningInterpreterInteractor.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2f7d232476c403b7f02a20084c50803ea4b10116
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/Interactor/AssigningInterpreterInteractor.swift
@@ -0,0 +1,24 @@
+//
+// AssigningInterpreterInteractor.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class AssigningInterpreterInteractor: AssigningInterpreterInteractorInputProtocol {
+
+ weak var presenter: AssigningInterpreterInteractorOutputProtocol!
+}
+
+extension AssigningInterpreterInteractor: SetInjectable {
+ func inject(dependencies: AssigningInterpreterInteractor.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: AssigningInterpreterInteractorOutputProtocol
+ }
+}
diff --git a/Nynja/Modules/AssigningInterpreter/Presenter/AssigningInterpreterPresenter.swift b/Nynja/Modules/AssigningInterpreter/Presenter/AssigningInterpreterPresenter.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2d589e246847b41800e62505e9a7b036500c7482
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/Presenter/AssigningInterpreterPresenter.swift
@@ -0,0 +1,33 @@
+//
+// AssigningInterpreterPresenter.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class AssigningInterpreterPresenter: BasePresenter, AssigningInterpreterPresenterProtocol, AssigningInterpreterInteractorOutputProtocol {
+ weak var view: AssigningInterpreterViewProtocol!
+ var interactor: AssigningInterpreterInteractorInputProtocol!
+ var wireFrame: AssigningInterpreterWireFrameProtocol!
+
+ func dismiss() {
+ self.wireFrame.dismiss()
+ }
+}
+
+extension AssigningInterpreterPresenter: SetInjectable {
+ func inject(dependencies: AssigningInterpreterPresenter.Dependencies) {
+ self.view = dependencies.view
+ self.interactor = dependencies.interactor
+ self.wireFrame = dependencies.wireFrame
+ }
+
+ struct Dependencies {
+ let view: AssigningInterpreterViewProtocol
+ let interactor: AssigningInterpreterInteractorInputProtocol
+ let wireFrame: AssigningInterpreterWireFrameProtocol
+ }
+}
diff --git a/Nynja/Modules/AssigningInterpreter/View/AsigningInterpreterLayout.swift b/Nynja/Modules/AssigningInterpreter/View/AsigningInterpreterLayout.swift
new file mode 100644
index 0000000000000000000000000000000000000000..195b7817a2544ad71248fa55c3e82e2d325866d7
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/View/AsigningInterpreterLayout.swift
@@ -0,0 +1,33 @@
+//
+// AsigningInterpreterLayout.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 8/3/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+extension AssigningInterpreterViewController {
+
+ struct Constraints {
+ static let sidePadding = 32.adjustedByWidth
+ struct loadingView {
+ static let bottomPadding = 24.adjustedByWidth
+ }
+ }
+
+ struct Texts {
+ struct bottomLabel {
+ static let height = CGFloat(33.adjustedByWidth)
+ static let font = UIFont(fontName: Constants.fonts.medium, height: height)
+ static let color = Constants.colors.red.getColor()
+ }
+
+ struct descriptionLabel {
+ static let height = CGFloat(22.adjustedByWidth)
+ static let font = UIFont(fontName: Constants.fonts.medium, height: height)
+ static let color = Constants.colors.darkGray.getColor()
+ }
+ }
+}
diff --git a/Nynja/Modules/AssigningInterpreter/View/AssigningInterpreterViewController.swift b/Nynja/Modules/AssigningInterpreter/View/AssigningInterpreterViewController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..3ab8ed9644cd1b16a00d661d99749ea5af2c6358
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/View/AssigningInterpreterViewController.swift
@@ -0,0 +1,109 @@
+//
+// AssigningInterpreterViewController.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+import SnapKit
+
+final class AssigningInterpreterViewController: BaseVC, AssigningInterpreterViewProtocol {
+
+ var presenter: AssigningInterpreterPresenterProtocol! {
+ didSet {
+ _presenter = presenter
+ }
+ }
+
+ lazy var loadingView: CircleLoadingView = {
+
+ let lineWidth: CGFloat = 8
+
+ let view = CircleLoadingView(frame: .zero, color: Constants.colors.red.getColor(), lineWidth: lineWidth)
+ view.backgroundColor = .clear
+
+ self.view.addSubview(view)
+
+ view.snp.makeConstraints({ (make) in
+ make.width.height.equalTo(self.view.snp.width).dividedBy(3)
+ make.centerX.equalToSuperview()
+ make.bottom.equalTo(self.view.snp.centerY)
+ make.bottom.equalTo(self.descriptionLabel.snp.top).offset(-Constraints.loadingView.bottomPadding)
+ })
+
+ return view
+ }()
+
+ lazy var descriptionLabel: UILabel = {
+ let label = UILabel()
+ label.numberOfLines = 0
+ label.textAlignment = .center
+ label.text = "asigning_interpreter_description".localized
+ label.font = Texts.descriptionLabel.font
+ label.textColor = Texts.descriptionLabel.color
+
+ self.view.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalToSuperview().offset(-Constraints.sidePadding)
+ })
+ return label
+ }()
+
+ lazy var bottomLabel: UILabel = {
+ let label = UILabel()
+ label.text = "coming_soon".localized.uppercased()
+ label.font = Texts.bottomLabel.font
+ label.textColor = Texts.bottomLabel.color
+
+ self.view.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.bottom.equalToSuperview().offset(-Constraints.sidePadding)
+ make.centerX.equalToSuperview()
+ })
+ return label
+ }()
+
+
+ // MARK: - Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ setupUI()
+ }
+
+
+ // MARK: - UI Setup
+
+ private func setupUI() {
+ self.navigationView.configure(config: NavigationView.Config(
+ isVisibleSeparator: navigationView.isSeparatorVisible ?? false,
+ isVisibleBackButton: true,
+ title: title,
+ navigationHandler: self,
+ backButtonImage: UIImage(named:"ic_back_navigation")))
+ self.screenTitle = "assigning_interpreter".localized.uppercased()
+ self.loadingView.isHidden = false
+ self.bottomLabel.isHidden = false
+ }
+}
+
+extension AssigningInterpreterViewController: NavigationProtocol {
+ func back() {
+ self.presenter.dismiss()
+ }
+}
+
+extension AssigningInterpreterViewController: SetInjectable {
+ func inject(dependencies: AssigningInterpreterViewController.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: AssigningInterpreterPresenterProtocol
+ }
+}
diff --git a/Nynja/Modules/AssigningInterpreter/View/CircleLoadingView/CircleLoadingView.swift b/Nynja/Modules/AssigningInterpreter/View/CircleLoadingView/CircleLoadingView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..43c328950c990f107f34120dab397a7efa654d82
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/View/CircleLoadingView/CircleLoadingView.swift
@@ -0,0 +1,104 @@
+//
+// CircleLoadingView.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/29/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+class CircleLoadingView: UIView {
+
+ private var color: UIColor = .red
+ private var lineWidth: CGFloat = 0
+
+ private var leftView: CircleView!
+ private var centerView: CircleView!
+ private var rightView: CircleView!
+
+ //MARK: - Init
+
+ required init(frame: CGRect, color: UIColor, lineWidth: CGFloat) {
+ super.init(frame: frame)
+ self.color = color
+ self.lineWidth = lineWidth
+ self.backgroundColor = .clear
+ self.addNotifications()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ deinit {
+ NotificationCenter.default.removeObserver(self)
+ }
+
+ //MARK: - Override
+
+ override func layoutSubviews() {
+ super.layoutSubviews()
+
+ for subview in self.subviews {
+ subview.removeFromSuperview()
+ }
+
+ let rect = self.bounds
+ let outer = CircleView(frame: rect, color: self.color, lineWidth: self.lineWidth)
+ self.addSubview(outer)
+
+ let innerSide: CGFloat = 1.5 * self.lineWidth
+ let offset: CGFloat = 1.5 * innerSide
+
+ let dx = rect.width/2 - innerSide/2
+ let dy = rect.height/2 - innerSide/2
+
+ let centerRect = CGRect(x: dx, y: dy, width: innerSide, height: innerSide)
+ let center = CircleView(frame: centerRect, color: color)
+ self.centerView = center
+ self.addSubview(center)
+
+ let leftRect = CGRect(x: dx - offset, y: dy, width: innerSide, height: innerSide)
+ let left = CircleView(frame: leftRect, color: color)
+ self.leftView = left
+ self.addSubview(left)
+
+ let rightRect = CGRect(x: dx + offset, y: dy, width: innerSide, height: innerSide)
+ let right = CircleView(frame: rightRect, color: color)
+ self.rightView = right
+ self.addSubview(right)
+
+ self.setupAnimations()
+ }
+
+ //MARK: - Private methods
+
+ private func setupAnimations() {
+ let duration = 0.6
+ let delay = duration / 3
+
+ self.animateView(view: self.leftView, duration: duration, delay: 0)
+
+ self.animateView(view: self.centerView, duration: duration, delay: delay)
+
+ self.animateView(view: self.rightView, duration: duration, delay: delay * 2)
+ }
+
+ private func animateView(view: UIView, duration: Double, delay: Double){
+ UIView.animate(withDuration: duration, delay: delay, options: [.curveEaseInOut, .repeat, .autoreverse], animations: {
+ view.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
+ view.alpha = 0.3
+ })
+ }
+
+ private func addNotifications() {
+ NotificationCenter.default.addObserver(self, selector: #selector(self.applicationDidBecomeActive), name: Notification.Name.UIApplicationDidBecomeActive, object: nil)
+ }
+
+ @objc
+ private func applicationDidBecomeActive() {
+ self.setNeedsLayout()
+ self.layoutIfNeeded()
+ }
+}
diff --git a/Nynja/Modules/AssigningInterpreter/View/CircleLoadingView/CircleView.swift b/Nynja/Modules/AssigningInterpreter/View/CircleLoadingView/CircleView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..03ecd78ad3a968d191351fe89c2bd73bd1f1b6a1
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/View/CircleLoadingView/CircleView.swift
@@ -0,0 +1,44 @@
+//
+// CircleView.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/29/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+class CircleView: UIView {
+
+ private var color: UIColor = .red
+ private var lineWidth: CGFloat?
+
+ required init(frame: CGRect, color: UIColor, lineWidth: CGFloat? = nil) {
+ super.init(frame: frame)
+ self.color = color
+ self.lineWidth = lineWidth
+ self.backgroundColor = .clear
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func draw(_ rect: CGRect) {
+ let center = CGPoint(x: rect.width/2,y: rect.height/2)
+
+ let circlePath = UIBezierPath(arcCenter: center, radius: rect.width/2 - (self.lineWidth ?? 0), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
+
+ let shapeLayer = CAShapeLayer()
+ shapeLayer.path = circlePath.cgPath
+
+ if let lineWidth = lineWidth {
+ shapeLayer.fillColor = UIColor.clear.cgColor
+ shapeLayer.strokeColor = color.cgColor
+ shapeLayer.lineWidth = lineWidth
+ } else {
+ shapeLayer.fillColor = color.cgColor
+ }
+ layer.addSublayer(shapeLayer)
+ }
+}
diff --git a/Nynja/Modules/AssigningInterpreter/Wireframe/AssigningInterpreterWireFrame.swift b/Nynja/Modules/AssigningInterpreter/Wireframe/AssigningInterpreterWireFrame.swift
new file mode 100644
index 0000000000000000000000000000000000000000..7b826285989c96553abd4481d412450a08fa58c6
--- /dev/null
+++ b/Nynja/Modules/AssigningInterpreter/Wireframe/AssigningInterpreterWireFrame.swift
@@ -0,0 +1,39 @@
+//
+// AssigningInterpreterWireFrame.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+final class AssigningInterpreterWireFrame: AssigningInterpreterWireFrameProtocol {
+
+ weak var navigation: UINavigationController?
+
+ func presentAssigningInterpreter(navigation: UINavigationController) {
+ self.navigation = navigation
+
+ let view = AssigningInterpreterViewController()
+ let presenter = AssigningInterpreterPresenter()
+ let interactor = AssigningInterpreterInteractor()
+
+ // Connecting
+ let viewDependencies = AssigningInterpreterViewController.Dependencies.init(presenter: presenter)
+ view.inject(dependencies: viewDependencies)
+ let presenterDependencies = AssigningInterpreterPresenter.Dependencies.init(view: view, interactor: interactor, wireFrame: self)
+ presenter.inject(dependencies: presenterDependencies)
+ let interactorDependencies = AssigningInterpreterInteractor.Dependencies.init(presenter: presenter)
+ interactor.inject(dependencies: interactorDependencies)
+
+ view.modalTransitionStyle = .crossDissolve
+ view.modalPresentationStyle = .overCurrentContext
+ navigation.pushViewController(view as UIViewController, animated: true)
+ }
+
+ func dismiss() {
+ self.navigation?.popViewController(animated: true)
+ }
+
+}
diff --git a/Nynja/Modules/Call/CallInProgressProtocols.swift b/Nynja/Modules/Call/CallInProgressProtocols.swift
index a3e92e39ce5d269143c10e41b2d4b1182925191e..28a07f4623858627335ef45f959b0a88cafb5f20 100644
--- a/Nynja/Modules/Call/CallInProgressProtocols.swift
+++ b/Nynja/Modules/Call/CallInProgressProtocols.swift
@@ -7,11 +7,10 @@
//
import UIKit
-import VoxImplant
protocol CallInProgressWireFrameProtocol: class {
- func presentCall(navigation: UINavigationController, callInProgressMode: CallInProgressMode, contact: Contact, call: VICall?, main: MainWireFrame?)
+ func presentCall(navigation: UINavigationController, callInProgressMode: CallInProgressMode, contact: Contact, main: MainWireFrame?)
/**
* Add here your methods for communication PRESENTER -> WIREFRAME
@@ -56,7 +55,6 @@ protocol CallInProgressPresenterProtocol: class {
*/
func willShow()
- func setupViews(myView: UIView, remoteView: UIView)
func acceptCall(withVideo: Bool)
func declineCall()
@@ -67,7 +65,6 @@ protocol CallInProgressPresenterProtocol: class {
func toggleMicrophone()
func isMuted()->Bool
func switchCamera()
- func disableVideo()
func viewShowed()
func rejectCall()
func updateCallParticipants()
@@ -102,8 +99,6 @@ protocol CallInProgressInteractorOutputProtocol: class {
protocol CallInProgressInteractorInputProtocol: class {
var presenter: CallInProgressInteractorOutputProtocol! { get set }
- var call: VICall? { get set }
- var contact: Contact? { get }
var room: Room? { get }
/**
@@ -117,9 +112,6 @@ protocol CallInProgressInteractorInputProtocol: class {
func toggleMicrophone()
func isMuted()->Bool
func switchCamera()
- func setupViews(myView: UIView, remoteView: UIView)
- func disableVideo()
- func setupDelegate()
func rejectCall()
func updateGroupCall(contacts: [Contact])
func removeCallMember(memberId: String)
diff --git a/Nynja/Modules/Call/CallScreens/CallInProgress/Interactor/CallInProgressInteractor.swift b/Nynja/Modules/Call/CallScreens/CallInProgress/Interactor/CallInProgressInteractor.swift
index f3a59004d2112ef44b20b04dd75e65325bb17a93..31ec6174bfe454a77382660e4742f6837fe6b8df 100644
--- a/Nynja/Modules/Call/CallScreens/CallInProgress/Interactor/CallInProgressInteractor.swift
+++ b/Nynja/Modules/Call/CallScreens/CallInProgress/Interactor/CallInProgressInteractor.swift
@@ -6,22 +6,18 @@
// Copyright © 2018 TecSynt Solutions. All rights reserved.
//
-import VoxImplant
-class CallInProgressInteractor: CallInProgressInteractorInputProtocol, VoxServiceDelegate, NynjaCallDelegate {
+class CallInProgressInteractor: CallInProgressInteractorInputProtocol, NynjaCallDelegate {
weak var presenter: CallInProgressInteractorOutputProtocol!
- weak var call: VICall?
- var vox = VoxService.sharedInstance
var callService = NynjaCommunicatorService.sharedInstance
var timer: Timer?
-
- var contact: Contact? {
- guard let voxId = call?.voxId else { return nil }
- return ContactDAO.findContactBy(voxId: voxId)
- }
+ init() {
+ callService.callDelegate = self
+ }
+
var room: Room? {
guard let roomId = nynCall?.externalInfo else { return nil }
return RoomDAO.findRoom(by: roomId)
@@ -58,18 +54,10 @@ class CallInProgressInteractor: CallInProgressInteractorInputProtocol, VoxServic
return service
}()
-
- func setupDelegate() {
- vox.delegate = self
- callService.callDelegate = self
- }
func acceptCall(withVideo: Bool) {
- if let id = call {
- vox.answer(call: id, withVideo: withVideo)
- } else if let ncall = self.nynCall {
- callService.acceptConference(call: ncall)
- }
+ guard let ncall = self.nynCall else { return }
+ callService.acceptConference(call: ncall)
}
func rejectCall() {
@@ -135,36 +123,17 @@ class CallInProgressInteractor: CallInProgressInteractorInputProtocol, VoxServic
}
}
- func disableVideo() {
- if let id = call {
- vox.disableVideo(call: id)
- if id.duration() > 0 {
- self.presenter.callConnected(withVideo: false)
- } else {
- self.presenter.setRingingWithoutVideo()
- }
- vox.withVideo = false
- }
- }
func startRinging() {
presenter.setRingingStatus()
}
func microphoneAction() {
- if let id = call {
- vox.switchMicrophone(call: id)
- }
- if let nc = self.nynCall {
- nc.toggleMicrophone()
- }
+ nynCall?.toggleMicrophone()
}
func toggleMicrophone() {
-
- if let nc = self.nynCall {
- nc.toggleMicrophone()
- }
+ nynCall?.toggleMicrophone()
}
func isMuted()->Bool {
@@ -187,45 +156,20 @@ class CallInProgressInteractor: CallInProgressInteractorInputProtocol, VoxServic
presenter.remoteVideoStreamStopped()
}
- func callClosed(call: VICall, isError: Bool) {
- self.presenter.callClosed()
- }
-
- func callConnected(call: VICall,withVideo: Bool) {
- self.presenter.callConnected(withVideo: withVideo)
- self.startTimer()
- }
-
- func setupViews(myView: UIView, remoteView: UIView) {
- vox.remoteView = remoteView
- vox.myView = myView
- }
-
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimedCode), userInfo: nil, repeats: true)
}
@objc func runTimedCode() {
- if let call = self.call {
- let durationInt = Int(call.duration())
- let minutes = durationInt / 60
- let seconds = durationInt % 60
-
- let text = String.localizedStringWithFormat("%u:%02u", minutes,seconds)
- if self.presenter != nil {
- self.presenter.updateTime(text:text)
- }
- } else if nil != self.nynCall {
-
- duration += 1
- let durationInt = Int(duration)
- let minutes = durationInt / 60
- let seconds = durationInt % 60
-
- let text = String.localizedStringWithFormat("%u:%02u", minutes,seconds)
- if self.presenter != nil {
- self.presenter.updateTime(text:text)
- }
+ guard self.nynCall != nil else { return }
+ duration += 1
+ let durationInt = Int(duration)
+ let minutes = durationInt / 60
+ let seconds = durationInt % 60
+
+ let text = String.localizedStringWithFormat("%u:%02u", minutes,seconds)
+ if self.presenter != nil {
+ self.presenter.updateTime(text:text)
}
}
diff --git a/Nynja/Modules/Call/CallScreens/CallInProgress/Presenter/CallInProgressPresenter.swift b/Nynja/Modules/Call/CallScreens/CallInProgress/Presenter/CallInProgressPresenter.swift
index a0ff75729a7c30449ecd2951178f68f3a91916e6..8f9b6b1d1fa3891aa39b7bfd2e7135f7ffa2f6f5 100644
--- a/Nynja/Modules/Call/CallScreens/CallInProgress/Presenter/CallInProgressPresenter.swift
+++ b/Nynja/Modules/Call/CallScreens/CallInProgress/Presenter/CallInProgressPresenter.swift
@@ -21,9 +21,6 @@ class CallInProgressPresenter: CallInProgressPresenterProtocol, CallInProgressIn
interactor.acceptCall(withVideo: withVideo)
}
- func disableVideo() {
- interactor.disableVideo()
- }
func declineCall() {
interactor.declineCall()
@@ -150,9 +147,6 @@ class CallInProgressPresenter: CallInProgressPresenterProtocol, CallInProgressIn
self.view.setupUI()
}
- func setupViews(myView: UIView, remoteView: UIView) {
- self.interactor.setupViews(myView: myView, remoteView: remoteView)
- }
func updateTime(text: String) {
self.view.updateTime(text: text)
diff --git a/Nynja/Modules/Call/CallScreens/CallInProgress/WireFrame/CallInProgressWireframe.swift b/Nynja/Modules/Call/CallScreens/CallInProgress/WireFrame/CallInProgressWireframe.swift
index 53c92b659014f6f527b1b19b12f71b146a5522f8..d1303d33bdf9abcd267c17691eb0c05baa722e8b 100644
--- a/Nynja/Modules/Call/CallScreens/CallInProgress/WireFrame/CallInProgressWireframe.swift
+++ b/Nynja/Modules/Call/CallScreens/CallInProgress/WireFrame/CallInProgressWireframe.swift
@@ -7,18 +7,16 @@
//
import UIKit
-import VoxImplant
class CallInProgressWireframe: CallInProgressWireFrameProtocol {
weak var navigation : UINavigationController?
weak var mainWF: MainWireFrame?
- weak var call: VICall?
weak var view: CallInProgressViewProtocol!
weak var nynCall: NYNCall?
weak var external: EditParticipantsDelegate?
- func presentCall(navigation: UINavigationController, callInProgressMode: CallInProgressMode, contact: Contact, call: VICall?, main: MainWireFrame?) {
+ func presentCall(navigation: UINavigationController, callInProgressMode: CallInProgressMode, contact: Contact, main: MainWireFrame?) {
self.navigation = navigation
self.mainWF = main
@@ -26,9 +24,6 @@ class CallInProgressWireframe: CallInProgressWireFrameProtocol {
let view = CallInProgressViewController()
let presenter = CallInProgressPresenter()
let interactor = CallInProgressInteractor()
-
- interactor.call = call
-
view.callInProgressMode = callInProgressMode
self.view = view
@@ -45,7 +40,6 @@ class CallInProgressWireframe: CallInProgressWireFrameProtocol {
interactor.presenter = presenter
navigation.pushViewController(view as UIViewController, animated: true)
- interactor.setupDelegate()
}
func presentDialInCall(navigation: UINavigationController, callInProgressMode: CallInProgressMode, contact: Contact?, call: NYNCall? = nil, main: MainWireFrame?) {
@@ -72,7 +66,6 @@ class CallInProgressWireframe: CallInProgressWireFrameProtocol {
interactor.presenter = presenter
navigation.pushViewController(view as UIViewController, animated: true)
- interactor.setupDelegate()
}
func presentCreateGroupCall(navigation: UINavigationController, callInProgressMode: CallInProgressMode, main: MainWireFrame?, call: NYNCall) {
@@ -102,7 +95,6 @@ class CallInProgressWireframe: CallInProgressWireFrameProtocol {
interactor.presenter = presenter
navigation.pushViewController(view as UIViewController, animated: true)
- interactor.setupDelegate()
}
func messageActionWith(room: Room, isVideo: Bool) {
diff --git a/Nynja/Modules/Channel/NewChannel/Interactor/NewChannelInteractor.swift b/Nynja/Modules/Channel/NewChannel/Interactor/NewChannelInteractor.swift
index 55f7c177e2952ce202d00318f1486e5122aa5195..a0d5d7a8859b69428c25e7e9fe176cb77aa89c67 100644
--- a/Nynja/Modules/Channel/NewChannel/Interactor/NewChannelInteractor.swift
+++ b/Nynja/Modules/Channel/NewChannel/Interactor/NewChannelInteractor.swift
@@ -18,6 +18,7 @@ final class NewChannelInteractor: BaseInteractor, NewChannelInteractorInputProto
weak var presenter: NewChannelInteractorOutputProtocol!
+ private var createChannelAction: (() -> Void)?
// MARK: - Dependencies
@@ -105,7 +106,12 @@ final class NewChannelInteractor: BaseInteractor, NewChannelInteractorInputProto
private func sendCreateChannel(avatarUrl: URL?, info: CreateChannelInfo) {
let room = makeRoom(with: avatarUrl, info: info)
- mqttService.createRoom(room, kind: .channel)
+ if mqttService.isConnectedSuccess {
+ mqttService.createRoom(room, kind: .channel)
+ }
+ createChannelAction = { [weak self] in
+ self?.mqttService.createRoom(room, kind: .channel)
+ }
}
private func makeRoom(with avatarUrl: URL?, info: CreateChannelInfo) -> Room {
@@ -139,15 +145,16 @@ final class NewChannelInteractor: BaseInteractor, NewChannelInteractorInputProto
// MARK: - StorageSubscriber
override func update(with changes: [StorageChange], type: SubscribeType) {
- if case .room = type,
+ guard case .room = type,
let change = changes.first,
change.kind == .insert,
let dbRoom = change.entity as? DBRoom,
- dbRoom.id == localId {
-
- let room = Room(room: dbRoom)
- presenter?.channelCreated(room)
+ dbRoom.id == localId else {
+ return
}
+ createChannelAction = nil
+ let room = Room(room: dbRoom)
+ presenter?.channelCreated(room)
}
}
@@ -173,7 +180,6 @@ extension NewChannelInteractor {
private func isAbleToHandleLink(_ link: Link) -> Bool {
return link.room_id == localId
}
-
}
@@ -182,9 +188,18 @@ extension NewChannelInteractor {
extension NewChannelInteractor {
func didConnect(_ mqttService: MQTTService) {
- presenter.didConnectToServer()
+ presenter?.didConnectToServer()
+
+ guard let action = createChannelAction else {
+ return
+ }
+ presenter?.showHUD()
+ action()
}
+ func didDisconnect(_ mqttService: MQTTService) {
+ presenter?.hideHUD()
+ }
}
diff --git a/Nynja/Modules/Channel/NewChannel/NewChannelProtocols.swift b/Nynja/Modules/Channel/NewChannel/NewChannelProtocols.swift
index f722ad10d06f31910c050449e976e62f4a5d8eb1..81fe468023334be70192c87a757d1750f891e2df 100644
--- a/Nynja/Modules/Channel/NewChannel/NewChannelProtocols.swift
+++ b/Nynja/Modules/Channel/NewChannel/NewChannelProtocols.swift
@@ -82,6 +82,8 @@ protocol NewChannelInteractorOutputProtocol: class {
func checkedLinkIsAvailable(_ link: String)
func somethingWrongWithLink(_ link: String, code: StatusCode)
+ func showHUD()
+ func hideHUD()
func didConnectToServer()
}
diff --git a/Nynja/Modules/Channel/NewChannel/Presenter/NewChannelPresenter.swift b/Nynja/Modules/Channel/NewChannel/Presenter/NewChannelPresenter.swift
index 4b5bfe6aad3cd1f9ab5b4e79081565af2d3abd11..04dc2f264385b4894d5bb750e2d4b7dfa291ad3d 100644
--- a/Nynja/Modules/Channel/NewChannel/Presenter/NewChannelPresenter.swift
+++ b/Nynja/Modules/Channel/NewChannel/Presenter/NewChannelPresenter.swift
@@ -232,6 +232,14 @@ final class NewChannelPresenter: BasePresenter, NewChannelPresenterProtocol, New
}
}
+ func showHUD() {
+ view.shouldShowSpinner = true
+ }
+
+ func hideHUD() {
+ view.shouldShowSpinner = false
+ }
+
func didConnectToServer() {
if shouldShowGeneratedLink {
interactor.generateLink()
diff --git a/Nynja/Modules/Channel/SubscribersSelector/View/SubscribersSelectorViewController.swift b/Nynja/Modules/Channel/SubscribersSelector/View/SubscribersSelectorViewController.swift
index 3922e10072f3bea9eb0a3db777e9fe8bdb79cbe1..4d5c17aa34faed4a8cf7c61c19668dee66cb1d43 100644
--- a/Nynja/Modules/Channel/SubscribersSelector/View/SubscribersSelectorViewController.swift
+++ b/Nynja/Modules/Channel/SubscribersSelector/View/SubscribersSelectorViewController.swift
@@ -67,14 +67,10 @@ final class SubscribersSelectorViewController: BaseVC, SubscribersSelectorViewPr
}()
private lazy var separator: UIView = {
- let separator = UIView()
-
- separator.backgroundColor = Constants.colors.separatorGrayColor.getColor()
+ let separator = SeparatorView()
self.view.addSubview(separator)
separator.snp.makeConstraints { make in
- make.height.equalTo(Constraints.Separator.height)
-
make.top.equalTo(avatarsView.snp.bottom).offset(Constraints.Separator.topInset)
make.left.right.equalToSuperview()
}
@@ -391,7 +387,6 @@ extension SubscribersSelectorViewController {
}
enum Separator {
- static let height = 1.0
static let topInset = 8.0.adjustedByWidth
}
diff --git a/Nynja/Modules/CreateGroup/CreateGroupProtocols.swift b/Nynja/Modules/CreateGroup/CreateGroupProtocols.swift
index 7694d0428359fbf4b8952c3926c9cf006ac18197..0ce0d54b621b4ffd4a95f0dedbedd54d1a731f92 100644
--- a/Nynja/Modules/CreateGroup/CreateGroupProtocols.swift
+++ b/Nynja/Modules/CreateGroup/CreateGroupProtocols.swift
@@ -59,6 +59,8 @@ protocol CreateGroupInteractorOutputProtocol: class {
/**
* Add here your methods for communication INTERACTOR -> PRESENTER
*/
+ func showHUD()
+ func hideHUD()
func created(room: Room)
}
diff --git a/Nynja/Modules/CreateGroup/Interactor/CreateGroupInteractor.swift b/Nynja/Modules/CreateGroup/Interactor/CreateGroupInteractor.swift
index 4631716e730d9e1c1cb4035771d5cdfdecc28fd5..ef479e969f9a5f65bbb5db298a385e4291a23702 100644
--- a/Nynja/Modules/CreateGroup/Interactor/CreateGroupInteractor.swift
+++ b/Nynja/Modules/CreateGroup/Interactor/CreateGroupInteractor.swift
@@ -17,25 +17,44 @@ class CreateGroupInteractor: BaseInteractor, CreateGroupInteractorInputProtocol
return ContactDAO.currentContact
}
+ private let mqttService = MQTTService.sharedInstance
+
private var avatarUrl: URL?
private let localId = IdBuilder(format: .defaultId).build()
+ private var sendRoomAction: (() -> Void)?
+
+ override init() {
+ super.init()
+ mqttService.addSubscriber(self)
+ }
+
+ deinit {
+ mqttService.removeSubscriber(self)
+ }
func createRoom(name: String, avatar: UIImage?, members: [Member]) {
- if let url = self.avatarUrl {
- let sync = SyncFileManager.sharedInstance
- sync.downloader = AmazonManager.shared
- SyncFileManager.sharedInstance.saveExternalFileLink(localUrl: url.path) { (ext, progress, request) in
- if let extUrl = ext {
- self.sendRoom(with: name, avatar: String(describing: extUrl), members: members)
- }
- }
- } else {
+ guard let url = self.avatarUrl else {
sendRoom(with: name, avatar: nil, members: members)
+ return
+ }
+ let sync = SyncFileManager.sharedInstance
+ sync.downloader = AmazonManager.shared
+ SyncFileManager.sharedInstance.saveExternalFileLink(localUrl: url.path) { (ext, progress, request) in
+ guard let extUrl = ext else {
+ return
+ }
+ self.sendRoom(with: name, avatar: String(describing: extUrl), members: members)
}
}
func sendRoom(with name: String, avatar: String?, members: [Member]) {
- MQTTService.sharedInstance.createRoom(id: localId, name: name, avatar: avatar, members: members)
+ if mqttService.isConnectedSuccess {
+ mqttService.createRoom(id: localId, name: name, avatar: avatar, members: members)
+ }
+ let roomId = localId
+ sendRoomAction = { [weak self] in
+ self?.mqttService.createRoom(id: roomId, name: name, avatar: avatar, members: members)
+ }
}
func avatarChanged(with url: URL) {
@@ -44,11 +63,26 @@ class CreateGroupInteractor: BaseInteractor, CreateGroupInteractorInputProtocol
// MARK: - StorageSubscriber
override func update(with changes: [StorageChange], type: SubscribeType) {
- if case .room = type, let dbRoom = changes.first?.entity as? DBRoom, dbRoom.id == localId {
- let room = Room(room: dbRoom)
- presenter?.created(room: room)
+ guard case .room = type, let dbRoom = changes.first?.entity as? DBRoom, dbRoom.id == localId else {
+ return
}
+ sendRoomAction = nil
+ let room = Room(room: dbRoom)
+ presenter?.created(room: room)
}
-
}
+extension CreateGroupInteractor: MQTTServiceDelegate {
+
+ func didConnect(_ mqttService: MQTTService) {
+ guard let action = sendRoomAction else {
+ return
+ }
+ presenter?.showHUD()
+ action()
+ }
+
+ func didDisconnect(_ mqttService: MQTTService) {
+ presenter?.hideHUD()
+ }
+}
diff --git a/Nynja/Modules/CreateGroup/Presenter/CreateGroupPresenter.swift b/Nynja/Modules/CreateGroup/Presenter/CreateGroupPresenter.swift
index 8fbc78735037601635cc9d80cee7abd7620e1838..2105d1f1c3b271f99192244ae587cb9ea3b738fd 100644
--- a/Nynja/Modules/CreateGroup/Presenter/CreateGroupPresenter.swift
+++ b/Nynja/Modules/CreateGroup/Presenter/CreateGroupPresenter.swift
@@ -55,11 +55,11 @@ class CreateGroupPresenter: BasePresenter, CreateGroupPresenterProtocol, CreateG
return
}
if room.name != "" {
- (wireFrame as? CreateGroupWireFrame)?.mainWireFrame?.view?.showSpinner()
+ showHUD()
interactor.createRoom(name: room.name!, avatar: avatar, members: room.members!)
disableAction = true
} else {
- (wireFrame as? CreateGroupWireFrame)?.mainWireFrame?.view?.hideSpinner()
+ hideHUD()
AlertManager.sharedInstance.showAlertOk(message: "Group_Name_Empty_Message".localized)
}
}
@@ -113,11 +113,21 @@ class CreateGroupPresenter: BasePresenter, CreateGroupPresenterProtocol, CreateG
self.view.setup(room: room, avatar: avatar, myAlias: myAlias, badgeNumber: contacts.count)
}
+ //MARK: CreateGroupInteractorOutputProtocol
+
func created(room: Room) {
- (wireFrame as? CreateGroupWireFrame)?.mainWireFrame?.view?.hideSpinner()
+ hideHUD()
wireFrame.showGroupChat(room: room)
}
+ func showHUD() {
+ (wireFrame as? CreateGroupWireFrame)?.mainWireFrame?.view?.showSpinner()
+ }
+
+ func hideHUD() {
+ (wireFrame as? CreateGroupWireFrame)?.mainWireFrame?.view?.hideSpinner()
+ }
+
// MARK: - EditGroupPhotoDelegate
func photoSavedAtUrl(url: URL) {
if let data = try? Data(contentsOf: url) {
diff --git a/Nynja/Modules/EditGroupName/EditGroupNameProtocols.swift b/Nynja/Modules/EditGroupName/EditGroupNameProtocols.swift
index f171dbfac4e4994176e5a6ad2898b404ba0b869f..3cf57dfd3fbd024b20261f608932d69bb5f42cdb 100644
--- a/Nynja/Modules/EditGroupName/EditGroupNameProtocols.swift
+++ b/Nynja/Modules/EditGroupName/EditGroupNameProtocols.swift
@@ -19,6 +19,7 @@ protocol EditGroupNameWireFrameProtocol: class {
/**
* Add here your methods for communication PRESENTER -> WIREFRAME
*/
+ func dismiss()
}
protocol EditGroupNameViewProtocol: class {
@@ -30,11 +31,10 @@ protocol EditGroupNameViewProtocol: class {
*/
func setup(groupName: String)
+ func hideKeyboard()
}
-protocol EditGroupNamePresenterProtocol: AnyObject, BasePresenterProtocol {
-
- var mode: GroupMode! { get set }
+protocol EditGroupNamePresenterProtocol: BasePresenterProtocol {
var view: EditGroupNameViewProtocol! { get set }
var interactor: EditGroupNameInteractorInputProtocol! { get set }
@@ -45,7 +45,7 @@ protocol EditGroupNamePresenterProtocol: AnyObject, BasePresenterProtocol {
*/
func showed()
- func saveGroupName(_ name:String)
+ func handleSaveTap(for name: String?)
}
protocol EditGroupNameInteractorOutputProtocol: class {
@@ -53,15 +53,18 @@ protocol EditGroupNameInteractorOutputProtocol: class {
/**
* Add here your methods for communication INTERACTOR -> PRESENTER
*/
+ func nameSaved()
+ func showAlert(with message: String)
}
protocol EditGroupNameInteractorInputProtocol: class {
var presenter: EditGroupNameInteractorOutputProtocol! { get set }
-
+ var oldName: String! { get }
/**
* Add here your methods for communication PRESENTER -> INTERACTOR
*/
func notifyDelegate(newName: String)
+ func validate(_ name: String?)
}
diff --git a/Nynja/Modules/EditGroupName/Interactor/EditGroupNameInteractor.swift b/Nynja/Modules/EditGroupName/Interactor/EditGroupNameInteractor.swift
index 73d1b68c5162c9187a9b9d2d064a452e0e924b02..d44e5b541fc24c08f6c5e49a60d6c5dfd45a554f 100644
--- a/Nynja/Modules/EditGroupName/Interactor/EditGroupNameInteractor.swift
+++ b/Nynja/Modules/EditGroupName/Interactor/EditGroupNameInteractor.swift
@@ -7,12 +7,43 @@
//
class EditGroupNameInteractor: EditGroupNameInteractorInputProtocol {
-
+
weak var presenter: EditGroupNameInteractorOutputProtocol!
+ private var validationService: TextInputValidationServiceProtocol!
+ private weak var delegate: GroupNameEditorDelegate?
+ var oldName:String!
- weak var delegate: GroupNameEditorDelegate?
+ required init(withName oldName: String, delegate: GroupNameEditorDelegate?) {
+ self.oldName = oldName
+ self.delegate = delegate
+ }
func notifyDelegate(newName: String) {
- delegate?.groupNameWasChanged(newValue: newName)
+ self.delegate?.groupNameWasChanged(newValue: newName)
+ }
+
+ func validate(_ name: String?) {
+ let groupName = name?.trimmingCharacters(in: .whitespaces)
+ do {
+ let groupName = try self.validationService.validateGroupName(groupName)
+ if self.oldName != groupName {
+ self.notifyDelegate(newName: groupName)
+ }
+ self.presenter.nameSaved()
+ } catch {
+ self.presenter.showAlert(with: error.localizedDescription)
+ }
+ }
+}
+
+extension EditGroupNameInteractor: SetInjectable {
+ func inject(dependencies: EditGroupNameInteractor.Dependencies) {
+ self.presenter = dependencies.presenter
+ self.validationService = dependencies.validationService
+ }
+
+ struct Dependencies {
+ let presenter: EditGroupNameInteractorOutputProtocol
+ let validationService: TextInputValidationServiceProtocol
}
}
diff --git a/Nynja/Modules/EditGroupName/Presenter/EditGroupNamePresenter.swift b/Nynja/Modules/EditGroupName/Presenter/EditGroupNamePresenter.swift
index ef2f0518f45f594b247cb5333e32b96b4eca5b7d..2f76db340e5b2a30d36d234125eac9d5146cdb52 100644
--- a/Nynja/Modules/EditGroupName/Presenter/EditGroupNamePresenter.swift
+++ b/Nynja/Modules/EditGroupName/Presenter/EditGroupNamePresenter.swift
@@ -16,22 +16,44 @@ class EditGroupNamePresenter: BasePresenter, EditGroupNamePresenterProtocol, Edi
}
}
- var mode: GroupMode!
+ private var mode: GroupMode!
+
+ required init(with mode: GroupMode) {
+ self.mode = mode
+ }
weak var view: EditGroupNameViewProtocol!
var interactor: EditGroupNameInteractorInputProtocol!
var wireFrame: EditGroupNameWireFrameProtocol!
-
- var oldName:String!
func showed() {
- view.setup(groupName: oldName)
+ view.setup(groupName: self.interactor.oldName)
}
- func saveGroupName(_ name: String) {
- if oldName != name {
- interactor.notifyDelegate(newName: name)
- }
- (view as? UIViewController)?.navigationController?.popViewController(animated: false)
+ func handleSaveTap(for name: String?) {
+ self.interactor.validate(name)
+ }
+
+ func showAlert(with message: String) {
+ AlertManager.sharedInstance.showAlertOk(message: message)
+ }
+
+ func nameSaved() {
+ self.wireFrame.dismiss()
+ }
+
+}
+
+extension EditGroupNamePresenter: SetInjectable {
+ func inject(dependencies: EditGroupNamePresenter.Dependencies) {
+ self.view = dependencies.view
+ self.interactor = dependencies.interactor
+ self.wireFrame = dependencies.wireFrame
+ }
+
+ struct Dependencies {
+ let view: EditGroupNameViewProtocol
+ let interactor: EditGroupNameInteractorInputProtocol
+ let wireFrame: EditGroupNameWireFrameProtocol
}
}
diff --git a/Nynja/Modules/EditGroupName/View/EditGroupNameViewController.swift b/Nynja/Modules/EditGroupName/View/EditGroupNameViewController.swift
index 857b8fd40daffb0467a794832544d91d24e69ace..bd8b9096ccb551080c726555b172f63a4756a836 100644
--- a/Nynja/Modules/EditGroupName/View/EditGroupNameViewController.swift
+++ b/Nynja/Modules/EditGroupName/View/EditGroupNameViewController.swift
@@ -117,21 +117,11 @@ class EditGroupNameViewController: BaseVC, EditGroupNameViewProtocol {
}
@objc func saveTapped() {
- let name = (nameField.input.text ?? "")
- if valid(groupName: name.trimmingCharacters(in: .whitespaces)) {
- self.view.endEditing(true)
- self.presenter.saveGroupName(name)
- }
+ self.presenter.handleSaveTap(for: nameField.input.text)
}
- // MARK: Utils
- private func valid(groupName name: String) -> Bool {
- if name.count < 2 {
- AlertManager.sharedInstance.showAlertOk(message: Strings.groupNameEmptyMessage.localized)
- return false
- }
-
- return true
+ func hideKeyboard() {
+ self.view.endEditing(true)
}
// MARK: Private
@@ -202,6 +192,16 @@ class EditGroupNameViewController: BaseVC, EditGroupNameViewProtocol {
}
}
+extension EditGroupNameViewController: SetInjectable {
+ func inject(dependencies: EditGroupNameViewController.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: EditGroupNamePresenterProtocol
+ }
+}
+
// MARK: - Testable
extension EditGroupNameViewController: TestableViewControllerProtocol {
diff --git a/Nynja/Modules/EditGroupName/WireFrame/EditGroupNameWireframe.swift b/Nynja/Modules/EditGroupName/WireFrame/EditGroupNameWireframe.swift
index cb64a69f387974a3ff4845e18edeeb1443b68dd0..b8739c1e74fb0eb0ffda943afe024506c3eb6039 100644
--- a/Nynja/Modules/EditGroupName/WireFrame/EditGroupNameWireframe.swift
+++ b/Nynja/Modules/EditGroupName/WireFrame/EditGroupNameWireframe.swift
@@ -13,23 +13,29 @@ class EditGroupNameWireFrame: EditGroupNameWireFrameProtocol {
weak var navigation : UINavigationController?
func presentEditGroupName(navigation: UINavigationController, currentName:String, delegate:GroupNameEditorDelegate?, mode: GroupMode) {
- let view = EditGroupNameViewController()
- let presenter = EditGroupNamePresenter()
- let interactor = EditGroupNameInteractor()
self.navigation = navigation
- presenter.oldName = currentName
- interactor.delegate = delegate
- // Connecting
- view.presenter = presenter
- presenter.mode = mode
- presenter.view = view
- presenter.wireFrame = self
- presenter.interactor = interactor
- interactor.presenter = presenter
+ let view = EditGroupNameViewController()
+ let presenter = EditGroupNamePresenter(with: mode)
+ let interactor = EditGroupNameInteractor(withName: currentName, delegate: delegate)
+
+ let presenterDependencies = EditGroupNamePresenter.Dependencies(view: view, interactor: interactor, wireFrame: self)
+ presenter.inject(dependencies: presenterDependencies)
- navigation.pushViewController(view as UIViewController, animated: true)
+ let viewDependencies = EditGroupNameViewController.Dependencies(presenter: presenter)
+ view.inject(dependencies: viewDependencies)
+ let serviceFactory = ServiceFactory()
+ let validationService = serviceFactory.makeTextInputValidationService()
+
+ let interactorDependencies = EditGroupNameInteractor.Dependencies(presenter: presenter, validationService: validationService)
+ interactor.inject(dependencies: interactorDependencies)
+
+ navigation.pushViewController(view as UIViewController, animated: true)
+ }
+
+ func dismiss() {
+ self.navigation?.popViewController(animated: false)
}
}
diff --git a/Nynja/Modules/Interpretation/Interactor/InterpretationInteractor.swift b/Nynja/Modules/Interpretation/Interactor/InterpretationInteractor.swift
new file mode 100644
index 0000000000000000000000000000000000000000..a660e82cd76564f4965c2f6330923155f85adaae
--- /dev/null
+++ b/Nynja/Modules/Interpretation/Interactor/InterpretationInteractor.swift
@@ -0,0 +1,24 @@
+//
+// InterpretationInteractor.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class InterpretationInteractor: InterpretationInteractorInputProtocol {
+
+ weak var presenter: InterpretationInteractorOutputProtocol!
+}
+
+extension InterpretationInteractor: SetInjectable {
+ func inject(dependencies: InterpretationInteractor.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: InterpretationInteractorOutputProtocol
+ }
+}
diff --git a/Nynja/Modules/Interpretation/InterpretationModel.swift b/Nynja/Modules/Interpretation/InterpretationModel.swift
new file mode 100644
index 0000000000000000000000000000000000000000..7554b732adb00bbd0bb1f48d5046bfbd2e919f76
--- /dev/null
+++ b/Nynja/Modules/Interpretation/InterpretationModel.swift
@@ -0,0 +1,21 @@
+//
+// InterpretationModel.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/28/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+class InterpretationModel {
+ var type: InterpretationType = .general
+ var fromLang: Language = Language.empty_default
+ var toLang: Language = Language.current
+ var time: Int = 30
+ var price: NYNMoney {
+ let result = Double(time) * NSDecimalNumber(decimal: type.price.amount).doubleValue
+ let decimalResult = Decimal(result)
+ return NYNMoney(decimalResult)
+ }
+}
diff --git a/Nynja/Modules/Interpretation/InterpretationProtocols.swift b/Nynja/Modules/Interpretation/InterpretationProtocols.swift
new file mode 100644
index 0000000000000000000000000000000000000000..c813bfa884ed49d65ed5385f6beceb171e0ebddd
--- /dev/null
+++ b/Nynja/Modules/Interpretation/InterpretationProtocols.swift
@@ -0,0 +1,66 @@
+//
+// InterpretationProtocols.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+protocol InterpretationWireFrameProtocol: class {
+
+ func presentInterpretation(navigation: UINavigationController)
+
+ /**
+ * Add here your methods for communication PRESENTER -> WIREFRAME
+ */
+ func openInterpretationType(delegate: SelectInterpretationTypeDelegate)
+ func openAssigningInterpreter()
+ func dismiss()
+}
+
+protocol InterpretationViewProtocol: class {
+
+ var presenter: InterpretationPresenterProtocol! { get set }
+
+ /**
+ * Add here your methods for communication PRESENTER -> VIEW
+ */
+
+ func setInterpretationType(_ type: InterpretationType)
+}
+
+protocol InterpretationPresenterProtocol: BasePresenterProtocol {
+
+ var view: InterpretationViewProtocol! { get set }
+ var interactor: InterpretationInteractorInputProtocol! { get set }
+ var wireFrame: InterpretationWireFrameProtocol! { get set }
+
+ /**
+ * Add here your methods for communication VIEW -> PRESENTER
+ */
+ func openAssigningInterpreter(with model: InterpretationModel)
+ func openInterpretationType()
+ func dismiss()
+}
+
+protocol InterpretationInteractorOutputProtocol: class {
+
+ /**
+ * Add here your methods for communication INTERACTOR -> PRESENTER
+ */
+}
+
+protocol InterpretationInteractorInputProtocol: class {
+
+ var presenter: InterpretationInteractorOutputProtocol! { get set }
+
+ /**
+ * Add here your methods for communication PRESENTER -> INTERACTOR
+ */
+}
+
+protocol SelectInterpretationTypeDelegate: class {
+ func typeSelected(_ type: InterpretationType)
+}
diff --git a/Nynja/Modules/Interpretation/Presenter/InterpretationPresenter.swift b/Nynja/Modules/Interpretation/Presenter/InterpretationPresenter.swift
new file mode 100644
index 0000000000000000000000000000000000000000..ec553175996412d0f7991fb1d7aebb7658454de0
--- /dev/null
+++ b/Nynja/Modules/Interpretation/Presenter/InterpretationPresenter.swift
@@ -0,0 +1,60 @@
+//
+// InterpretationPresenter.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class InterpretationPresenter: BasePresenter, InterpretationPresenterProtocol, InterpretationInteractorOutputProtocol {
+
+ weak var view: InterpretationViewProtocol!
+ var interactor: InterpretationInteractorInputProtocol!
+ var wireFrame: InterpretationWireFrameProtocol!
+ private var useCaseValidationService: UseCaseValidationServiceProtocol!
+
+ func openInterpretationType() {
+ self.wireFrame.openInterpretationType(delegate: self)
+ }
+
+ func openAssigningInterpreter(with model: InterpretationModel) {
+ self.validateInterpretationConditions(model: model)
+ }
+
+ func dismiss() {
+ self.wireFrame.dismiss()
+ }
+
+ private func validateInterpretationConditions(model: InterpretationModel) {
+ do {
+ try self.useCaseValidationService.validateInterpretationLanguages(model.fromLang, toLanguage: model.toLang)
+ self.wireFrame.openAssigningInterpreter()
+ } catch {
+ AlertManager.sharedInstance.showAlertOk(message: error.localizedDescription)
+ }
+ }
+}
+
+extension InterpretationPresenter: SelectInterpretationTypeDelegate {
+ func typeSelected(_ type: InterpretationType) {
+ self.view.setInterpretationType(type)
+ }
+}
+
+extension InterpretationPresenter: SetInjectable {
+ func inject(dependencies: InterpretationPresenter.Dependencies) {
+ self.view = dependencies.view
+ self.interactor = dependencies.interactor
+ self.wireFrame = dependencies.wireFrame
+ self.useCaseValidationService = dependencies.useCaseValidationService
+ }
+
+ struct Dependencies {
+ let view: InterpretationViewProtocol
+ let interactor: InterpretationInteractorInputProtocol
+ let wireFrame: InterpretationWireFrameProtocol
+ let useCaseValidationService: UseCaseValidationServiceProtocol
+ }
+}
diff --git a/Nynja/Modules/Interpretation/View/AlertTextFieldViewController/AlertTextFieldViewController.swift b/Nynja/Modules/Interpretation/View/AlertTextFieldViewController/AlertTextFieldViewController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..b374e1ff49468c284b1e326a21314af9e41f1cb3
--- /dev/null
+++ b/Nynja/Modules/Interpretation/View/AlertTextFieldViewController/AlertTextFieldViewController.swift
@@ -0,0 +1,192 @@
+//
+// AlertTextFieldViewController.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/29/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+typealias TimeCallback = (Int) -> ()
+typealias EmptyCallBack = ()->()
+
+class AlertTextFieldViewController: BaseVC {
+
+ var completion: TimeCallback?
+ private var initTimeValue: Int!
+ private let validationService = ServiceFactory().makeTextInputValidationService()
+
+ // MARK: - Views
+
+ lazy var backView: UIView = {
+ let view = UIView()
+ self.view.addSubview(view)
+
+ view.snp.makeConstraints({ (make) in
+ make.top.left.right.equalToSuperview()
+ make.bottom.equalTo(self.view.keyboardLayoutGuide.snp.top)
+ })
+ return view
+ }()
+
+ lazy var containerView: UIView = {
+ let view = UIView()
+ view.setContentHuggingPriority(.defaultHigh, for: .vertical)
+ view.backgroundColor = Constants.colors.darkLight.getColor()
+ self.backView.addSubview(view)
+
+ view.snp.makeConstraints({ (make) in
+ make.centerX.centerY.equalToSuperview()
+ make.width.equalTo(Constraints.titleLabel.width)
+ })
+ return view
+ }()
+
+ lazy var titleLabel: UILabel = {
+ let label = UILabel()
+ label.text = Strings.titleLabelText
+ label.font = Texts.titleLabel.font
+ label.textColor = Texts.titleLabel.color
+ self.containerView.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.top.equalToSuperview().offset(Constraints.titleLabel.top)
+ make.height.equalTo(Constraints.titleLabel.height)
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalToSuperview().inset(Constraints.sidePadding)
+ make.bottom.equalTo(inputField.snp.top)
+ })
+ return label
+ }()
+
+ lazy var inputField: MaterialTextField = {
+ let field = MaterialTextField()
+ field.keyboardType = .numberPad
+
+ self.containerView.addSubview(field)
+ field.snp.makeConstraints { (make) in
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalToSuperview().offset(-Constraints.sidePadding)
+ make.bottom.equalTo(okButton.snp.top)
+ }
+ return field
+ }()
+
+ lazy var okButton: UIButton = {
+ let button = UIButton()
+ self.containerView.addSubview(button)
+ let title = "ok".localized.uppercased()
+ button.setTitle(title, for: .normal)
+ button.titleLabel?.font = Texts.buttons.font
+ button.setTitleColor(Texts.buttons.color, for: .normal)
+ button.addTarget(self, action: #selector(okButtonTapped(_:)), for: .touchUpInside)
+
+ button.snp.makeConstraints({ (make) in
+ make.width.equalTo(Constraints.okButton.width)
+ make.height.equalTo(Constraints.okButton.height)
+ make.right.equalToSuperview().inset(Constraints.sidePadding)
+ make.left.equalTo(cancelButton.snp.right).offset(Constraints.cancelButton.rightPadding)
+ make.centerY.equalTo(cancelButton)
+ make.bottom.equalToSuperview().offset(-Constraints.okButton.bottom)
+ })
+ return button
+ }()
+
+ lazy var cancelButton: UIButton = {
+ let button = UIButton()
+ self.containerView.addSubview(button)
+ let title = "cancel".localized.uppercased()
+ button.setTitle(title, for: .normal)
+ button.titleLabel?.font = Texts.buttons.font
+ button.titleLabel?.textAlignment = .right
+ button.setTitleColor(Texts.buttons.color, for: .normal)
+ button.addTarget(self, action: #selector(cancelButtonTapped(_:)), for: .touchUpInside)
+
+ button.snp.makeConstraints({ (make) in
+ make.width.equalTo(Constraints.cancelButton.width).priority(200)
+ make.height.equalTo(Constraints.cancelButton.height)
+ })
+ return button
+ }()
+
+
+ // MARK: - Init
+
+ convenience init(initialValue: Int) {
+ self.init()
+ initTimeValue = initialValue
+
+ self.modalTransitionStyle = .crossDissolve
+ self.modalPresentationStyle = .overCurrentContext
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.baseSetup()
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+ let _ = self.inputField.becomeFirstResponder()
+ }
+
+ private func baseSetup() {
+ self.backImage.image = nil
+ self.view.backgroundColor = Constants.colors.black.getColor(withAlpha: 0.4)
+ self.view.isOpaque = false
+ self.containerView.isHidden = false
+ self.titleLabel.isHidden = false
+
+ self.inputField.text = String(self.initTimeValue)
+
+ self.inputField.textChanged = { [unowned self] input in
+ if input.text.isEmpty {
+ self.validate(0)
+ } else if let num = Int(input.text) {
+ self.validate(num)
+ }
+ }
+ self.inputField.shouldTextChanged = { (input, range, string) in
+ if string == "0" && range == NSRange(location: 0, length: 0) { return false }
+ let allowedCharacters = CharacterSet(charactersIn:"0123456789")
+ let characterSet = CharacterSet(charactersIn: string)
+ return allowedCharacters.isSuperset(of: characterSet)
+ }
+ }
+
+ // MARK: - Actions
+
+ @objc
+ func okButtonTapped(_ sender: UIButton) {
+ if let number = Int(self.inputField.text) {
+ self.completion?(number)
+ self.dismiss(animated: true)
+ }
+ }
+
+ @objc
+ func cancelButtonTapped(_ sender: UIButton) {
+ self.dismiss(animated: true)
+ }
+
+ private func validate(_ number: Int) {
+ do {
+ let _ = try self.validationService.validateInterpretationTime(number)
+ self.updateInfo(with: "")
+ self.okButton.isEnabled = true
+ } catch {
+ self.updateInfo(with: error.localizedDescription)
+ self.okButton.isEnabled = false
+ }
+ }
+
+ func updateInfo(with message: String?) {
+ guard let message = message else {
+ inputField.info = nil
+ return
+ }
+ let inputInfo = InputInfo(text: message, kind: .warning)
+ inputField.info = inputInfo
+ }
+}
diff --git a/Nynja/Modules/Interpretation/View/AlertTextFieldViewController/AlertTextFieldViewControllerLayout.swift b/Nynja/Modules/Interpretation/View/AlertTextFieldViewController/AlertTextFieldViewControllerLayout.swift
new file mode 100644
index 0000000000000000000000000000000000000000..22b7088ddcc2f87fbb8dd4390aaaabcf3eadf074
--- /dev/null
+++ b/Nynja/Modules/Interpretation/View/AlertTextFieldViewController/AlertTextFieldViewControllerLayout.swift
@@ -0,0 +1,62 @@
+//
+// AlertTextFieldViewControllerLayout.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 8/5/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+extension AlertTextFieldViewController {
+
+ struct Strings {
+ static let titleLabelText = "enter_interpretation_time".localized
+ }
+
+ struct Constraints {
+
+ static let sidePadding = 24.adjustedByWidth
+
+ struct titleLabel {
+ static let height = Texts.titleLabel.height
+ static let width = 275.adjustedByWidth
+ static let top = 16.adjustedByWidth
+ }
+
+ struct inputField {
+ static let height = 30.adjustedByWidth
+ }
+
+ struct okButton {
+ static let height = 32.adjustedByWidth
+ static let width = 32.adjustedByWidth
+ static let bottom = 8.adjustedByWidth
+ }
+
+ struct cancelButton {
+ static let height = 32.adjustedByWidth
+ static let width = 52.adjustedByWidth
+ static let rightPadding = 8.adjustedByWidth
+ }
+ }
+
+ struct Texts {
+ struct dafaultWhite {
+ static let height = 20.adjustedByWidth
+ static let font = UIFont(fontName: Constants.fonts.medium, height: CGFloat(height))
+ static let color = Constants.colors.white.getColor()
+ }
+ struct titleLabel {
+ static let height = 17.adjustedByWidth
+ static let font = UIFont(fontName: Constants.fonts.regular, height: CGFloat(height))
+ static let color = Constants.colors.darkGray.getColor()
+ }
+
+ struct buttons {
+ static let titleHeight = 22.adjustedByWidth
+ static let font = UIFont(fontName: Constants.fonts.medium, height: CGFloat(titleHeight))
+ static let color = Constants.colors.red.getColor()
+ }
+ }
+}
diff --git a/Nynja/Modules/Interpretation/View/InterpretationLayout.swift b/Nynja/Modules/Interpretation/View/InterpretationLayout.swift
new file mode 100644
index 0000000000000000000000000000000000000000..7aa7cdbbfcbc482097a900cae9209af00e694eec
--- /dev/null
+++ b/Nynja/Modules/Interpretation/View/InterpretationLayout.swift
@@ -0,0 +1,99 @@
+//
+// InterpretationLayout.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+extension InterpretationViewController {
+
+ // MARK: Text
+ struct Text {
+ struct defaultWhite {
+ static let height = CGFloat(22.adjustedByWidth)
+ static let font = UIFont(fontName: Constants.fonts.medium, height: height)
+ static let fontColor = Constants.colors.white.getColor()
+ }
+ struct defaultGray {
+ static let height = CGFloat(20.adjustedByWidth)
+ static let font = UIFont(fontName: Constants.fonts.regular, height: height)
+ static let fontColor = Constants.colors.gray.getColor()
+ }
+ }
+
+ struct Constraints {
+
+ static let sidePadding = 16.adjustedByWidth
+ static let interitemPadding = 8.adjustedByWidth
+ static let separatorHeight = 1
+
+ struct fromLangPickerView {
+ static let widthOffset = Constraints.swapButton.side/2 + Constraints.sidePadding
+ static let height = 158.adjustedByWidth
+ }
+
+ struct langForInterpretationLabel {
+ static let bottomPadding = 8.adjustedByWidth
+ }
+
+ struct toLangPickerView {
+ static let bottomPadding = 16.adjustedByWidth
+ }
+
+ struct swapButton {
+ static let side = 42.adjustedByWidth
+ static let topPadding = 8.adjustedByWidth
+ }
+
+ struct fromLabel {
+ static let rightPadding = Constraints.swapButton.side
+ }
+
+ struct interpretationTypeLabel {
+ static let bottomPadding = 8.adjustedByWidth
+ }
+
+ struct interpretationTypeButton {
+ static let width = 30.adjustedByWidth
+ }
+
+ struct interpretationTimeButton {
+ static let width = 30.adjustedByWidth
+ }
+
+ struct totalPriceValueLabel {
+ static let width = 30.adjustedByWidth
+ }
+
+ struct searchButton {
+ static let height = 44.adjustedByWidth
+ static let topPading = 24.adjustedByWidth
+ static let bottomPadding = 16.adjustedByWidth
+ }
+
+ struct separatorBottom {
+ static let topPading = 8.adjustedByWidth
+ }
+ }
+
+ // MARK: String
+ enum Strings: String {
+ case interpretationTime = "interpretation_time"
+ case langForInterpretation = "lang_for_interpretation"
+ case from = "from"
+ case to = "to"
+ case interpretationType = "interpretation_type"
+ case totalPrice = "total_price"
+ case search = "search_interpreter"
+ }
+
+ struct Colors {
+ struct gradientView {
+ static let colors: [UIColor] = [Constants.colors.marketplaceMunu.gradient.getColor(withAlpha: 0.1),
+ Constants.colors.marketplaceMunu.gradient.getColor(withAlpha: 0.5)]
+ }
+ }
+}
diff --git a/Nynja/Modules/Interpretation/View/InterpretationViewController.swift b/Nynja/Modules/Interpretation/View/InterpretationViewController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..f6745652f3cb2bee1fcd11bcea074b42faf63c67
--- /dev/null
+++ b/Nynja/Modules/Interpretation/View/InterpretationViewController.swift
@@ -0,0 +1,431 @@
+//
+// InterpretationViewController.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+import SnapKit
+
+final class InterpretationViewController: BaseVC, InterpretationViewProtocol {
+
+ var presenter: InterpretationPresenterProtocol! {
+ didSet {
+ _presenter = presenter
+ }
+ }
+
+ private let model = InterpretationModel()
+
+ private var fromLangDelegate: LanguagePickerDelegate!
+ private var toLangDelegate: LanguagePickerDelegate!
+
+ lazy var interpretationTimeLabel: UILabel = {
+ let label = UILabel()
+ label.text = Strings.interpretationTime.localized
+ label.font = Text.defaultWhite.font
+ label.textColor = Text.defaultWhite.fontColor
+
+ self.view.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.top.equalTo(navigationView.snp.bottomMargin).offset(Constraints.sidePadding)
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalTo(interpretationTimeButton.snp.left).offset(-Constraints.interitemPadding)
+ make.height.equalTo(Text.defaultWhite.height)
+ make.bottom.equalTo(separatorTop.snp.top).offset(-Constraints.sidePadding)
+ })
+ return label
+ }()
+
+ lazy var interpretationTimeButton: UIButton = {
+ let button = UIButton()
+ button.titleLabel?.font = Text.defaultGray.font
+ button.setTitleColor(Text.defaultGray.fontColor, for: .normal)
+ button.addTarget(self, action: #selector(interpretationTimeButtonTapped), for: .touchUpInside)
+
+ self.view.addSubview(button)
+
+ button.snp.makeConstraints({ (make) in
+ make.width.equalTo(Constraints.interpretationTimeButton.width).priority(200)
+ make.top.equalTo(navigationView.snp.bottomMargin).offset(Constraints.sidePadding)
+ make.height.equalTo(Text.defaultGray.height)
+ make.right.equalToSuperview().inset(Constraints.sidePadding)
+ })
+ return button
+ }()
+
+ lazy var separatorTop: UIView = {
+ let separator = UIView()
+
+ separator.backgroundColor = Constants.colors.separatorGrayColor.getColor()
+
+ self.view.addSubview(separator)
+ separator.snp.makeConstraints({ (make) in
+ make.height.equalTo(Constraints.separatorHeight)
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalToSuperview().inset(Constraints.sidePadding)
+ make.bottom.equalTo(langForInterpretationLabel.snp.top).offset(-Constraints.sidePadding)
+ })
+ return separator
+ }()
+
+ lazy var langForInterpretationLabel: UILabel = {
+ let label = UILabel()
+ label.text = Strings.langForInterpretation.localized
+ label.font = Text.defaultWhite.font
+ label.textColor = Text.defaultWhite.fontColor
+
+ self.view.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalToSuperview().offset(-Constraints.sidePadding)
+ make.height.equalTo(Text.defaultWhite.height)
+ make.bottom.equalTo(fromLabel.snp.top).offset(-Constraints.langForInterpretationLabel.bottomPadding)
+ })
+ return label
+ }()
+
+ lazy var fromLabel: UILabel = {
+ let label = UILabel()
+ label.text = Strings.from.localized
+ label.font = Text.defaultGray.font
+ label.textColor = Text.defaultGray.fontColor
+
+ self.view.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.height.equalTo(Text.defaultGray.height)
+ make.width.equalTo(fromLangPickerView)
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalTo(toLabel.snp.left).offset(-Constraints.fromLabel.rightPadding)
+ make.top.equalTo(toLabel)
+ make.bottom.equalTo(fromLangPickerView.snp.top)
+ make.bottom.equalTo(swapTopLine.snp.top)
+ })
+ return label
+ }()
+
+ lazy var toLabel: UILabel = {
+ let label = UILabel()
+ label.text = Strings.to.localized
+ label.font = Text.defaultGray.font
+ label.textColor = Text.defaultGray.fontColor
+
+ self.view.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.height.equalTo(Text.defaultGray.height)
+ make.right.equalToSuperview().offset(-Constraints.sidePadding)
+ make.bottom.equalTo(toLangPickerView.snp.top)
+ })
+ return label
+ }()
+
+ lazy var fromLangPickerView: UIPickerView = {
+ let picker = UIPickerView()
+
+ self.view.addSubview(picker)
+ picker.snp.makeConstraints({ (make) in
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.height.equalTo(Constraints.fromLangPickerView.height)
+ make.width.equalToSuperview().dividedBy(2).offset(-Constraints.fromLangPickerView.widthOffset)
+ make.right.equalTo(swapButton.snp.left)
+ make.centerY.equalTo(swapButton)
+
+ make.bottom.equalTo(interpretationTypeLabel.snp.top).offset(-Constraints.sidePadding)
+ make.bottom.equalTo(swapBottomLine.snp.bottom)
+ })
+ return picker
+ }()
+
+ lazy var swapButton: UIButton = {
+ let button = UIButton()
+ self.view.addSubview(button)
+ button.setImage(#imageLiteral(resourceName: "marketplace_swap_button"), for: .normal)
+ button.addTarget(self, action: #selector(swapButtonTapped), for: .touchUpInside)
+
+ button.snp.makeConstraints({ (make) in
+ make.width.height.equalTo(Constraints.swapButton.side)
+ make.right.equalTo(toLangPickerView.snp.left)
+ make.top.equalTo(swapTopLine.snp.bottom).offset(Constraints.swapButton.topPadding)
+ make.centerX.equalTo(swapTopLine)
+ make.centerX.equalTo(swapBottomLine)
+ make.bottom.equalTo(swapBottomLine.snp.top)
+ })
+ return button
+ }()
+
+ lazy var swapTopLine: GradientView = {
+ let view = GradientView(colors: Colors.gradientView.colors)
+ self.view.addSubview(view)
+
+ view.snp.makeConstraints({ (make) in
+ make.width.equalTo(Constraints.separatorHeight)
+ })
+ return view
+ }()
+
+ lazy var swapBottomLine: GradientView = {
+ let view = GradientView(colors: Colors.gradientView.colors)
+ view.transform = CGAffineTransform(rotationAngle: .pi)
+ self.view.addSubview(view)
+
+ view.snp.makeConstraints({ (make) in
+ make.width.equalTo(Constraints.separatorHeight)
+ })
+ return view
+ }()
+
+ lazy var toLangPickerView: UIPickerView = {
+ let picker = UIPickerView()
+ self.view.addSubview(picker)
+ picker.snp.makeConstraints({ (make) in
+ make.height.equalTo(Constraints.fromLangPickerView.height)
+ make.right.equalToSuperview().offset(-Constraints.sidePadding)
+ make.bottom.equalTo(interpretationTypeButton.snp.top).offset(-Constraints.toLangPickerView.bottomPadding)
+ })
+ return picker
+ }()
+
+ lazy var interpretationTypeLabel: UILabel = {
+ let label = UILabel()
+ label.text = Strings.interpretationType.localized
+ label.font = Text.defaultWhite.font
+ label.textColor = Text.defaultWhite.fontColor
+
+ self.view.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.height.equalTo(Text.defaultWhite.height)
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalTo(interpretationTypeButton.snp.left).offset(-Constraints.interitemPadding)
+ make.bottom.equalTo(separatorMiddle.snp.top).offset(-Constraints.interpretationTypeLabel.bottomPadding)
+ })
+ return label
+ }()
+
+ lazy var interpretationTypeButton: UIButton = {
+ let button = UIButton()
+ button.setTitle(self.model.type.localized, for: .normal)
+ button.titleLabel?.font = Text.defaultGray.font
+ button.setTitleColor(Text.defaultGray.fontColor, for: .normal)
+
+ button.addTarget(self, action: #selector(interpretationTypeButtonTapped), for: .touchUpInside)
+ self.view.addSubview(button)
+
+ button.snp.makeConstraints({ (make) in
+ make.height.equalTo(Text.defaultGray.height)
+ make.width.equalTo(Constraints.interpretationTypeButton.width).priority(200)
+ make.right.equalToSuperview().inset(Constraints.sidePadding)
+ })
+ return button
+ }()
+
+ lazy var separatorMiddle: UIView = {
+ let separator = UIView()
+
+ separator.backgroundColor = Constants.colors.separatorGrayColor.getColor()
+
+ view.addSubview(separator)
+ separator.snp.makeConstraints({ (make) in
+ make.height.equalTo(Constraints.separatorHeight)
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalToSuperview().inset(Constraints.sidePadding)
+ })
+ return separator
+ }()
+
+ private lazy var searchButton: NynjaButton = {
+ let button = NynjaButton(height: CGFloat(Constraints.searchButton.height))
+ button.titleLabel?.font = Text.defaultWhite.font
+ button.setTitle(Strings.search.localized.uppercased(), for: .normal)
+ self.view.addSubview(button)
+
+ button.addTarget(self, action: #selector(searchButtonTapped), for: .touchUpInside)
+
+ button.snp.makeConstraints({ (make) in
+ make.height.equalTo(Constraints.searchButton.height)
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalToSuperview().offset(-Constraints.sidePadding)
+ make.bottom.equalToSuperview().offset(-Constraints.searchButton.bottomPadding)
+ make.top.equalTo(separatorBottom.snp.bottom).offset(Constraints.searchButton.topPading)
+ })
+ return button
+ }()
+
+ lazy var separatorBottom: UIView = {
+ let separator = UIView()
+
+ separator.backgroundColor = Constants.colors.separatorGrayColor.getColor()
+
+ view.addSubview(separator)
+ separator.snp.makeConstraints({ (make) in
+ make.height.equalTo(Constraints.separatorHeight)
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.right.equalToSuperview().inset(Constraints.sidePadding)
+ make.top.equalTo(totalPriceTitleLabel.snp.bottom).offset(Constraints.separatorBottom.topPading)
+ make.top.equalTo(totalPriceValueLabel.snp.bottom).offset(Constraints.separatorBottom.topPading)
+ })
+ return separator
+ }()
+
+ lazy var totalPriceTitleLabel: UILabel = {
+ let label = UILabel()
+ label.text = Strings.totalPrice.localized
+ label.font = Text.defaultWhite.font
+ label.textColor = Text.defaultWhite.fontColor
+
+ self.view.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.left.equalToSuperview().offset(Constraints.sidePadding)
+ make.height.equalTo(Text.defaultWhite.height)
+ make.right.equalTo(totalPriceValueLabel.snp.left).offset(-Constraints.interitemPadding)
+ })
+ return label
+ }()
+
+ lazy var totalPriceValueLabel: UILabel = {
+ let label = UILabel()
+ label.font = Text.defaultWhite.font
+ label.textColor = Constants.colors.red.getColor()
+
+ self.view.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.height.equalTo(Text.defaultWhite.height)
+ make.width.equalTo(Constraints.totalPriceValueLabel.width).priority(200)
+ make.right.equalToSuperview().offset(-Constraints.sidePadding)
+ })
+ return label
+ }()
+
+
+ // MARK: - Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ setupUI()
+ }
+
+
+ // MARK: - UI Setup
+
+ private func setupUI() {
+ self.navigationView.configure(config: NavigationView.Config(
+ isVisibleSeparator: navigationView.isSeparatorVisible ?? false,
+ isVisibleBackButton: true,
+ title: title,
+ navigationHandler: self,
+ backButtonImage: UIImage(named:"ic_back_navigation")))
+ self.screenTitle = "interpretation".localized.uppercased()
+ self.interpretationTimeLabel.isHidden = false
+ self.searchButton.isHidden = false
+ self.setupPickers()
+ self.interpretationTimeButton.setTitle("\(self.model.time) min", for: .normal)
+ self.interpretationTypeButton.setTitle(self.model.type.localized, for: .normal)
+ self.updateTotalPrice()
+ }
+
+ private func setupPickers() {
+ self.setupLanguagePicker(picker: self.fromLangPickerView, with: fromLangDelegate, callback: self)
+ self.setupLanguagePicker(picker: self.toLangPickerView, with: self.toLangDelegate, callback: self)
+
+ self.selectRow(with: self.model.fromLang, in: self.fromLangPickerView, animated: false)
+ self.selectRow(with: self.model.toLang, in: self.toLangPickerView, animated: false)
+ }
+
+ private func setupLanguagePicker(picker: UIPickerView, with delegate: LanguagePickerDelegate, callback: LanguagePickerDelegateCallback) {
+ delegate.callback = callback
+ picker.dataSource = delegate
+ picker.delegate = delegate
+ }
+
+ private func selectRow(with language: Language, in picker: UIPickerView, animated: Bool) {
+ var row: Int? = fromLangDelegate.positionIn(value: language)
+ if picker == self.toLangPickerView {
+ row = toLangDelegate.positionIn(value: language)
+ }
+ picker.selectRow(row ?? 0, inComponent: 0, animated: animated)
+ }
+
+ // MARK: - Actions
+ @objc
+ private func swapButtonTapped() {
+ guard self.model.fromLang != .empty_default, self.model.fromLang != self.model.toLang else { return }
+ let temp = self.model.fromLang
+
+ self.model.fromLang = self.model.toLang
+ self.model.toLang = temp
+
+ self.selectRow(with: self.model.fromLang, in: self.fromLangPickerView, animated: true)
+ self.selectRow(with: self.model.toLang, in: toLangPickerView, animated: true)
+ }
+
+ @objc
+ private func interpretationTimeButtonTapped() {
+ AlertManager().showTimeFieldAlert(with: self.model.time) { [unowned self] (time) in
+ self.setInterpretationTime(time)
+ }
+ }
+
+ @objc
+ private func interpretationTypeButtonTapped() {
+ self.presenter.openInterpretationType()
+ }
+
+ @objc
+ private func searchButtonTapped() {
+ self.presenter.openAssigningInterpreter(with: self.model)
+ }
+
+ func setInterpretationType(_ type: InterpretationType) {
+ self.model.type = type
+ self.interpretationTypeButton.setTitle(type.localized, for: .normal)
+ self.updateTotalPrice()
+ }
+
+ private func setInterpretationTime(_ time: Int) {
+ self.model.time = time
+ self.interpretationTimeButton.setTitle("\(time) min", for: .normal)
+ self.updateTotalPrice()
+ }
+
+ func updateTotalPrice() {
+ self.totalPriceValueLabel.text = self.model.price.formattedCurrency
+ }
+}
+
+extension InterpretationViewController: NavigationProtocol {
+ func back() {
+ self.presenter.dismiss()
+ }
+}
+
+extension InterpretationViewController: LanguagePickerDelegateCallback {
+ func didSelectLanguage(_ pickerView: UIPickerView, language: Language) {
+ if pickerView == self.fromLangPickerView {
+ self.model.fromLang = language
+ } else if pickerView == self.toLangPickerView {
+ self.model.toLang = language
+ }
+ }
+}
+
+extension InterpretationViewController: SetInjectable {
+ func inject(dependencies: InterpretationViewController.Dependencies) {
+ self.presenter = dependencies.presenter
+ self.fromLangDelegate = dependencies.fromLangDelegate
+ self.toLangDelegate = dependencies.toLangDelegate
+ }
+
+ struct Dependencies {
+ let presenter: InterpretationPresenterProtocol
+ let fromLangDelegate: LanguagePickerDelegate
+ let toLangDelegate: LanguagePickerDelegate
+ }
+}
diff --git a/Nynja/Modules/Interpretation/View/LanguagePickerDelegate.swift b/Nynja/Modules/Interpretation/View/LanguagePickerDelegate.swift
new file mode 100644
index 0000000000000000000000000000000000000000..5fd82bb474d5b9186e04a113b937f9de914c67c3
--- /dev/null
+++ b/Nynja/Modules/Interpretation/View/LanguagePickerDelegate.swift
@@ -0,0 +1,78 @@
+//
+// LanguagePickerDelegate.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+protocol LanguagePickerDelegateCallback : class {
+ func didSelectLanguage(_ pickerView: UIPickerView, language : Language)
+}
+
+class LanguagePickerDelegate : NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
+ private let rows = 100000
+ var pickerViewMiddle: Int { return ((self.rows / self.data.count) / 2) * self.data.count}
+
+ weak var callback : LanguagePickerDelegateCallback?
+
+ private var data: [Language]!
+
+ //MARK: - Init
+ required init(defaultEmptyLanguage: Bool) {
+ self.data = defaultEmptyLanguage ? Language.allValuesWithDefault : Language.allValues
+ }
+
+ //MARK: - UIPickerViewDataSource
+ func numberOfComponents(in pickerView: UIPickerView) -> Int {
+ return 1
+ }
+
+ func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
+ return rows
+ }
+
+ //MARK: - UIPickerViewDelegate
+ func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
+ return CGFloat(Values.height)
+ }
+
+ func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
+ pickerView.hideLines()
+ return self.createLabel(forRow: row, pickerView: pickerView)
+ }
+
+ func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
+ callback?.didSelectLanguage(pickerView, language: valueForRow(row))
+ }
+
+ func positionIn(value : Language) -> Int? {
+ if let i = data.index(of: value) {
+ return pickerViewMiddle + i
+ }
+ return nil
+ }
+
+ private func createLabel(forRow row: Int, pickerView: UIPickerView) -> UILabel {
+ let size = CGSize(width: pickerView.bounds.width,
+ height: Values.height)
+ let label = UILabel(frame: CGRect(origin: .zero, size: size))
+ label.text = valueForRow(row).longValue
+ label.font = Values.font
+ label.textAlignment = NSTextAlignment.center
+ label.textColor = Values.fontColor
+ return label
+ }
+
+ private func valueForRow(_ row: Int) -> Language {
+ return self.data[row % data.count]
+ }
+
+ struct Values {
+ static let height = CGFloat(24.adjustedByWidth)
+ static let font = UIFont(fontName: Constants.fonts.medium, height: height)
+ static let fontColor = Constants.colors.white.getColor()
+ }
+}
diff --git a/Nynja/Modules/Interpretation/Wireframe/InterpretationWireFrame.swift b/Nynja/Modules/Interpretation/Wireframe/InterpretationWireFrame.swift
new file mode 100644
index 0000000000000000000000000000000000000000..4c02b27e3474e9c27d39c8eb956a2d291137b825
--- /dev/null
+++ b/Nynja/Modules/Interpretation/Wireframe/InterpretationWireFrame.swift
@@ -0,0 +1,51 @@
+//
+// InterpretationWireFrame.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+final class InterpretationWireFrame: InterpretationWireFrameProtocol {
+ weak var navigation: UINavigationController?
+
+ func presentInterpretation(navigation: UINavigationController) {
+ self.navigation = navigation
+
+ let view = InterpretationViewController()
+ let presenter = InterpretationPresenter()
+ let interactor = InterpretationInteractor()
+
+ // Connecting
+
+ let viewDependencies = InterpretationViewController.Dependencies(presenter: presenter, fromLangDelegate: LanguagePickerDelegate(defaultEmptyLanguage: true), toLangDelegate: LanguagePickerDelegate(defaultEmptyLanguage: false))
+ view.inject(dependencies: viewDependencies)
+
+ let serviceFactory = ServiceFactory()
+ let useCaseValidationService = serviceFactory.makeUseCaseValidationServise()
+ let presenterDependencies = InterpretationPresenter.Dependencies(view: view, interactor: interactor, wireFrame: self, useCaseValidationService: useCaseValidationService)
+ presenter.inject(dependencies: presenterDependencies)
+ let interactorDependencies = InterpretationInteractor.Dependencies(presenter: presenter)
+ interactor.inject(dependencies: interactorDependencies)
+
+ view.modalTransitionStyle = .crossDissolve
+ view.modalPresentationStyle = .overCurrentContext
+ navigation.pushViewController(view as UIViewController, animated: true)
+ }
+
+ func openInterpretationType(delegate: SelectInterpretationTypeDelegate) {
+ guard let navigation = self.navigation else { return }
+ InterpretationTypeWireFrame().presentInterpretationType(navigation: navigation, delegate: delegate)
+ }
+
+ func openAssigningInterpreter() {
+ guard let navigation = self.navigation else { return }
+ AssigningInterpreterWireFrame().presentAssigningInterpreter(navigation: navigation)
+ }
+
+ func dismiss() {
+ self.navigation?.popViewController(animated: true)
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/Interactor/InterpretationTypeInteractor.swift b/Nynja/Modules/InterpretationType/Interactor/InterpretationTypeInteractor.swift
new file mode 100644
index 0000000000000000000000000000000000000000..e2c61b8140d89cad89d30a866c424544f1617311
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/Interactor/InterpretationTypeInteractor.swift
@@ -0,0 +1,23 @@
+//
+// InterpretationTypeInteractor.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class InterpretationTypeInteractor: InterpretationTypeInteractorInputProtocol {
+ weak var presenter: InterpretationTypeInteractorOutputProtocol!
+}
+
+extension InterpretationTypeInteractor: SetInjectable {
+ func inject(dependencies: InterpretationTypeInteractor.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: InterpretationTypeInteractorOutputProtocol
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/InterpretationType.swift b/Nynja/Modules/InterpretationType/InterpretationType.swift
new file mode 100644
index 0000000000000000000000000000000000000000..f85ac5b1339265cff9e435516e34bd8655b150d0
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/InterpretationType.swift
@@ -0,0 +1,42 @@
+//
+// InterpretationType.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/28/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+enum InterpretationType: String {
+ case general = "interpretation_type_general"
+ case technology = "interpretation_type_technology"
+ case legal = "interpretation_type_legal"
+ case medical = "interpretation_type_medical"
+
+ var description: String {
+ switch self {
+ case .general:
+ return "interpretation_type_description_general".localized
+ case .technology:
+ return "interpretation_type_description_technology".localized
+ case .legal:
+ return "interpretation_type_description_legal".localized
+ case .medical:
+ return "interpretation_type_description_medical".localized
+ }
+ }
+
+ var price: NYNMoney {
+ switch self {
+ case .general:
+ return NYNMoney(0.40)
+ case .technology:
+ return NYNMoney(0.50)
+ case .legal:
+ return NYNMoney(0.63)
+ case .medical:
+ return NYNMoney(0.75)
+ }
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/InterpretationTypeInteractor.swift b/Nynja/Modules/InterpretationType/InterpretationTypeInteractor.swift
new file mode 100644
index 0000000000000000000000000000000000000000..85be4ae294471f21098788bf51b35cf413d053d7
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/InterpretationTypeInteractor.swift
@@ -0,0 +1,24 @@
+//
+// InterpretationTypeInteractor.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class InterpretationTypeInteractor: InterpretationTypeInteractorInputProtocol {
+
+ weak var presenter: InterpretationTypeInteractorOutputProtocol!
+}
+
+extension InterpretationTypeInteractor: SetInjectable {
+ func inject(dependencies: InterpretationTypeInteractor.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: InterpretationTypeInteractorOutputProtocol
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/InterpretationTypePresenter.swift b/Nynja/Modules/InterpretationType/InterpretationTypePresenter.swift
new file mode 100644
index 0000000000000000000000000000000000000000..99e2ea5f256e0e018be3e692edf962dd9dd6d162
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/InterpretationTypePresenter.swift
@@ -0,0 +1,34 @@
+//
+// InterpretationTypePresenter.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class InterpretationTypePresenter: BasePresenter, InterpretationTypePresenterProtocol, InterpretationTypeInteractorOutputProtocol {
+
+ weak var view: InterpretationTypeViewProtocol!
+ var interactor: InterpretationTypeInteractorInputProtocol!
+ var wireFrame: InterpretationTypeWireFrameProtocol!
+
+ func showed() {
+
+ }
+}
+
+extension InterpretationTypePresenter: SetInjectable {
+ func inject(dependencies: InterpretationTypePresenter.Dependencies) {
+ self.view = dependencies.view
+ self.interactor = dependencies.interactor
+ self.wireFrame = dependencies.wireFrame
+ }
+
+ struct Dependencies {
+ let view: InterpretationTypeViewProtocol
+ let interactor: InterpretationTypeInteractorInputProtocol
+ let wireFrame: InterpretationTypeWireFrameProtocol
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/InterpretationTypeProtocols.swift b/Nynja/Modules/InterpretationType/InterpretationTypeProtocols.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2bb64d5c32c2b55606f638f4fbcc1549daf2387e
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/InterpretationTypeProtocols.swift
@@ -0,0 +1,56 @@
+//
+// InterpretationTypeProtocols.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+protocol InterpretationTypeWireFrameProtocol: class {
+
+ func presentInterpretationType(navigation: UINavigationController, delegate: SelectInterpretationTypeDelegate)
+
+ /**
+ * Add here your methods for communication PRESENTER -> WIREFRAME
+ */
+ func dismiss()
+}
+
+protocol InterpretationTypeViewProtocol: class {
+ var presenter: InterpretationTypePresenterProtocol! { get set }
+ /**
+ * Add here your methods for communication PRESENTER -> VIEW
+ */
+}
+
+protocol InterpretationTypePresenterProtocol: BasePresenterProtocol {
+
+ var view: InterpretationTypeViewProtocol! { get set }
+ var interactor: InterpretationTypeInteractorInputProtocol! { get set }
+ var wireFrame: InterpretationTypeWireFrameProtocol! { get set }
+ var delegate: SelectInterpretationTypeDelegate? { get set }
+ /**
+ * Add here your methods for communication VIEW -> PRESENTER
+ */
+
+ func selectItem(type: InterpretationType)
+ func dismiss()
+}
+
+protocol InterpretationTypeInteractorOutputProtocol: class {
+
+ /**
+ * Add here your methods for communication INTERACTOR -> PRESENTER
+ */
+}
+
+protocol InterpretationTypeInteractorInputProtocol: class {
+
+ var presenter: InterpretationTypeInteractorOutputProtocol! { get set }
+
+ /**
+ * Add here your methods for communication PRESENTER -> INTERACTOR
+ */
+}
diff --git a/Nynja/Modules/InterpretationType/InterpretationTypeViewController.swift b/Nynja/Modules/InterpretationType/InterpretationTypeViewController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..f1196dcabb7cf9d970e8fef31d3456926891d359
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/InterpretationTypeViewController.swift
@@ -0,0 +1,45 @@
+//
+// InterpretationTypeViewController.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+import SnapKit
+
+final class InterpretationTypeViewController: BaseVC, InterpretationTypeViewProtocol {
+
+ var presenter: InterpretationTypePresenterProtocol! {
+ didSet {
+ _presenter = presenter
+ }
+ }
+
+
+ // MARK: - Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ setupUI()
+ presenter.showed()
+ }
+
+
+ // MARK: - UI Setup
+
+ private func setupUI() {
+ }
+
+}
+
+extension InterpretationTypeViewController: SetInjectable {
+ func inject(dependencies: InterpretationTypeViewController.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: InterpretationTypePresenterProtocol
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/InterpretationTypeWireFrame.swift b/Nynja/Modules/InterpretationType/InterpretationTypeWireFrame.swift
new file mode 100644
index 0000000000000000000000000000000000000000..65fb6d8528d8c58f7029be0c4070df391e5da773
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/InterpretationTypeWireFrame.swift
@@ -0,0 +1,35 @@
+//
+// InterpretationTypeWireFrame.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+final class InterpretationTypeWireFrame: InterpretationTypeWireFrameProtocol {
+
+ weak var navigation: UINavigationController?
+
+ func presentInterpretationType(navigation: UINavigationController) {
+ self.navigation = navigation
+
+ let view = InterpretationTypeViewController()
+ let presenter = InterpretationTypePresenter()
+ let interactor = InterpretationTypeInteractor()
+
+ // Connecting
+ let viewDependencies = InterpretationTypeViewController.Dependencies(presenter: presenter)
+ view.inject(dependencies: viewDependencies)
+ let presenterDependencies = InterpretationTypePresenter.Dependencies(view: view, interactor: interactor, wireFrame: self)
+ presenter.inject(dependencies: presenterDependencies)
+ let interactorDependencies = InterpretationTypeInteractor.Dependencies(presenter: presenter)
+ interactor.inject(dependencies: interactorDependencies)
+
+ view.modalTransitionStyle = .crossDissolve
+ view.modalPresentationStyle = .overCurrentContext
+ navigation.pushViewController(view as UIViewController, animated: true)
+ }
+
+}
diff --git a/Nynja/Modules/InterpretationType/Presenter/InterpretationTypePresenter.swift b/Nynja/Modules/InterpretationType/Presenter/InterpretationTypePresenter.swift
new file mode 100644
index 0000000000000000000000000000000000000000..bfdaf80e575bc3eaeb939e14e5d6af41ac63cc0a
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/Presenter/InterpretationTypePresenter.swift
@@ -0,0 +1,43 @@
+//
+// InterpretationTypePresenter.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class InterpretationTypePresenter: BasePresenter, InterpretationTypePresenterProtocol, InterpretationTypeInteractorOutputProtocol {
+
+
+ weak var view: InterpretationTypeViewProtocol!
+ var interactor: InterpretationTypeInteractorInputProtocol!
+ var wireFrame: InterpretationTypeWireFrameProtocol!
+ weak var delegate: SelectInterpretationTypeDelegate?
+
+ func selectItem(type: InterpretationType) {
+ self.delegate?.typeSelected(type)
+ self.wireFrame.dismiss()
+ }
+
+ func dismiss() {
+ self.wireFrame.dismiss()
+ }
+}
+
+extension InterpretationTypePresenter: SetInjectable {
+ func inject(dependencies: InterpretationTypePresenter.Dependencies) {
+ self.view = dependencies.view
+ self.interactor = dependencies.interactor
+ self.wireFrame = dependencies.wireFrame
+ self.delegate = dependencies.delegate
+ }
+
+ struct Dependencies {
+ let view: InterpretationTypeViewProtocol
+ let interactor: InterpretationTypeInteractorInputProtocol
+ let wireFrame: InterpretationTypeWireFrameProtocol
+ let delegate: SelectInterpretationTypeDelegate
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/View/InterpretationTypeViewController.swift b/Nynja/Modules/InterpretationType/View/InterpretationTypeViewController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..03e291930a37df26bced919ce8ca26b4c2bfdbf9
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/View/InterpretationTypeViewController.swift
@@ -0,0 +1,83 @@
+//
+// InterpretationTypeViewController.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+import SnapKit
+
+final class InterpretationTypeViewController: BaseVC, InterpretationTypeViewProtocol {
+
+ var presenter: InterpretationTypePresenterProtocol! {
+ didSet {
+ _presenter = presenter
+ }
+ }
+
+ private var tableDataSource: InterpretationTypeTableDataSource!
+ private var tableDelegate: InterpretationTypeTableDelegate!
+
+ private lazy var tableView: UITableView = {
+ let table = UITableView()
+ table.backgroundColor = .clear
+ table.separatorStyle = .none
+ table.bounces = false
+ table.register(viewModel: InterpretationTypeCellModel.self)
+
+ self.view.addSubview(table)
+ table.snp.makeConstraints({ (make) in
+ make.top.equalTo(navigationView.snp.bottom)
+ make.left.right.bottom.equalToSuperview()
+ })
+ return table
+ }()
+
+
+ // MARK: - Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ setupUI()
+ }
+
+ // MARK: - UI Setup
+
+ private func setupUI() {
+ self.navigationView.configure(config: NavigationView.Config(
+ isVisibleSeparator: navigationView.isSeparatorVisible ?? false,
+ isVisibleBackButton: true,
+ title: title,
+ navigationHandler: self,
+ backButtonImage: UIImage(named:"ic_back_navigation")))
+ self.screenTitle = "interpretation_type".localized.uppercased()
+ self.tableView.dataSource = self.tableDataSource
+ self.tableView.delegate = self.tableDelegate
+
+ tableDelegate.selectAction = { [unowned self] type in
+ self.presenter.selectItem(type: type)
+ }
+ }
+}
+
+extension InterpretationTypeViewController: NavigationProtocol {
+ func back() {
+ self.presenter.dismiss()
+ }
+}
+
+extension InterpretationTypeViewController: SetInjectable {
+ func inject(dependencies: InterpretationTypeViewController.Dependencies) {
+ self.presenter = dependencies.presenter
+ self.tableDataSource = dependencies.tableDataSource
+ self.tableDelegate = dependencies.tableDelegate
+ }
+
+ struct Dependencies {
+ let presenter: InterpretationTypePresenterProtocol
+ let tableDataSource: InterpretationTypeTableDataSource
+ let tableDelegate: InterpretationTypeTableDelegate
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/View/TableView/Cell/InterpretationTypeCell.swift b/Nynja/Modules/InterpretationType/View/TableView/Cell/InterpretationTypeCell.swift
new file mode 100644
index 0000000000000000000000000000000000000000..730cf08b92757ca88392bb651205e765b9bfd9fb
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/View/TableView/Cell/InterpretationTypeCell.swift
@@ -0,0 +1,121 @@
+//
+// InterpretationTypeCell.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/28/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+class InterpretationTypeCell: UITableViewCell {
+
+ lazy var titleLabel: UILabel = {
+ let label = UILabel()
+ label.isUserInteractionEnabled = true
+ label.font = Text.defaultWhite.font
+ label.textColor = Text.defaultWhite.color
+ label.setContentHuggingPriority(.defaultHigh, for: .vertical)
+ self.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.top.equalToSuperview().offset(Constarints.titleLabel.topPadding)
+ make.left.equalToSuperview().offset(Constarints.sidePadding)
+ make.right.equalToSuperview().offset(-Constarints.sidePadding)
+ make.height.equalTo(Constarints.titleLabel.height)
+ make.bottom.equalTo(self.descriptionLabel.snp.top)
+
+ })
+ return label
+ }()
+
+ lazy var descriptionLabel: UILabel = {
+ let label = UILabel()
+ label.isUserInteractionEnabled = true
+ label.numberOfLines = 0
+ label.font = Text.defaultGray.font
+ label.textColor = Text.defaultGray.color
+
+ self.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.left.equalToSuperview().offset(Constarints.sidePadding)
+ make.right.equalToSuperview().offset(-Constarints.sidePadding)
+ make.bottom.equalTo(self.priceTitleLabel.snp.top).offset(-Constarints.descriptionLabel.bottomPadding)
+ make.bottom.equalTo(self.priceValueLabel.snp.top).offset(-Constarints.descriptionLabel.bottomPadding)
+ })
+ return label
+ }()
+
+ lazy var priceTitleLabel: UILabel = {
+ let label = UILabel()
+ label.isUserInteractionEnabled = true
+ label.text = "price_per_minute".localized
+ label.font = Text.defaultWhite.font
+ label.textColor = Text.defaultWhite.color
+
+ self.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.height.equalTo(Constarints.priceTitleLabel.height)
+ make.left.equalToSuperview().offset(Constarints.sidePadding)
+ make.right.equalTo(self.priceValueLabel.snp.left).offset(-Constarints.priceTitleLabel.rightPadding)
+ make.bottom.equalTo(self.separator.snp.top).offset(-Constarints.priceTitleLabel.bottomPadding)
+ })
+ return label
+ }()
+
+ lazy var priceValueLabel: UILabel = {
+ let label = UILabel()
+ label.isUserInteractionEnabled = true
+ label.font = Text.defaultRed.font
+ label.textColor = Text.defaultRed.color
+
+ self.addSubview(label)
+
+ label.snp.makeConstraints({ (make) in
+ make.height.equalTo(Constarints.priceTitleLabel.height)
+ make.width.equalTo(Constarints.priceValueLabel.width).priority(200)
+ make.right.equalToSuperview().offset(-Constarints.sidePadding)
+ })
+ return label
+ }()
+
+ lazy var separator: UIView = {
+ let separator = UIView()
+
+ separator.backgroundColor = Constants.colors.separatorGrayColor.getColor()
+
+ self.addSubview(separator)
+ separator.snp.makeConstraints({ (make) in
+ make.height.equalTo(Constarints.separatorHeight)
+ make.left.equalToSuperview().offset(Constarints.sidePadding)
+ make.right.equalToSuperview().offset(-Constarints.sidePadding)
+ make.bottom.equalToSuperview()
+ })
+ return separator
+ }()
+
+ // MARK: Init
+ override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
+ super.init(style: style, reuseIdentifier: reuseIdentifier)
+ baseSetup()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)
+ baseSetup()
+ }
+
+ private func baseSetup() {
+ self.backgroundColor = UIColor.clear
+ self.selectionStyle = .none
+ }
+
+ // MARK: ConfigurableCell
+ func setup(with type: InterpretationType) {
+ self.titleLabel.text = type.localized
+ self.descriptionLabel.text = type.description
+ self.priceValueLabel.text = type.price.formattedCurrency
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/View/TableView/Cell/InterpretationTypeCellLayout.swift b/Nynja/Modules/InterpretationType/View/TableView/Cell/InterpretationTypeCellLayout.swift
new file mode 100644
index 0000000000000000000000000000000000000000..89c87015df6c3af4afc4e8c0e0eec0ee87ca71a0
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/View/TableView/Cell/InterpretationTypeCellLayout.swift
@@ -0,0 +1,65 @@
+//
+// InterpretationTypeCellLayout.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/28/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+extension InterpretationTypeCell {
+ struct Constarints {
+
+ static let sidePadding = 16.adjustedByWidth
+ static let separatorHeight = 1
+
+ struct titleLabel {
+ static let topPadding = 8.adjustedByWidth
+ static let height = Text.defaultWhite.height
+ static let bottomPadding = 16.adjustedByWidth
+ }
+
+ struct descriptionLabel {
+ static let bottomPadding = 8.adjustedByWidth
+ }
+
+ struct priceTitleLabel {
+ static let height = Text.defaultWhite.height
+ static let rightPadding = 8.adjustedByWidth
+ static let bottomPadding = 8.adjustedByWidth
+ }
+
+ struct priceValueLabel {
+ static let height = Text.defaultRed.height
+ static let width = 50.adjustedByWidth
+ static let rightPadding = 8.adjustedByWidth
+ }
+
+ static var sumHeight: CGFloat {
+ let height = titleLabel.topPadding + titleLabel.height + titleLabel.bottomPadding + descriptionLabel.bottomPadding + priceTitleLabel.height + priceTitleLabel.bottomPadding + separatorHeight
+ return CGFloat(height)
+ }
+ }
+
+ // MARK: Text
+ struct Text {
+ struct defaultWhite {
+ static let height = 22.adjustedByWidth
+ static let font = UIFont(fontName: Constants.fonts.medium, height: CGFloat(height))
+ static let color = Constants.colors.white.getColor()
+ }
+
+ struct defaultGray {
+ static let height = 20.adjustedByWidth
+ static let font = UIFont(fontName: Constants.fonts.regular, height: CGFloat(height))
+ static let color = Constants.colors.gray.getColor()
+ }
+
+ struct defaultRed {
+ static let height = 22.adjustedByWidth
+ static let font = UIFont(fontName: Constants.fonts.medium, height: CGFloat(height))
+ static let color = Constants.colors.red.getColor()
+ }
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/View/TableView/Cell/InterpretationTypeCellModel.swift b/Nynja/Modules/InterpretationType/View/TableView/Cell/InterpretationTypeCellModel.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2a28e11d31796c94ec1549ad063ea5497144ff9e
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/View/TableView/Cell/InterpretationTypeCellModel.swift
@@ -0,0 +1,25 @@
+//
+// InterpretationTypeCellModel.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/28/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import NynjaUIKit
+
+final class InterpretationTypeCellModel: CellViewModel {
+ var accessibilityIdentifier: String {
+ return "InterpretationTypeCell"
+ }
+
+ let type: InterpretationType
+
+ init(type: InterpretationType) {
+ self.type = type
+ }
+
+ func setup(cell: InterpretationTypeCell) {
+ cell.setup(with: type)
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/View/TableView/InterpretationTypeTableDataSource.swift b/Nynja/Modules/InterpretationType/View/TableView/InterpretationTypeTableDataSource.swift
new file mode 100644
index 0000000000000000000000000000000000000000..3686b39b375295506bd8a0de344426c4ba013724
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/View/TableView/InterpretationTypeTableDataSource.swift
@@ -0,0 +1,29 @@
+//
+// InterpretationTypeTableDataSource.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/28/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+class InterpretationTypeTableDataSource: NSObject, UITableViewDataSource {
+
+ var rows: [InterpretationType] = [.general, .technology, .legal, .medical]
+
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return rows.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ return tableView.dequeueReusableCell(withModel: self.typeModel(at: indexPath), for: indexPath)
+
+ }
+
+ private func typeModel(at indexPath: IndexPath) -> InterpretationTypeCellModel {
+ let type = self.rows[indexPath.row]
+ return InterpretationTypeCellModel(type: type)
+ }
+
+}
diff --git a/Nynja/Modules/InterpretationType/View/TableView/InterpretationTypeTableDelegate.swift b/Nynja/Modules/InterpretationType/View/TableView/InterpretationTypeTableDelegate.swift
new file mode 100644
index 0000000000000000000000000000000000000000..d8ec049a6353b171f8fa5e46e15a83b132414a7c
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/View/TableView/InterpretationTypeTableDelegate.swift
@@ -0,0 +1,38 @@
+//
+// InterpretationTypeTableDelegate.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/28/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+class InterpretationTypeTableDelegate: NSObject, UITableViewDelegate {
+ typealias SelectAction = (InterpretationType) -> Void
+
+ var selectAction: SelectAction?
+
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ guard let item = self.self.getCurrentType(tableView: tableView, indexPath: indexPath) else { return }
+ selectAction?(item)
+ }
+
+ func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+ return self.getRowHeight(tableView: tableView, indexPath: indexPath)
+ }
+
+ func getRowHeight(tableView: UITableView, indexPath: IndexPath) -> CGFloat {
+ guard let item = self.getCurrentType(tableView: tableView, indexPath: indexPath) else { return 0 }
+ let width = tableView.frame.size.width - (2 * CGFloat(InterpretationTypeCell.Constarints.sidePadding))
+ let heightDescription = item.description.height(withConstrainedWidth: width, font: InterpretationTypeCell.Text.defaultGray.font!)
+
+ return InterpretationTypeCell.Constarints.sumHeight + heightDescription
+ }
+
+ private func getCurrentType(tableView: UITableView, indexPath: IndexPath) -> InterpretationType? {
+ guard let ds = tableView.dataSource as? InterpretationTypeTableDataSource else { return nil }
+ let item = ds.rows[indexPath.row]
+ return item
+ }
+}
diff --git a/Nynja/Modules/InterpretationType/Wireframe/InterpretationTypeWireFrame.swift b/Nynja/Modules/InterpretationType/Wireframe/InterpretationTypeWireFrame.swift
new file mode 100644
index 0000000000000000000000000000000000000000..c546098a656ee703fe771943af9c14fddcab0875
--- /dev/null
+++ b/Nynja/Modules/InterpretationType/Wireframe/InterpretationTypeWireFrame.swift
@@ -0,0 +1,43 @@
+//
+// InterpretationTypeWireFrame.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/26/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+final class InterpretationTypeWireFrame: InterpretationTypeWireFrameProtocol {
+
+ weak var navigation: UINavigationController?
+
+ func presentInterpretationType(navigation: UINavigationController, delegate: SelectInterpretationTypeDelegate) {
+ self.navigation = navigation
+
+ let view = InterpretationTypeViewController()
+ let presenter = InterpretationTypePresenter()
+ let interactor = InterpretationTypeInteractor()
+
+ // Connecting
+ let viewDependencies = InterpretationTypeViewController.Dependencies(presenter: presenter,
+ tableDataSource: InterpretationTypeTableDataSource(),
+ tableDelegate:InterpretationTypeTableDelegate())
+ view.inject(dependencies: viewDependencies)
+ let presenterDependencies = InterpretationTypePresenter.Dependencies(view: view,
+ interactor: interactor,
+ wireFrame: self,
+ delegate: delegate)
+ presenter.inject(dependencies: presenterDependencies)
+ let interactorDependencies = InterpretationTypeInteractor.Dependencies(presenter: presenter)
+ interactor.inject(dependencies: interactorDependencies)
+
+ view.modalTransitionStyle = .crossDissolve
+ view.modalPresentationStyle = .overCurrentContext
+ navigation.pushViewController(view as UIViewController, animated: true)
+ }
+
+ func dismiss() {
+ self.navigation?.popViewController(animated: true)
+ }
+}
diff --git a/Nynja/Modules/Main/Interactor/MainInteractor.swift b/Nynja/Modules/Main/Interactor/MainInteractor.swift
index f587c50d26852e966560197e8e441f5599a829eb..0f24383c634f7ec42272c16b138fac5b5ddaba94 100644
--- a/Nynja/Modules/Main/Interactor/MainInteractor.swift
+++ b/Nynja/Modules/Main/Interactor/MainInteractor.swift
@@ -8,7 +8,7 @@
import SDWebImage
-class MainInteractor: MainInteractorInputProtocol, VoxServiceDelegate, EditPhotoDelegate, MQTTServiceDelegate {
+class MainInteractor: MainInteractorInputProtocol, EditPhotoDelegate, MQTTServiceDelegate {
func startRinging() {
@@ -64,18 +64,10 @@ class MainInteractor: MainInteractorInputProtocol, VoxServiceDelegate, EditPhoto
cleanServices()
}
- func setVideoView(view: UIView?) {
- VoxService.sharedInstance.remoteView = view
- }
-
func deleteAccount() {
if let phone = StorageService.sharedInstance.phone, phone != "" {
MQTTService.sharedInstance.deleteUser(number: phone)
}
-
- VoxService.sharedInstance.voxClient.disconnect()
- VoxService.sharedInstance.isCallInProgress = false
-
cleanServices()
}
diff --git a/Nynja/Modules/Main/MainProtocols.swift b/Nynja/Modules/Main/MainProtocols.swift
index e038bed8e1f76dc3a19de16dc6422f16e4ca5fa3..17dc901021d1a958dae981b5f0984d581d1fdb9f 100644
--- a/Nynja/Modules/Main/MainProtocols.swift
+++ b/Nynja/Modules/Main/MainProtocols.swift
@@ -7,7 +7,6 @@
//
import UIKit
-import VoxImplant
protocol WheelOutProtocol: class {
func setEnableDoubleTap(enabled: Bool)
@@ -94,6 +93,7 @@ protocol MainWireFrameProtocol: class {
func getRecentsLocation() -> [LocationType]
func getStarredLocation() -> [LocationType]
func getRecentsMedia() -> [Media]
+ func openMarketplace()
// Group
func showAddParticipants()
@@ -231,7 +231,6 @@ protocol MainInteractorInputProtocol: class {
func call(name: String)
func videoCall(name: String)
func logout()
- func setVideoView(view: UIView?)
func deleteAccount()
func updateAvatar(url: URL)
func saveLogoutState()
diff --git a/Nynja/Modules/Main/Presenter/MainPresenter.swift b/Nynja/Modules/Main/Presenter/MainPresenter.swift
index 374f50643fddad96b0d8a24041bbadd8670e5ecf..d47d42ab1c7277e432e3d2652c6861a072d8c1bd 100644
--- a/Nynja/Modules/Main/Presenter/MainPresenter.swift
+++ b/Nynja/Modules/Main/Presenter/MainPresenter.swift
@@ -5,7 +5,6 @@
// Created by Bohdan Paliychuk on 19/07/2017.
// Copyright © 2017 TecSynt Solutions. All rights reserved.
//
-import VoxImplant
class MainPresenter: MainPresenterProtocol, MainInteractorOutputProtocol, ScheduleMessageDelegate, EditParticipantsDelegate {
@@ -16,7 +15,6 @@ class MainPresenter: MainPresenterProtocol, MainInteractorOutputProtocol, Schedu
weak var view: MainViewProtocol!
var interactor: MainInteractorInputProtocol!
var wireFrame: MainWireFrameProtocol!
- weak var call: VICall?
func showQRReader() {
wireFrame.showQRReader()
@@ -149,12 +147,13 @@ class MainPresenter: MainPresenterProtocol, MainInteractorOutputProtocol, Schedu
func showMessages(contact: Contact, call: NYNCall, callVC: CallInProgressViewProtocol, isVideo: Bool) {
if isVideo {
- if VoxService.sharedInstance.isRemoveVideoStream {
- view.showPartnerVideoViewWithPhotoURL(url: contact.avatarUrl)
- } else {
- let prevView = self.view.showPartnerVideoView()
- self.interactor.setVideoView(view: prevView)
- }
+ // if VoxService.sharedInstance.isRemoveVideoStream {
+// TODO: ASK ANGEL
+// view.showPartnerVideoViewWithPhotoURL(url: contact.avatarUrl)
+// } else {
+// let prevView = self.view.showPartnerVideoView()
+// self.interactor.setVideoView(view: prevView)
+// }
} else {
self.view.showReturnToCall(call: call)
}
diff --git a/Nynja/Modules/Main/View/MainViewController.swift b/Nynja/Modules/Main/View/MainViewController.swift
index bf0ea5a49738fffa868d28336fb2a794f5bea710..09f4b1e65a44936d4b3a74ed7e396493e66195d8 100644
--- a/Nynja/Modules/Main/View/MainViewController.swift
+++ b/Nynja/Modules/Main/View/MainViewController.swift
@@ -9,9 +9,9 @@
import NynjaUIKit
import UIKit
-import VoxImplant
import MobileCoreServices
import AssetsLibrary
+import AVFoundation
class MainViewController: BaseVC, MainViewProtocol, HitTestDelegate, UINavigationControllerDelegate, WheelPreviewProtocol {
private var cameraCoordinator: CameraFlowCoordinatorProtocol?
diff --git a/Nynja/Modules/Main/WireFrame/MainWireframe.swift b/Nynja/Modules/Main/WireFrame/MainWireframe.swift
index cadfc19e38691127e7481afb8837b3e69b653914..aa632f95085eaea0f278855606ddec2c5b1b7c77 100644
--- a/Nynja/Modules/Main/WireFrame/MainWireframe.swift
+++ b/Nynja/Modules/Main/WireFrame/MainWireframe.swift
@@ -7,19 +7,16 @@
//
import UIKit
-import VoxImplant
import CoreLocation
import SnapKit
-class MainWireFrame: MainWireFrameProtocol, VoxServiceDelegate, NynjaCommunicatorServiceDelegate {
+class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate {
weak var navigation : UINavigationController?
weak var contentNavigation: UINavigationController!
weak var view: MainViewController?
weak var messageinteractor: MessageInteractor?
- weak var call: VICall?
-
weak var external: EditParticipantsDelegate? = nil
func presentMain(navigation: UINavigationController, isRegistered: Bool, checkSession: Bool = false) {
@@ -73,7 +70,12 @@ class MainWireFrame: MainWireFrameProtocol, VoxServiceDelegate, NynjaCommunicato
}
func showContacts() {
- ContactsWireFrame().presentContacts(navigation: contentNavigation, mainWireFrame: self)
+ PermissionManager().requestContactsPermission { [weak self] status in
+ guard let `self` = self, status == .authorized else {
+ return
+ }
+ ContactsWireFrame().presentContacts(navigation: self.contentNavigation, mainWireFrame: self)
+ }
}
func showCreateConferenceCall() {
@@ -123,6 +125,12 @@ class MainWireFrame: MainWireFrameProtocol, VoxServiceDelegate, NynjaCommunicato
SelectCountryWireFrame().presentSelectCountry(navigation: navigation, main: self, selectCountryDelegate: selectCountryDelegate)
}
}
+
+ func openMarketplace() {
+ if let navigation = self.navigation {
+ MarketplaceWireFrame().presentMarketplace(navigation: navigation, main: self)
+ }
+ }
func showAddContactByUserName() {
if let navigation = self.contentNavigation {
@@ -211,28 +219,6 @@ class MainWireFrame: MainWireFrameProtocol, VoxServiceDelegate, NynjaCommunicato
messageinteractor?.sendTypingStatus(isTyping)
}
- func incomingCall(call: VICall, isVideo: Bool) {
- }
-
- func ringing(call: VICall) {
- }
-
- func callClosed(call: VICall, isError: Bool) {
- }
-
- func getNameFrom(call: VICall) -> Contact {
- let name = "main_undefined".localized
- var contact = Contact()
-
- if let voxId = call.voxId, let cont = ContactDAO.findContactBy(voxId: voxId) {
- contact = cont
- } else {
- contact.names = name
- }
-
- return contact
- }
-
var callInProgressVC :CallInProgressViewProtocol?
var isVideo: Bool = false
var isGroup: Bool = false
@@ -267,7 +253,6 @@ class MainWireFrame: MainWireFrameProtocol, VoxServiceDelegate, NynjaCommunicato
}
func viewShowed() {
- VoxService.sharedInstance.delegate = self
NynjaCommunicatorService.sharedInstance.delegate = self
}
diff --git a/Nynja/Modules/Marketplace/MarketplaceInteractor.swift b/Nynja/Modules/Marketplace/MarketplaceInteractor.swift
new file mode 100644
index 0000000000000000000000000000000000000000..8c338d31e746d3f0d35c516633bb11623c7979df
--- /dev/null
+++ b/Nynja/Modules/Marketplace/MarketplaceInteractor.swift
@@ -0,0 +1,25 @@
+//
+// MarketplaceInteractor.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/24/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class MarketplaceInteractor: MarketplaceInteractorInputProtocol {
+
+ weak var presenter: MarketplaceInteractorOutputProtocol!
+}
+
+extension MarketplaceInteractor: SetInjectable {
+ func inject(dependencies: MarketplaceInteractor.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: MarketplaceInteractorOutputProtocol
+ }
+}
+
diff --git a/Nynja/Modules/Marketplace/MarketplacePresenter.swift b/Nynja/Modules/Marketplace/MarketplacePresenter.swift
new file mode 100644
index 0000000000000000000000000000000000000000..9d284f264255ec8c29307ecd4d91345ad9367b97
--- /dev/null
+++ b/Nynja/Modules/Marketplace/MarketplacePresenter.swift
@@ -0,0 +1,42 @@
+//
+// MarketplacePresenter.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/24/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import Foundation
+
+final class MarketplacePresenter: BasePresenter, MarketplacePresenterProtocol, MarketplaceInteractorOutputProtocol {
+
+ weak var view: MarketplaceViewProtocol!
+ var interactor: MarketplaceInteractorInputProtocol!
+ var wireFrame: MarketplaceWireFrameProtocol!
+
+ func showed() {
+
+ }
+
+ func openMarketplaceMenu(type: CircleMenuItemType) {
+ self.wireFrame.openMarketplaceMenu(type: type)
+ }
+
+ func dismissMarketplace() {
+ self.wireFrame.dismissMarketplace()
+ }
+}
+
+extension MarketplacePresenter: SetInjectable {
+ func inject(dependencies: MarketplacePresenter.Dependencies) {
+ self.view = dependencies.view
+ self.interactor = dependencies.interactor
+ self.wireFrame = dependencies.wireFrame
+ }
+
+ struct Dependencies {
+ let view: MarketplaceViewProtocol
+ let interactor: MarketplaceInteractorInputProtocol
+ let wireFrame: MarketplaceWireFrameProtocol
+ }
+}
diff --git a/Nynja/Modules/Marketplace/MarketplaceProtocols.swift b/Nynja/Modules/Marketplace/MarketplaceProtocols.swift
new file mode 100644
index 0000000000000000000000000000000000000000..7b44670a3c3d6ab919c18c20ba24abbf5e4af688
--- /dev/null
+++ b/Nynja/Modules/Marketplace/MarketplaceProtocols.swift
@@ -0,0 +1,60 @@
+//
+// MarketplaceProtocols.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/24/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+protocol MarketplaceWireFrameProtocol: class {
+
+ func presentMarketplace(navigation: UINavigationController, main: MainWireFrame?)
+
+ /**
+ * Add here your methods for communication PRESENTER -> WIREFRAME
+ */
+ func openMarketplaceMenu(type: CircleMenuItemType)
+ func dismissMarketplace()
+}
+
+protocol MarketplaceViewProtocol: class {
+
+ var presenter: MarketplacePresenterProtocol! { get set }
+
+ /**
+ * Add here your methods for communication PRESENTER -> VIEW
+ */
+}
+
+protocol MarketplacePresenterProtocol: BasePresenterProtocol {
+
+ var view: MarketplaceViewProtocol! { get set }
+ var interactor: MarketplaceInteractorInputProtocol! { get set }
+ var wireFrame: MarketplaceWireFrameProtocol! { get set }
+
+ /**
+ * Add here your methods for communication VIEW -> PRESENTER
+ */
+
+ func showed()
+ func openMarketplaceMenu(type: CircleMenuItemType)
+ func dismissMarketplace()
+}
+
+protocol MarketplaceInteractorOutputProtocol: class {
+
+ /**
+ * Add here your methods for communication INTERACTOR -> PRESENTER
+ */
+}
+
+protocol MarketplaceInteractorInputProtocol: class {
+
+ var presenter: MarketplaceInteractorOutputProtocol! { get set }
+
+ /**
+ * Add here your methods for communication PRESENTER -> INTERACTOR
+ */
+}
diff --git a/Nynja/Modules/Marketplace/MarketplaceViewController.swift b/Nynja/Modules/Marketplace/MarketplaceViewController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..c630d082af795dce95acb7230ba1ea0dcbd79844
--- /dev/null
+++ b/Nynja/Modules/Marketplace/MarketplaceViewController.swift
@@ -0,0 +1,93 @@
+//
+// MarketplaceViewController.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/24/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+import SnapKit
+
+final class MarketplaceViewController: BaseVC, MarketplaceViewProtocol {
+
+ var presenter: MarketplacePresenterProtocol! {
+ didSet {
+ _presenter = presenter
+ }
+ }
+
+ private let initialMenuSet: CircleMenuSet = CircleMenuFactory().marketplace
+
+ lazy var circleMenu: CircleMenu = {
+ let menu = CircleMenu(rect: .zero, delegate: self, menuSet: initialMenuSet)
+ self.view.addSubview(menu)
+
+ let horizontalPaddingsSum: CGFloat = 2 * 16
+ let side = (self.view.bounds.width - horizontalPaddingsSum)
+
+ menu.snp.makeConstraints({ (make) in
+ make.centerX.centerY.equalToSuperview()
+ make.width.height.equalTo(side)
+ })
+ return menu
+ }()
+
+
+ // MARK: - Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ setupUI()
+ presenter.showed()
+
+ self.navigationView.configure(config: NavigationView.Config(
+ isVisibleSeparator: navigationView.isSeparatorVisible ?? false,
+ isVisibleBackButton: true,
+ title: title,
+ navigationHandler: self,
+ backButtonImage: UIImage(named:"ic_back_navigation")))
+ self.screenTitle = self.initialMenuSet.title
+ }
+
+
+ // MARK: - UI Setup
+
+ private func setupUI() {
+ self.circleMenu.isHidden = false
+ }
+}
+
+extension MarketplaceViewController: SetInjectable {
+ func inject(dependencies: MarketplaceViewController.Dependencies) {
+ self.presenter = dependencies.presenter
+ }
+
+ struct Dependencies {
+ let presenter: MarketplacePresenterProtocol
+ }
+}
+
+extension MarketplaceViewController: CircleMenuDelegate {
+ func didSelectItem(_ menu: CircleMenu, type: CircleMenuItemType) {
+ guard let menuSet = CircleMenuFactory().allElementsDictionary[type] else {
+ self.presenter.openMarketplaceMenu(type: type)
+ return
+ }
+ menu.updateMenu(with: menuSet)
+ }
+
+ func didChangeCurrentSet(_ menu: CircleMenu, title: String) {
+ self.screenTitle = title
+ }
+}
+
+extension MarketplaceViewController: NavigationProtocol {
+ func back() {
+ let canNavigateBackInsideControl = self.circleMenu.navigateBackInMenuStack()
+ if canNavigateBackInsideControl == false {
+ self.presenter.dismissMarketplace()
+ }
+ }
+}
diff --git a/Nynja/Modules/Marketplace/MarketplaceWireFrame.swift b/Nynja/Modules/Marketplace/MarketplaceWireFrame.swift
new file mode 100644
index 0000000000000000000000000000000000000000..b8df785b11a130cacd046261703355ca5aa120d4
--- /dev/null
+++ b/Nynja/Modules/Marketplace/MarketplaceWireFrame.swift
@@ -0,0 +1,50 @@
+//
+// MarketplaceWireFrame.swift
+// Nynja
+//
+// Created by Roman Chopovenko on 7/24/18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+final class MarketplaceWireFrame: MarketplaceWireFrameProtocol {
+
+ weak var navigation: UINavigationController?
+ weak var main: MainWireFrame?
+
+ func presentMarketplace(navigation: UINavigationController, main: MainWireFrame?) {
+ self.navigation = navigation
+ self.main = main
+
+ let view = MarketplaceViewController()
+ let presenter = MarketplacePresenter()
+ let interactor = MarketplaceInteractor()
+
+ // Connecting
+ let viewDependencies = MarketplaceViewController.Dependencies(presenter: presenter)
+ view.inject(dependencies: viewDependencies)
+ let presenterDependencies = MarketplacePresenter.Dependencies(view: view, interactor: interactor, wireFrame: self)
+ presenter.inject(dependencies: presenterDependencies)
+ let interactorDependencies = MarketplaceInteractor.Dependencies(presenter: presenter)
+ interactor.inject(dependencies: interactorDependencies)
+
+ view.modalTransitionStyle = .crossDissolve
+ view.modalPresentationStyle = .overCurrentContext
+ navigation.pushViewController(view as UIViewController, animated: true)
+ }
+
+ func openMarketplaceMenu(type: CircleMenuItemType) {
+ guard let navigation = self.navigation else { return }
+ switch type {
+ case .interpretation:
+ InterpretationWireFrame().presentInterpretation(navigation: navigation)
+ default:
+ break
+ }
+ }
+
+ func dismissMarketplace() {
+ navigation?.popToRootViewController(animated: true)
+ }
+}
diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor+Fetch.swift b/Nynja/Modules/Message/Interactor/MessageInteractor+Fetch.swift
index ea2f35beed8e0fe51539a366b49f018bce8aaa67..66b480fc7574265d9bce4aa7add82d1e4f129690 100644
--- a/Nynja/Modules/Message/Interactor/MessageInteractor+Fetch.swift
+++ b/Nynja/Modules/Message/Interactor/MessageInteractor+Fetch.swift
@@ -30,12 +30,18 @@ extension MessageInteractor {
return nil
}
- func fetchCheckpoint() {
+ private func fetchCheckpoint() {
if let feed = self.feed {
self.checkpoint = ChatCheckpointDAO.fetchCheckpoint(for: feed)
}
}
+ func fetchPenultimateMessage() {
+ if let type = fetchType {
+ penultimateMessage = MessageDAO.fetchPenultimateMessage(of: type)
+ }
+ }
+
// MARK: - Fetch Chat Model
func fetchRoomFromStorage() {
if let r = room, let id = r.id, let room = RoomDAO.findRoom(by: id) {
@@ -50,40 +56,26 @@ extension MessageInteractor {
// MARK: - Fetch History
func fetchFromStorage() {
- if let contact = self.contact {
- self.feed = FeedDAO.fetchP2p(for: contact.phoneId)
- fetchCheckpoint()
- } else if let room = self.room, let roomId = room.id {
- self.feed = FeedDAO.fetchMuc(for: roomId)
- fetchCheckpoint()
- }
+ prepareFeedAndCheckpoint()
guard let type = self.fetchType else {
- //TODO: - Error
return
}
- let messages = MessageDAO.fetchMessages(type)
-
+ let messages = fetchMessagesFromStorage(for: type)
self.configuration.messages = messages
- self.lastMessage = messages.last
-
self.prepareMessagesToUsing(type)
}
func fetchNewFromStorage() {
- if let contact = self.contact {
- self.feed = FeedDAO.fetchP2p(for: contact.phoneId)
- fetchCheckpoint()
- } else if let room = self.room, let roomId = room.id {
- self.feed = FeedDAO.fetchMuc(for: roomId)
- fetchCheckpoint()
- }
+ prepareFeedAndCheckpoint()
- guard let type = fetchType else { return }
+ guard let type = fetchType else {
+ return
+ }
let oldMessages = configuration.messages
- var newMessages = MessageDAO.fetchMessages(type)
+ var newMessages = fetchMessagesFromStorage(for: type)
if let startID = newMessages.first?.msg_id, oldMessages.index(where: { $0.msg_id == startID }) != nil {
// If all history fetched and the first message already exists in local data source.
@@ -107,6 +99,35 @@ extension MessageInteractor {
}
}
+ private func prepareFeedAndCheckpoint() {
+ if let contact = self.contact {
+ self.feed = FeedDAO.fetchP2p(for: contact.phoneId)
+ } else if let room = self.room, let roomId = room.id {
+ self.feed = FeedDAO.fetchMuc(for: roomId)
+ }
+
+ fetchCheckpoint()
+ }
+
+ private func fetchMessagesFromStorage(for type: FetchType) -> [Message] {
+ return MessageDAO
+ .fetchMessages(type)
+ .filter { message in
+ guard let desc = message.files?.first,
+ desc.mime == SendMessageType.audioCall.rawValue else {
+ return true
+ }
+
+ guard desc.data?.first?.key == FeatureKeys.File.Call.users.rawValue,
+ let ids = desc.data?.first?.value?.splitByComma(),
+ let phoneId = storageService.phoneId else {
+ return false
+ }
+
+ return ids.contains { $0 == phoneId }
+ }
+ }
+
// MARK: - Setup Configuration
private func prepareMessagesToUsing(_ type: FetchType, isNew: Bool = false) {
// Auto download/upload
diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift b/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift
index 759c8aa946b243d2c9b15403e786cb63e42f0caf..530802fb8daeeafcd6a401df7cdb56e33c76a1d6 100644
--- a/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift
+++ b/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift
@@ -16,7 +16,7 @@ extension MessageInteractor {
} else if let room = updatedRoom(with: info, type: type) {
handleUpdate(room: room)
} else if let message = changedMessage(with: info, type: type) {
- if info.kind == .delete {
+ if message.statusString == "deleted" {
handleMessageDelete(message)
} else {
handleMessageInsertOrUpdate(message)
@@ -114,6 +114,10 @@ extension MessageInteractor {
}
private func handleMessageInsertOrUpdate(_ message: Message) {
+ guard !["delete", "edit"].contains(message.statusString ?? "") else {
+ return
+ }
+
if let contact = self.contact, let phoneId = myContact?.phoneId {
let p2pFeed = p2p(firstId: phoneId, secondId: contact.phoneId)
@@ -167,7 +171,6 @@ extension MessageInteractor {
}
private func handleNewMessage(_ message: Message) {
- lastMessage = message
configuration.messages.append(message)
var config: MessageConfiguration
diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor+Utils.swift b/Nynja/Modules/Message/Interactor/MessageInteractor+Utils.swift
index a6ecf967043af6c6f541af116d58d1fb399d7545..7350d048b0f09717f89faaeb7d305cfabe88f745 100644
--- a/Nynja/Modules/Message/Interactor/MessageInteractor+Utils.swift
+++ b/Nynja/Modules/Message/Interactor/MessageInteractor+Utils.swift
@@ -8,7 +8,7 @@
extension MessageInteractor {
- func nextMessage(from serverId: Int64) -> Message? {
+ func nextMessage(from serverId: MessageServerId) -> Message? {
guard let index = configuration.messages.index(where: { message in
if let id = message.id {
return id > serverId
@@ -29,7 +29,7 @@ extension MessageInteractor {
return nil
}
- func messageBy(serverId: Int64) -> Message? {
+ func messageBy(serverId: MessageServerId) -> Message? {
if let index = indexOfMessage(with: serverId) {
return configuration.messages[index]
}
@@ -42,7 +42,7 @@ extension MessageInteractor {
return configuration.messages.index(where: { $0.msg_id == localId })
}
- func indexOfMessage(with serverId: Int64) -> Int? {
+ func indexOfMessage(with serverId: MessageServerId) -> Int? {
return configuration.messages.index(where: { $0.id == serverId })
}
diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor.swift b/Nynja/Modules/Message/Interactor/MessageInteractor.swift
index 7b5cce973e92b4f83a151bc42ab20c6a78d1be18..2fd3df63460cddbd7bde281b26944d7bee8c045f 100644
--- a/Nynja/Modules/Message/Interactor/MessageInteractor.swift
+++ b/Nynja/Modules/Message/Interactor/MessageInteractor.swift
@@ -11,7 +11,7 @@ import UIKit
import CoreLocation
final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, HistoryHandlerDelegate, TypingHandlerDelegate, IoHandlerDelegate, ReachabilityServiceObserver, MQTTServiceDelegate, MessageProcessingDelegate, MessageHandlerSubscriber, MessageInteractorCallProtocol {
-
+
private struct Constants {
static let messagesPageSize: Int64 = -15
}
@@ -51,9 +51,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
var room: Room? {
return chat as? Room
}
-
- var lastMessage: Message?
-
+
var myContact: Contact? {
return _contact
}
@@ -162,9 +160,9 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
}()
var initialMessage: ChatInitialMessage?
+ var penultimateMessage: Message?
// MARK: -- Private
- private var prevLastMessageId: Int64?
private lazy var _contact: Contact? = {
return ContactDAO.currentContact
@@ -174,7 +172,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
private var presenceTimer: TimerHandler?
- private var originMessagesIdOfReply: [Int64] = []
+ private var originMessagesIdOfReply: [MessageServerId] = []
let processingQueue = DispatchQueue(label: "\(Bundle.main.bundleIdentifier).message-interactor", qos: .default)
@@ -219,15 +217,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
// MARK: - BaseInteractor
override func loadData() {
super.loadData()
-
- if let room = self.room, let id = room.id, let kind = room.kind {
- fetchRoomFromStorage()
- mqttService.getRoom(id: id, kind: kind)
- fetchData()
- } else {
- fetchContact()
- fetchData()
- }
+ fetchData()
}
// MARK: - MessageInteractorInputProtocol
@@ -270,7 +260,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
// MARK: - History
- private func requestHistory(_ messageID: Int64?) {
+ private func requestHistory(_ messageID: MessageServerId?) {
processingQueue.async { [weak self] in
guard let `self` = self, let request = self.makeHistoryRequest(messageID) else {
return
@@ -278,8 +268,23 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
self.mqttService.sendHistoryRequest(with: request)
}
}
+
+ private func requestHistory(from: MessageServerId, to: MessageServerId) {
+ processingQueue.async { [weak self] in
+ guard let `self` = self,
+ let phoneId = self.storageService.phoneId,
+ let request = try? self.historyRequestFactory.makeHistoryRequestModel(rosterId: phoneId,
+ chat: self.chat,
+ from: from,
+ to: to) else {
+ return
+ }
+
+ self.mqttService.sendHistoryRequest(with: request)
+ }
+ }
- private func makeHistoryRequest(_ messageID: Int64?) -> HistoryRequestModel? {
+ private func makeHistoryRequest(_ messageID: MessageServerId?) -> HistoryRequestModel? {
guard let msgId = messageID ?? chat.last_msg?.id, let rosterId = storageService.phoneId else {
return nil
@@ -319,7 +324,50 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
}
// MARK: - Fetch Data
- func fetchData(messageID: Int64? = nil, isNew:Bool = false) {
+
+ func fetchData() {
+ fetchChatModel()
+ fetchPenultimateMessage()
+
+ if !hasGapInsideHistory() {
+ fetchMessages()
+ } else if let startId = chat.last_msg?.id, let endId = penultimateMessage?.id {
+ processingQueue.async { [weak self] in
+ guard let `self` = self else { return }
+ self.fetchFromStorage()
+ }
+ requestHistory(from: startId, to: endId)
+ }
+ }
+
+ private func hasGapInsideHistory() -> Bool {
+ guard chat.last_msg?.statusString != "update" else {
+ return false
+ }
+
+ guard let id = penultimateMessage?.id,
+ let lastId = chat.last_msg?.id,
+ id != lastId else {
+ return false
+ }
+
+ guard let prevId = penultimateMessage?.prev else {
+ return true
+ }
+
+ return prevId != lastId
+ }
+
+ func fetchChatModel() {
+ if let room = self.room, let id = room.id, let kind = room.kind {
+ fetchRoomFromStorage()
+ mqttService.getRoom(id: id, kind: kind)
+ } else {
+ fetchContact()
+ }
+ }
+
+ func fetchMessages(from messageID: MessageServerId? = nil, isNew: Bool = false) {
if !isNew {
processingQueue.async { [weak self] in
guard let `self` = self else { return }
@@ -328,19 +376,19 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
}
requestHistory(messageID)
}
-
-
+
+
// MARK: - Sender
func sender(for message: Message) -> MessageSender? {
let isOwner = message.from == myContact?.phone_id
if let _ = (message.feed_id as? muc), let from = message.from, let member = member(for: from) {
- return MessageSender(fullname: member.fullName ?? "", nick: member.alias, avatar: member.avatar)
+ return MessageSender(phoneId: member.phone_id, fullname: member.fullName ?? "", nick: member.alias, avatar: member.avatar)
} else if let contact = self.contact {
if isOwner, let myContact = myContact {
- return MessageSender(fullname: myContact.fullName ?? "", nick: myContact.nick, avatar: myContact.avatar)
+ return MessageSender(phoneId: myContact.phone_id, fullname: myContact.fullName ?? "", nick: myContact.nick, avatar: myContact.avatar)
} else {
- return MessageSender(fullname: contact.fullName ?? "", nick: contact.nick, avatar: contact.avatar)
+ return MessageSender(phoneId: contact.phone_id, fullname: contact.fullName ?? "", nick: contact.nick, avatar: contact.avatar)
}
}
@@ -740,8 +788,10 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
}
func deleteMessage(localId: String, forBoth: Bool) {
- guard let message = messageBy(localId: localId), let messageId = message.id, let phoneId = myContact?.phoneId else {
- return
+ guard let message = messageBy(localId: localId),
+ let messageId = message.id,
+ let phoneId = myContact?.phoneId else {
+ return
}
var messageAction: DBMessageAction
@@ -771,6 +821,8 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
let messageForDelete = messageFactory.makeMessageForDelete(message: message,
seenBy: [seenBy])
mqttService.sendMessage(message: messageForDelete)
+
+ messageForDelete.status = StringAtom(string: "deleted")
try? storageService.perform(action: .save, with: messageForDelete)
handleMessageDelete(message)
@@ -788,7 +840,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
repliedMessage = nil
}
- func processForwardMessageTap(serverId: Int64) {
+ func processForwardMessageTap(serverId: MessageServerId) {
guard let link = MessageDAO.fetchMessage(serverId: serverId)?.link,
let linkedMessage = MessageDAO.fetchMessage(serverId: link),
let localId = linkedMessage.msg_id else {
@@ -908,7 +960,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H
}
}
- private func notifyAboutRead(withOld oldReader: Int64?, new newReader: Int64?) {
+ private func notifyAboutRead(withOld oldReader: MessageServerId?, new newReader: MessageServerId?) {
guard let newReader = newReader,
oldReader != newReader,
let index = configuration.messages.index(where: { $0.id == newReader }),
@@ -1007,7 +1059,7 @@ extension MessageInteractor {
}
}
- private func readMessage(_ messageId: Int64) {
+ private func readMessage(_ messageId: MessageServerId) {
guard let rosterId = storageService.phoneId else {
return
}
@@ -1023,4 +1075,10 @@ extension MessageInteractor {
assertionFailure(error.localizedDescription)
}
}
+
+ func getMessageSenderContact(for sender: MessageSender) -> Contact? {
+ guard let phoneId = sender.phoneId else { return nil }
+ guard let contact = ContactDAO.findContactBy(phoneId: phoneId) else { return nil }
+ return contact
+ }
}
diff --git a/Nynja/Modules/Message/Presenter/MessagePresenter+MentionUnreadCounter.swift b/Nynja/Modules/Message/Presenter/MessagePresenter+MentionUnreadCounter.swift
index 6741129e497b14588c53edbebe849ae79a5333b0..f15d4013b625a704529b97613935dbe2ae84abb8 100644
--- a/Nynja/Modules/Message/Presenter/MessagePresenter+MentionUnreadCounter.swift
+++ b/Nynja/Modules/Message/Presenter/MessagePresenter+MentionUnreadCounter.swift
@@ -13,7 +13,7 @@ extension MessagePresenter {
// MARK: - Unread Counter
func prepareMentionedMessages(in room: Room) {
- guard let mentions = room.mentions?.compactMap({ Int64($0) }) else {
+ guard let mentions = room.mentions?.compactMap({ MessageServerId($0) }) else {
return
}
for mention in mentions {
@@ -22,7 +22,7 @@ extension MessagePresenter {
unreadMentionIds = mentions
}
- func mentionsCount(after serverMessageId: Int64) -> Int {
+ func mentionsCount(after serverMessageId: MessageServerId) -> Int {
let lastUnreadId = lastVisibleMessageId.map { max($0, serverMessageId) } ?? serverMessageId
defer {
lastVisibleMessageId = lastUnreadId
@@ -72,7 +72,7 @@ extension MessagePresenter {
lastUnreadMentionCount = 0
}
- private func nextUnreadMentionIdentifier(after lastVisibleMessageId: Int64?) -> Int64? {
+ private func nextUnreadMentionIdentifier(after lastVisibleMessageId: MessageServerId?) -> MessageServerId? {
if let lastId = lastVisibleMessageId {
guard let nextUnreadMentionedMessageIndex = unreadMentionIds.index(where: { $0 > lastId }) else {
return nil
@@ -87,9 +87,9 @@ extension MessagePresenter {
guard let mentions = room.mentions else {
return
}
- var unique: Set = []
+ var unique: Set = []
for case let mention in mentions {
- guard let id = Int64(mention) else {
+ guard let id = MessageServerId(mention) else {
continue
}
unique.insert(id)
diff --git a/Nynja/Modules/Message/Presenter/MessagePresenter.swift b/Nynja/Modules/Message/Presenter/MessagePresenter.swift
index e79f94391149517c4980088cfa2005feabe34407..bb5521ef787192202d6d57ce97ec1e37af4058b1 100644
--- a/Nynja/Modules/Message/Presenter/MessagePresenter.swift
+++ b/Nynja/Modules/Message/Presenter/MessagePresenter.swift
@@ -28,9 +28,9 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract
// -- unread mention counter
- var uniqueUnreadMentionIds: Set = []
- var unreadMentionIds: [Int64] = []
- var lastVisibleMessageId: Int64?
+ var uniqueUnreadMentionIds: Set = []
+ var unreadMentionIds: [MessageServerId] = []
+ var lastVisibleMessageId: MessageServerId?
var lastUnreadMentionCount = 0 {
didSet {
if oldValue != lastUnreadMentionCount, lastUnreadMentionCount == 0 {
@@ -402,7 +402,7 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract
view.scrollToMessage(with: localId)
}
- func forwardMessageTapped(serverId: Int64) {
+ func forwardMessageTapped(serverId: MessageServerId) {
self.interactor.processForwardMessageTap(serverId: serverId)
}
@@ -734,7 +734,7 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract
private func getCellModel(message: Message,
repliedModel: RepliedMessageModel?,
- readerID: Int64? = nil,
+ readerID: MessageServerId? = nil,
links: [String]? = nil,
mentions: [MentionInfo]? = nil,
translation: TranslationInfo? = nil,
@@ -838,7 +838,7 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract
return nil
}
- private func deliveryStatusFrom(readerID: Int64?, messageID: Int64?) -> DeliveryStatus {
+ private func deliveryStatusFrom(readerID: MessageServerId?, messageID: MessageServerId?) -> DeliveryStatus {
switch (readerID, messageID) {
case (.none, .some): return .sent
case (_, .none): return .unsent
@@ -955,7 +955,11 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract
func startSendingMessage() {
view.hideReplyPreview()
}
-
+
+ func handleOpponentAvatarTap(for sender: MessageSender) {
+ guard let contact = self.interactor.getMessageSenderContact(for: sender) else { return }
+ self.openSharedContact(contact: contact)
+ }
// MARK: - Utils
private func updateStatus(_ status: String) {
if internetStatus == .connected {
@@ -963,8 +967,8 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract
}
}
- func getPreviousMessages(id: Int64) {
- self.interactor.fetchData(messageID: id, isNew: true)
+ func getPreviousMessages(id: MessageServerId) {
+ self.interactor.fetchMessages(from: id, isNew: true)
}
func hasRunningCall() -> Bool {
@@ -974,6 +978,10 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract
func rejoinRunningCall() {
interactor.rejoinRunningCall()
}
+
+ func openMarketplaceScreen() {
+ self.wireFrame.openMarketplaceScreen()
+ }
}
diff --git a/Nynja/Modules/Message/Protocols/MessageProtocols.swift b/Nynja/Modules/Message/Protocols/MessageProtocols.swift
index 4ae1ebb15b659f5ca3c79688876178ba6ba40499..7f267f39a0e8677e4589f37f82a076efb4257d28 100644
--- a/Nynja/Modules/Message/Protocols/MessageProtocols.swift
+++ b/Nynja/Modules/Message/Protocols/MessageProtocols.swift
@@ -39,6 +39,7 @@ protocol MessageWireframeProtocol: DocumentInteractionInput {
func openChat(_ chat: ChatModel, originLocalId: String?)
func showLanguageSelector(input: LanguageSelector.Input)
func show(alert: UIAlertController)
+ func openMarketplaceScreen()
}
//MARK: Presenter -
@@ -74,7 +75,7 @@ protocol MessagePresenterProtocol: BasePresenterProtocol,
func saveImageToGallery(with fileUrl: URL)
func saveVideoToGallery(with fileUrl: URL)
- func getPreviousMessages(id: Int64)
+ func getPreviousMessages(id: MessageServerId)
func didCancelTapped(_ id: String)
func didContentLoadingRequested(_ id: String)
@@ -112,17 +113,18 @@ protocol MessagePresenterProtocol: BasePresenterProtocol,
func scrollToSelectedReplyMessage()
func scrollToSelectedEditMessage()
- func forwardMessageTapped(serverId: Int64)
+ func forwardMessageTapped(serverId: MessageServerId)
func clearEditMessageObject()
func declineReply()
func openURL(url: URL)
+ func handleOpponentAvatarTap(for sender: MessageSender)
func hasRunningCall() -> Bool
func rejoinRunningCall()
-
+ func openMarketplaceScreen()
}
//MARK: Interactor -
@@ -193,7 +195,7 @@ protocol MessageInteractorInputProtocol: BaseInteractorProtocol, MentionFetchInp
func configure()
func goAway()
- func fetchData(messageID: Int64?, isNew: Bool)
+ func fetchMessages(from messageID: MessageServerId?, isNew: Bool)
func sendTyping(_ type: TypingModelType)
func sendTypingStatus(_ isTyping: Bool)
@@ -224,7 +226,7 @@ protocol MessageInteractorInputProtocol: BaseInteractorProtocol, MentionFetchInp
func unstarMessage(localId: String)
func deleteMessage(localId: String, forBoth: Bool)
func prepareToReply(localId: String)
- func processForwardMessageTap(serverId: Int64)
+ func processForwardMessageTap(serverId: MessageServerId)
func declineReply()
func member(for phoneId: String) -> Member?
@@ -236,6 +238,8 @@ protocol MessageInteractorInputProtocol: BaseInteractorProtocol, MentionFetchInp
func editMessage(_ message: InputTextMessage)
func clearEditMessageObject()
func scheduleInfo(for message: InputScheduleMessage) -> ScheduleInfo?
+
+ func getMessageSenderContact(for sender: MessageSender) -> Contact?
func hasRunningCall() -> Bool
func rejoinRunningCall()
}
@@ -263,10 +267,9 @@ protocol MessageViewProtocol: class {
func showContextMenu(fromCell cell: BaseChatCell, convertingModel: ConvertionMessageModel?, targetView: UIView?)
- func scrollToBottomIfNeeded()
func scrollToMessage(with localId: String?)
- func scrollToUnreadMessage(with index: Int)
- func scrollToMessage(serverId: Int64)
+ func scrollToMessage(serverId: MessageServerId)
+ func scrollToBottomIfNeeded()
func scrollToBottom()
func updateHeaderStatus(_ status: String)
diff --git a/Nynja/Modules/Message/View/MessageVC+CellDelegate.swift b/Nynja/Modules/Message/View/MessageVC+CellDelegate.swift
index 78ff22af8b59185932c27fc75917fdb76704eb69..f4c5fe1054a1a0cd0188921fdbbe4b8777992105 100644
--- a/Nynja/Modules/Message/View/MessageVC+CellDelegate.swift
+++ b/Nynja/Modules/Message/View/MessageVC+CellDelegate.swift
@@ -12,11 +12,11 @@ extension MessageVC: BaseChatCellDelegate, AudioManagerDelegate, ProximitySensor
// MARK: - BaseChatCellDelegate
func didCellTapped(_ cell: BaseChatCell) {
- guard let model = cell.model else {
+ guard let model = cell.model, let type = model.type else {
return
}
-
- switch model.type! {
+
+ switch type {
case .image:
if let url = model.fileUrl {
let contentImageView = cell.transitionImageView
@@ -184,4 +184,16 @@ extension MessageVC: BaseChatCellDelegate, AudioManagerDelegate, ProximitySensor
currentPlayingModel?.notifyAudioHandler()
currentPlayingModel = nil
}
+
+ func updateCellIfVisible() {
+ if let model = currentPlayingModel {
+ reloadIfVisible(models: [model])
+ }
+ }
+
+ func opponentAvatarTapped(_ cell: BaseChatCell) {
+ inputBar.endEditing(true)
+ guard let sender = cell.model?.sender else { return }
+ self.presenter.handleOpponentAvatarTap(for: sender)
+ }
}
diff --git a/Nynja/Modules/Message/View/MessageVC+ContextMenuDelegate.swift b/Nynja/Modules/Message/View/MessageVC+ContextMenuDelegate.swift
index 5e6be46c7abd7d60870c9c3ed0314bcc44062ffe..90875402bb0c88d5be3eafad9a35847959b96196 100644
--- a/Nynja/Modules/Message/View/MessageVC+ContextMenuDelegate.swift
+++ b/Nynja/Modules/Message/View/MessageVC+ContextMenuDelegate.swift
@@ -161,6 +161,6 @@ extension MessageVC: NynjaContextMenuDelegate {
}
private func marketPlace(menu: NynjaContextMenu, userInfo: NynjaContextMenuUserInfo?) {
-
+ self.presenter.openMarketplaceScreen()
}
}
diff --git a/Nynja/Modules/Message/View/MessageVC.swift b/Nynja/Modules/Message/View/MessageVC.swift
index ba19708d1417bcdd5a0e7053e68b5719f474d995..db23d18357a445a17b3fac93cbc05e5c57ca79e0 100644
--- a/Nynja/Modules/Message/View/MessageVC.swift
+++ b/Nynja/Modules/Message/View/MessageVC.swift
@@ -18,7 +18,7 @@ fileprivate enum ButtonState {
case none
}
-final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSwipable {
+final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSwipable, CallInfoViewDelegate {
var presenter: MessagePresenterProtocol! {
didSet {
@@ -29,11 +29,9 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
var progressDictionary = [String: [(ProgressModel)->Void]]()
var loadingStatus = false
- var isHideKeyboard = true
- var verticalOffset: CGFloat = 0.0
- var messageDS: MessageDS!
- var messageTVDelegate: MessageTableViewDelegate!
+ var messageDS: MessageCollectionViewDataSource!
+ var messageTVDelegate: MessageCollectionViewDelegate!
var contextMenuPresented = false
@@ -67,6 +65,12 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
static let convertionModel = "convertionModel"
static let starId = "starID"
}
+
+ private let timeFormatter: DateFormatter = {
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateFormat = "YYYY-MM-dd"
+ return dateFormatter
+ }()
// MARK: - Views
private var bottomInset = Constraints.replyPreview.bottomInset
@@ -94,33 +98,35 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
make.left.right.equalToSuperview()
}
}
-
- private(set) lazy var tableView: UITableView = {
- let tv = UITableView()
-
- tv.separatorStyle = .none
- tv.backgroundColor = UIColor.clear
-
- registerCells(for: tv)
-
- tv.estimatedRowHeight = 0 // NOTE: It is important for scrolling behavior.
- tv.estimatedSectionHeaderHeight = 0
+
+ private let collectionViewLayout: UICollectionViewFlowLayout = {
+ let layout = MessageCollectionViewLayout()
+ layout.minimumLineSpacing = 0
+ return layout
+ }()
+
+ private let defaultCollectionViewInsets = UIEdgeInsets(top: Constraints.tableView.defaultVerticalInset,
+ left: 0,
+ bottom: Constraints.tableView.bottomInset,
+ right: 0).flippedVertically()
+
+ private(set) lazy var collectionView: UICollectionView = {
+ let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
+ collectionView.backgroundColor = .clear
+ collectionView.alwaysBounceVertical = true
+ collectionView.transform = CGAffineTransform(rotationAngle: .pi)
-
- tv.contentInset = UIEdgeInsets(
- top: Constraints.tableView.defaultVerticalInset,
- left: 0,
- bottom: Constraints.tableView.bottomInset,
- right: 0
- )
-
- self.view.addSubview(tv)
- tv.snp.makeConstraints { make in
+ collectionView.contentInset = defaultCollectionViewInsets
+
+ view.addSubview(collectionView)
+ collectionView.snp.makeConstraints { make in
makeDefaultTableConstraint(make)
make.bottom.equalTo(staticFooterView.snp.top)
}
-
- return tv
+
+ registerCells(for: collectionView)
+
+ return collectionView
}()
private(set) lazy var inputBar: InputBar = {
@@ -165,11 +171,6 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
self?.presenter.sendTyping(status)
}
- inputBar.changesHeightHandler = { [weak self] diff in
- guard let `self` = self else { return }
- self.tableView.contentInset.top -= diff
- self.tableView.contentOffset.y += diff
- }
inputBar.inputTypeChangeHandler = { [weak self] inputType in
self?.stickerInputState.performUpdates { state in
state.inputType = inputType.toggled()
@@ -229,7 +230,7 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
view.snp.makeConstraints({ (make) in
make.height.equalTo(gradientHeight)
make.left.right.equalToSuperview()
- make.bottom.equalTo(tableView.snp.bottom)
+ make.bottom.equalTo(collectionView.snp.bottom)
})
return view
@@ -294,7 +295,7 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
view.addSubview(searchView)
searchView.snp.makeConstraints { maker in
maker.left.right.equalToSuperview()
- maker.bottom.equalTo(tableView.snp.bottom)
+ maker.bottom.equalTo(collectionView.snp.bottom)
}
searchView.setupContentInset(to: gradientView)
@@ -346,7 +347,7 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
view.addSubview(mentionPanelView)
mentionPanelView.snp.makeConstraints { maker in
- maker.edges.equalTo(tableView)
+ maker.edges.equalTo(collectionView)
}
return mentionPanelView
@@ -369,6 +370,17 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
return counterView
}()
+
+
+ // MARK: - Calls
+
+ private weak var callInfoView: CallInfoView? {
+ didSet {
+ if callInfoView == nil {
+ oldValue?.removeFromSuperview()
+ }
+ }
+ }
// MARK: - Initialize
@@ -381,11 +393,11 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
audioManager.delegate = self
proximityManager.delegate = self
- messageDS = MessageDS(view: self)
- tableView.dataSource = messageDS
+ messageDS = MessageCollectionViewDataSource(view: self)
+ collectionView.dataSource = messageDS
- messageTVDelegate = MessageTableViewDelegate(view: self)
- tableView.delegate = messageTVDelegate
+ messageTVDelegate = MessageCollectionViewDelegate(view: self)
+ collectionView.delegate = messageTVDelegate
replyPreview.delegate = self
gradientView.isHidden = false
@@ -407,7 +419,7 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
mentionCounterView.isHidden = true
stickerSearchResultView.isHidden = true
- messageTVDelegate.rejoinBannerDisplayed = presenter.hasRunningCall()
+ displayRejoinBanner(display: presenter.hasRunningCall(), count: 0)
}
deinit {
@@ -418,21 +430,22 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
super.viewDidAppear(animated)
viewVisible = true
- NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)
- NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: .UIApplicationDidEnterBackground, object: nil)
+ let center = NotificationCenter.default
+ center.addObserver(self, selector: #selector(willEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)
+ center.addObserver(self, selector: #selector(didEnterBackground), name: .UIApplicationDidEnterBackground, object: nil)
- NotificationCenter.default.addObserver(self, selector: #selector(self.willResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
- NotificationCenter.default.addObserver(self, selector: #selector(self.didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
+ center.addObserver(self, selector: #selector(willResignActive), name: .UIApplicationWillResignActive, object: nil)
+ center.addObserver(self, selector: #selector(didBecomeActive), name: .UIApplicationDidBecomeActive, object: nil)
- tableView.layoutIfNeeded()
+ collectionView.layoutIfNeeded()
presenter.viewDidAppear()
}
- @objc func willResignActive() {
+ @objc private func willResignActive() {
goAway()
}
- @objc func didBecomeActive() {
+ @objc private func didBecomeActive() {
goAway()
}
@@ -458,13 +471,20 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
+
+ updateScrollIndicatorInsets()
- let expandedHeight = self.tableView.bounds.height + CGFloat(FooterViewLayout.collapsedHeight)
+ let expandedHeight = collectionView.bounds.height + CGFloat(FooterViewLayout.collapsedHeight)
footerView?.update(constraintLayout: CollapsedView.ConstraintLayout(availableHeight: [CGFloat(FooterViewLayout.collapsedHeight),
CGFloat(FooterViewLayout.middleHeight),
expandedHeight]))
}
+
+ private func updateScrollIndicatorInsets() {
+ let indicatorInset = collectionView.bounds.width - collectionView.scrollIndicatorInsets.left - 8
+ collectionView.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: indicatorInset)
+ }
// MARK: - BaseVC
@@ -480,38 +500,18 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
}
override func keyboardNotified(endFrame: CGRect) {
- let currentTableBounds = tableView.bounds
-
if endFrame.origin.y >= UIScreen.main.bounds.size.height {
- if !isHideKeyboard {
- let newTableBounds = currentTableBounds.updating(height: currentTableBounds.height + verticalOffset)
- adjustTableTopInset(for: newTableBounds)
-
- tableView.contentOffset = CGPoint(x: 0, y: tableView.contentOffset.y - verticalOffset)
- verticalOffset = 0
- }
- isHideKeyboard = true
- updateToHide()
-
+ updateToHide(view: inputBar, offset: 0)
} else {
- if isHideKeyboard {
- verticalOffset = CGFloat(endFrame.height) - safeAreaBottomInset()
- let newTableBounds = currentTableBounds.updating(height: currentTableBounds.height - verticalOffset)
- adjustTableTopInset(for: newTableBounds)
-
- tableView.contentOffset = CGPoint(x: 0, y: tableView.contentOffset.y + verticalOffset)
- }
- isHideKeyboard = false
- updateToShow(endFrame: endFrame)
+ updateToShow(view: inputBar, offset: -endFrame.height)
}
}
-
+
@objc private func wheelDidOpen() {
endInputBarInteraction()
}
-
//MARK: - Translation
private func handleUpdateForTranslation(in textView: UITextView) {
let inputText = textView.text ?? ""
@@ -670,9 +670,9 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
// MARK: - Mentions
func setupUnreadMentions(after index: Int) {
- var serverId = messageDS.cells[index].serverID
+ var serverId = messageDS.cellModel(at: index).serverID
- if serverId == nil, let previousModel = messageDS.cells[.. CGPoint {
- return CGPoint(x: tableView.bounds.width / 2,
- y: tableView.contentOffset.y + tableView.bounds.height - tableView.contentInset.bottom)
- }
-
func lastVisibleCellIndex() -> Int? {
- let point = lastMessagePosition()
- return tableView.indexPathForRow(at: point)?.row
+ let offset = messageDS.isReversed
+ ? collectionView.contentInset.top
+ : collectionView.bounds.height - collectionView.contentInset.bottom
+
+ let lastMessagePosition = CGPoint(
+ x: collectionView.bounds.width / 2,
+ y: collectionView.contentOffset.y + offset
+ )
+ return collectionView.indexPathForItem(at: lastMessagePosition)?.item
}
@@ -823,12 +825,9 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
}
func updateData(with configuration: DisplayChatConfiguration) {
- let isLastMessageVisible = self.isLastMessageVisible
-
- messageDS.cells = configuration.cells
- tableView.reloadData()
- adjustTableTopInset()
-
+ messageDS.update(configuration.cells)
+ collectionView.reloadData()
+
hasUnread = configuration.isUnreadShown
adjustPosition(with: configuration, isLastMessageVisible: isLastMessageVisible)
adjustButtonState(configuration)
@@ -836,13 +835,11 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
updateUnreadMentions()
}
- func updateNewData(_ cells: [BaseChatCellModel]) {
- var result = cells
-
+ func updateNewData(_ history: [BaseChatCellModel]) {
var lastTimeShowed: Date?
- for i in (0.. cells.count {
- let indexPath = IndexPath(row: cells.count, section: 0)
- let rect = tableView.rectForRow(at: indexPath)
- tableView.contentOffset.y = rect.minY + initialOffset.y - offsetForDeletedTimeCell
+ if !messageDS.isReversed, messageDS.count > history.count {
+ let indexPath = IndexPath(row: history.count, section: 0)
+ guard let attributes = collectionView.layoutAttributesForItem(at: indexPath) else {
+ return
+ }
+ let rect = attributes.frame
+ collectionView.contentOffset.y = rect.minY + initialOffset.y - offsetForDeletedTimeCell
}
loadingStatus = false
@@ -904,7 +899,8 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
case .message(let localId):
scrollToMessage(with: localId)
case .unread(let index):
- scrollToUnreadMessage(with: Int(index))
+ let index = messageDS.presentationIndex(from: index)
+ scrollToUnreadMessage(with: index)
case .checkpoint(let checkpoint):
scrollToCheckpoint(checkpoint)
case .lastMessage:
@@ -939,12 +935,11 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
presenter.openSharedContact(contact: contact)
}
- func getPreviousMessages(id: Int64) {
+ func getPreviousMessages(id: MessageServerId) {
presenter.getPreviousMessages(id: id)
}
func newMessage(_ cell: BaseChatCellModel) {
- let isLastMessageVisible = self.isLastMessageVisible
if let messageDS = self.messageDS {
var unreadCellIndex: Int?
@@ -952,33 +947,28 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
hasUnread = false
unreadCellIndex = messageDS.unreadCellIndex
}
-
+
readMessage(cell)
- tableView.performUpdates(animated: false) {
+ collectionView.performBatchUpdates({
if let unrIndex = unreadCellIndex {
- messageDS.cells.remove(at: unrIndex)
- tableView.deleteRows(at: [IndexPath(row: unrIndex, section: 0)], with: .none)
+ messageDS.remove(at: unrIndex)
+ collectionView.deleteItems(at: [IndexPath(row: unrIndex, section: 0)])
}
-
- messageDS.cells.append(cell)
- tableView.insertRows(at: [IndexPath(row: messageDS.cells.count - 1, section: 0)], with: .none)
- }
- adjustTableTopInset()
-
- if isLastMessageVisible || cell.isOwner {
- dispatchAsyncMainAfter(0.01, block: {
- self.scrollToBottom(animated: false)
- })
- } else {
- buttonState = .newMessages
- }
+ messageDS.addNewMessage(cell)
+ collectionView.insertItems(at: [IndexPath(row: messageDS.bottomIndex, section: 0)])
+
+ }, completion: { _ in
+ if self.isLastMessageVisible || cell.isOwner {
+ dispatchAsyncMainAfter(0.01) {
+ self.scrollToBottom(animated: false)
+ }
+ } else {
+ self.buttonState = .newMessages
+ }
+ })
}
}
-
- private var unreadCellIndex: Int? {
- return messageDS.cells.index(where: { $0.unread == true })
- }
func updateProgress(progressModel: ProgressModel?) {
DispatchQueue.main.async {
@@ -992,30 +982,38 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
func updateTranslationProgress(progressModel: ConvertionProgressModel?) {
DispatchQueue.main.async {
let id = progressModel?.id
- let list = self.messageDS.cells.filter { $0.id == id }
+ let list = self.messageDS.filter { $0.id == id }
list.forEach { $0.translationProgressModel = progressModel }
self.reloadIfVisible(models: list)
}
}
func updateMessage(_ model: BaseChatCellModel) {
- if let messageId = model.id, let index = messageDS.cells.index(where: { $0.id == messageId }) {
- model.deliveryStatus = messageDS.cells[index].deliveryStatus // TODO: castil
- messageDS.cells[index] = model
- //TODO: I think we need to do it outside this class
- var shouldReload = [model]
- messageDS.cells.forEach { (mod) in
- switch mod.messageType {
- case .reply(let replyModel, _):
- if replyModel.id == messageId && replyModel.type.rawValue == "text" {
- replyModel.text = model.text ?? ""
- shouldReload.append(mod)
- }
- default: break
+ guard let messageId = model.id, let index = messageDS.index(where: { $0.id == messageId }) else {
+ return
+ }
+ model.deliveryStatus = messageDS.cellModel(at: index).deliveryStatus // TODO: castil
+ messageDS.set(model, at: index)
+
+ //TODO: I think we need to do it outside this class
+ var shouldReload = [model]
+
+ for idx in messageDS.indices {
+ let cellModel = messageDS.cellModel(at: idx)
+
+ switch cellModel.messageType {
+ case let .reply(replyModel, _):
+ if replyModel.id == messageId && replyModel.type == .text {
+ // TODO: in this case we don'a actually need to reload cell, because height won't be changed.
+ // It would be better to update only cells layout.
+ replyModel.text = model.text ?? ""
+ shouldReload.append(cellModel)
}
+ default:
+ break
}
- reloadIfVisible(models: shouldReload)
}
+ reloadIfVisible(models: shouldReload)
}
func showContextMenu(fromCell cell: BaseChatCell, convertingModel: ConvertionMessageModel? = nil, targetView: UIView? = nil) {
@@ -1041,7 +1039,7 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
design: NynjaContextMenuItemsFactory.defaultDesign,
userInfo: itemsFactory.userInfo,
targetView: targetView,
- targetMask: tableView,
+ targetMask: collectionView,
containerViewMaskRect: menuMask)
let menu = NynjaContextMenu()
@@ -1100,18 +1098,15 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
}
func updateDeliveryStatus(_ status: DeliveryStatus, messageId: String) {
- let cells = self.messageDS.cells
-
- guard let index = cells.index(where: { $0.id == messageId }) else {
+ guard let index = messageDS.index(where: { $0.id == messageId }) else {
return
}
-
if status != .read {
- let model = cells[index]
+ let model = messageDS.cellModel(at: index)
model.deliveryStatus = status
reloadIfVisible(models: [model])
} else {
- markMesssagesAsRead(from: index, cells: cells)
+ markMesssagesAsRead(from: index)
}
}
@@ -1124,35 +1119,30 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
}
func updateStar(starID: String?, messageId: String) {
- let cells = self.messageDS.cells
-
- guard let index = cells.index(where: { $0.id == messageId }) else {
+ guard let model = messageDS.first(where: { $0.id == messageId }) else {
return
}
- let model = cells[index]
model.starID = starID
reloadIfVisible(models: [model])
}
func updateUnreadTitle(_ count: Int) {
- let index = min(messageDS.cells.count, count)
+ let index = messageDS.presentationIndex(from: min(messageDS.count, count))
let unreadIndex = messageDS.unreadCellIndex
-
- tableView.performUpdates {
+
+ collectionView.performBatchUpdates({
if let unreadIndex = unreadIndex {
- messageDS.cells.swapAt(unreadIndex, index)
- tableView.moveRow(at: makeIndexPathForUpdate(with: unreadIndex),
- to: makeIndexPathForUpdate(with: index))
+ messageDS.swapAt(unreadIndex, index)
+ collectionView.moveItem(at: makeIndexPathForUpdate(with: unreadIndex),
+ to: makeIndexPathForUpdate(with: index))
} else {
- messageDS.cells.insert(BaseChatCellModel.unreadModel(), at: index)
- tableView.insertRows(at: [makeIndexPathForUpdate(with: index)], with: .none)
+ messageDS.insert(.unreadModel(), at: index)
+ collectionView.insertItems(at: [makeIndexPathForUpdate(with: index)])
}
- }
- adjustTableTopInset()
- scrollToUnreadMessage(with: index + 1)
-
- let lastIndex = messageDS.cells.count - 1
- readMessage(messageDS.cells[lastIndex])
+ }, completion: { _ in
+ self.scrollToUnreadMessage(with: index + 1)
+ self.messageDS.bottomModel.map { self.readMessage($0) }
+ })
}
private func makeIndexPathForUpdate(with row: Int) -> IndexPath {
@@ -1160,39 +1150,56 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
}
// MARK: Scroll to message
+
func scrollToMessage(with localId: String?) {
- if let id = localId, let index = messageDS.cells.index(where: { $0.id == id }) {
- scrollToMessage(with: index)
- } else if !messageDS.cells.isEmpty {
+ scrollToMessage(with: localId, scrollPosition: .bottom)
+ }
+
+ func scrollToMessage(with localId: String?, scrollPosition: UICollectionViewScrollPosition) {
+ if let id = localId, let index = messageDS.index(where: { $0.id == id }) {
+ scrollToMessage(with: index, scrollPosition: scrollPosition)
+ } else if !messageDS.isEmpty {
scrollToBottom()
}
}
private func scrollToCheckpoint(_ checkpoint: ChatCheckpoint) {
- if canScroll {
- scrollToMessage(with: checkpoint.localId)
- if checkpoint.topOffset > 0 {
- var contentOffset = tableView.contentOffset.y + CGFloat(checkpoint.topOffset)
- contentOffset = min(contentOffset, tableView.contentSize.height - tableView.bounds.height)
- tableView.contentOffset.y = contentOffset + (gradientHeight - tableView.contentInset.bottom)
- }
+ collectionView.layoutIfNeeded()
+ guard let index = messageDS.index(where: { $0.id == checkpoint.localId }) else {
+ return
}
+ let indexPath = IndexPath(item: index, section: 0)
+
+ guard let attributes = collectionView.layoutAttributesForItem(at: indexPath) else {
+ return
+ }
+
+ var contentOffset = attributes.frame.minY
+ contentOffset = contentOffset - CGFloat(checkpoint.topOffset)
+ contentOffset = max(-collectionView.contentInset.top,
+ min(contentOffset, collectionView.contentSize.height - collectionView.bounds.height))
+
+ collectionView.contentOffset.y = contentOffset
}
- func scrollToMessage(with index: Int) {
+ private func scrollToMessage(with index: Int, scrollPosition: UICollectionViewScrollPosition = .bottom) {
let indexPath = IndexPath(row: index, section: 0)
- scroll(to: indexPath, at: .top, animated: false)
+ scroll(to: indexPath, at: scrollPosition, animated: false)
}
- func scrollToUnreadMessage(with index: Int) {
- let prevInd = index - 1
- let cellModel = messageDS.cells[safe: prevInd]
- guard cellModel?.unread == true else { return }
+ private func scrollToUnreadMessage(with index: Int) {
+ guard case let prevInd = index - 1, messageDS.indices.contains(prevInd) else {
+ return
+ }
+ let cellModel = messageDS.cellModel(at: prevInd)
+ guard cellModel.unread == true else {
+ return
+ }
scrollToMessage(with: prevInd)
}
- func scrollToMessage(serverId: Int64) {
- guard let index = messageDS.cells.index(where: { $0.serverID == serverId }) else {
+ func scrollToMessage(serverId: MessageServerId) {
+ guard let index = messageDS.index(where: { $0.serverID == serverId }) else {
return
}
scrollToMessage(with: index)
@@ -1252,24 +1259,30 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
}
func displayRejoinBanner(display: Bool, count: Int) {
- self.messageTVDelegate.rejoinBannerDisplayed = display
- self.messageTVDelegate.membersCount = count
- self.tableView.reloadData()
- adjustTableTopInset()
+ // Use bottom inset, because collectionView has reversed transform:
+ // collectionView.transform = CGAffineTransform(rotationAngle: .pi)
+ var inset = defaultCollectionViewInsets.bottom
+
+ if display {
+ let callInfoView = self.callInfoView ?? makeRejoinBar()
+ callInfoView.membersCount = count
+ callInfoView.delegate = self
+ self.callInfoView = callInfoView
+
+ inset += CallInfoView.Constraints.baseSizes.height
+ } else {
+ callInfoView = nil
+ }
+
+ if collectionView.contentInset.bottom != inset {
+ collectionView.contentInset.bottom = inset
+ }
}
private func toggleReplyPreview(_ shouldShow: Bool) {
let isShown = !replyPreview.isHidden
guard shouldShow != isShown else { return }
- let replyPreviewHeight = CGFloat(Constraints.replyPreview.height)
- if shouldShow {
- tableView.contentOffset.y += replyPreviewHeight
- tableView.contentInset.top -= replyPreviewHeight
- } else {
- tableView.contentInset.top += replyPreviewHeight
- }
-
replyPreview.isHidden = !shouldShow
if shouldShow {
@@ -1356,63 +1369,71 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
})
}
- private func registerCells(for tableView: UITableView) {
+ private func registerCells(for collectionView: UICollectionView) {
MessageCellFactory.selfIdentifiers.forEach {
- tableView.register(BaseChatCell.self, forCellReuseIdentifier: $0)
+ collectionView.register(BaseChatCell.self, forCellWithReuseIdentifier: $0)
}
MessageCellFactory.opponenetIdentifiers.forEach {
- tableView.register(OponentChatCell.self, forCellReuseIdentifier: $0)
+ collectionView.register(OponentChatCell.self, forCellWithReuseIdentifier: $0)
}
- tableView.register(SystemCell.self, forCellReuseIdentifier: "system")
- tableView.register(TimeCell.self, forCellReuseIdentifier: "time")
- tableView.register(UnreadCell.self, forCellReuseIdentifier: "unread")
+ collectionView.register(SystemCell.self, forCellWithReuseIdentifier: "system")
+ collectionView.register(TimeCell.self, forCellWithReuseIdentifier: "time")
+ collectionView.register(UnreadCell.self, forCellWithReuseIdentifier: "unread")
}
func reloadIfVisible(models: [BaseChatCellModel]) {
- tableView.performUpdates {
+ collectionView.performBatchUpdates({
var indexPaths: [IndexPath] = []
- tableView.visibleCells.forEach { (cell) in
- if let model = (cell as? BaseChatCell)?.model {
- if let index = models.index(where: { (mod) -> Bool in
- if mod.id != nil {
- return mod.id == model.id
- }
-
- if mod.text != nil {
- return mod.text == model.text
- }
-
- return false
- }) {
- (cell as? BaseChatCell)?.updateData(models[index])
- indexPaths.append(tableView.indexPath(for: cell)!)
+ collectionView.visibleCells.forEach { (cell) in
+ guard let cell = cell as? BaseChatCell, let model = cell.model else {
+ return
+ }
+ if models.contains(where: { (mod) -> Bool in
+ if mod.id != nil {
+ return mod.id == model.id
+ }
+
+ if mod.text != nil {
+ return mod.text == model.text
+ }
+
+ return false
+ }) {
+ if let indexPath = collectionView.indexPath(for: cell) {
+ indexPaths.append(indexPath)
}
}
}
- tableView.reloadRows(at: indexPaths, with: .none)
- }
- adjustTableTopInset()
+ collectionView.reloadItems(at: indexPaths)
+ }, completion: nil)
}
- private func markMesssagesAsRead(from index: Int, cells: [BaseChatCellModel]) {
+ private func markMesssagesAsRead(from index: Int) {
var updatedCells: [BaseChatCellModel] = []
- for i in (0...index).reversed() where cells[i].isOwner {
- let model = cells[i]
- if model.deliveryStatus == .read {
+
+ for i in (0...index).reversed() {
+ let i = messageDS.presentationIndex(from: i)
+ guard case let model = messageDS.cellModel(at: i), model.isOwner else {
+ continue
+ }
+
+ guard model.deliveryStatus != .read else {
break
- } else {
- model.deliveryStatus = .read
- updatedCells.append(model)
}
- reloadIfVisible(models: updatedCells)
+
+ model.deliveryStatus = .read
+ updatedCells.append(model)
}
+ reloadIfVisible(models: updatedCells)
}
func scrollToBottomIfNeeded() {
- guard let model = messageDS.cells.last, bottomGap < messageTVDelegate.height(for: model) else { return }
- dispatchAsyncMainAfter(0.01, block: {
+ guard let model = messageDS.bottomModel, bottomGap < messageTVDelegate.height(for: model) else {
+ return
+ }
+ dispatchAsyncMainAfter(0.01) {
self.scrollToBottom(animated: true)
- })
+ }
}
func scrollToBottom() {
@@ -1420,58 +1441,56 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
}
func scrollToBottom(animated: Bool) {
- let indexPath = IndexPath(row: messageDS.cells.count - 1, section: 0)
- scroll(to: indexPath, at: .bottom, animated: animated)
+ guard case let index = messageDS.bottomIndex, messageDS.indices.contains(index) else {
+ return
+ }
+ let indexPath = IndexPath(row: index, section: 0)
+ scroll(to: indexPath, at: .top, animated: animated)
+ }
+
+ func scrollToTop() {
+ guard case let index = messageDS.topIndex, messageDS.indices.contains(index) else {
+ return
+ }
+ let indexPath = IndexPath(row: index, section: 0)
+ scroll(to: indexPath, at: .bottom, animated: true)
}
- private func scroll(to indexPath: IndexPath, at position: UITableViewScrollPosition, animated: Bool) {
+ private func scroll(to indexPath: IndexPath, at position: UICollectionViewScrollPosition, animated: Bool) {
guard !contextMenuPresented, canScroll else {
return
}
- tableView.scrollToRow(at: indexPath, at: position, animated: animated)
+ collectionView.scrollToItem(at: indexPath, at: position, animated: animated)
}
private var canScroll: Bool {
- return canScroll(with: tableView.bounds)
+ return canScroll(with: collectionView.bounds)
}
private func canScroll(with bounds: CGRect) -> Bool {
- return tableView.contentSize.height > bounds.height
+ return collectionView.contentSize.height > bounds.height
}
private var checkpoint: ChatCheckpoint? {
- let visibleCells = tableView.visibleCells
- guard let index = visibleCells.index(where: { $0 is BaseChatCell }),
- let cell = visibleCells[index] as? BaseChatCell,
- let model = cell.model, let localId = model.id else {
- return nil
+ let visibleCells = collectionView.visibleCells
+ let bottomCell = visibleCells
+ .sorted(by: { $0.frame.minY < $1.frame.minY })
+ .first(where: { $0 is BaseChatCell })
+
+ guard let cell = bottomCell as? BaseChatCell, let model = cell.model, let localId = model.id else {
+ return nil
}
- let rect = tableView.convert(cell.frame, to: tableView.superview)
- let offset = tableView.frame.origin.y - rect.origin.y
+ let rect = collectionView.convert(cell.frame, to: collectionView.superview)
+ let offset = collectionView.frame.maxY - rect.maxY
return ChatCheckpoint(localId: localId, topOffset: Double(offset))
}
- private var localIdOfFirstVisibleMessage: String? {
- var messageCell: BaseChatCell?
-
- for cell in tableView.visibleCells {
- if let chatCell = cell as? BaseChatCell {
- messageCell = chatCell
- break
- }
- }
-
- if let cell = messageCell, let model = cell.model {
- return model.id
- }
-
- return nil
- }
-
private var bottomGap: CGFloat {
- return tableView.contentSize.height - (tableView.contentOffset.y + tableView.bounds.height)
+ return messageDS.isReversed
+ ? collectionView.contentOffset.y
+ : collectionView.contentSize.height - (collectionView.contentOffset.y + collectionView.bounds.height)
}
private var isLastMessageVisible: Bool {
@@ -1499,22 +1518,18 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw
}
func removeMessage(_ messageId: String) {
- guard let messageDS = self.messageDS, let index = messageDS.cells.index(where: { $0.id == "\(messageId)" }) else {
+ guard let messageDS = self.messageDS, let index = messageDS.index(where: { $0.id == messageId }) else {
return
}
- tableView.performUpdates {
- messageDS.cells.remove(at: index)
- tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .none)
- }
- adjustTableTopInset()
+ collectionView.performBatchUpdates({
+ messageDS.remove(at: index)
+ collectionView.deleteItems(at: [IndexPath(row: index, section: 0)])
+ }, completion: nil)
}
func getNextPage() {
- for i in 0.. CallInfoView {
+ let callInfoView = CallInfoView()
+
+ view.addSubview(callInfoView)
+ callInfoView.snp.makeConstraints { maker in
+ maker.top.equalTo(collectionView.snp.top)
+ maker.left.right.equalToSuperview()
+ maker.height.equalTo(CallInfoView.Constraints.baseSizes.height)
}
- }
-
- //MARK: - Rejoin bar
- func rejoinRunningCall() {
- presenter.rejoinRunningCall()
+
+ return callInfoView
}
}
-//MARK: - Translation
+// MARK: - Translation
extension MessageVC {
func showTranslationPreview(_ selectedLang: SelectedLang, isAuto: Bool) {
guard case .lang(let language) = selectedLang, !language.isNone else {
@@ -1612,7 +1620,7 @@ extension MessageVC {
make.height.equalTo(0)
}
- tableView.snp.remakeConstraints { make in
+ collectionView.snp.remakeConstraints { make in
makeDefaultTableConstraint(make)
make.bottom.equalTo(staticFooterView.snp.top)
}
@@ -1625,7 +1633,7 @@ extension MessageVC {
staticFooterView.snp.updateConstraints { make in
make.height.equalTo(gradientHeight)
}
- tableView.snp.remakeConstraints { make in
+ collectionView.snp.remakeConstraints { make in
makeDefaultTableConstraint(make)
make.bottom.equalTo(staticFooterView.snp.top).offset(-FooterViewLayout.collapsedHeight)
}
@@ -1647,7 +1655,7 @@ extension MessageVC {
}
}
-//MARK: - UI configuration
+// MARK: - Translation UI configuration
private extension MessageVC {
func makeFooterView(on view: UIView, bottom: UIView) -> CollapsedView {
let footerView = CollapsedView()
diff --git a/Nynja/Modules/Message/View/Views/CallInfoView/CallInfoView.swift b/Nynja/Modules/Message/View/Views/CallInfoView/CallInfoView.swift
index f3e197a0d8186f8fc99c4472d410dddcbe8aafe2..420a2d7875667c79288fcd9203d3b7504abb55db 100644
--- a/Nynja/Modules/Message/View/Views/CallInfoView/CallInfoView.swift
+++ b/Nynja/Modules/Message/View/Views/CallInfoView/CallInfoView.swift
@@ -16,9 +16,15 @@ class CallInfoView: UIView {
weak var delegate: CallInfoViewDelegate?
- var membersCount: Int = 0
+ var membersCount: Int = 0 {
+ didSet {
+ participantsCount.text = text(for: membersCount)
+ }
+ }
+
// MARK: - Views
+
lazy var headsetImage: UIImageView = {
let img = UIImageView()
img.contentMode = .scaleAspectFill
@@ -78,7 +84,7 @@ class CallInfoView: UIView {
pc.backgroundColor = Constants.colors.sectionBackgroundColor.getColor()
pc.textAlignment = .left
pc.numberOfLines = 1
- pc.text = String(format: "call_info_banner_members".localized, self.membersCount)
+ pc.text = text(for: membersCount)
self.labelsView.addSubview(pc)
pc.snp.makeConstraints({ (make) in
@@ -119,16 +125,22 @@ class CallInfoView: UIView {
baseSetup()
}
+
// MARK: - Setup
+
private func baseSetup() {
durationLabel.isHidden = false
self.backgroundColor = Constants.colors.sectionBackgroundColor.getColor()
}
- //MARK: actions
+ private func text(for membersCount: Int) -> String {
+ return String(format: "call_info_banner_members".localized, membersCount)
+ }
- @objc func joinButtonPressed() {
-
+
+ // MARK: - Actions
+
+ @objc private func joinButtonPressed() {
self.delegate?.didPressButtonJoinIn(callInfoView: self)
}
}
diff --git a/Nynja/Modules/Message/View/Views/CollectionView/MessageCollectionViewDataSource.swift b/Nynja/Modules/Message/View/Views/CollectionView/MessageCollectionViewDataSource.swift
new file mode 100644
index 0000000000000000000000000000000000000000..46b415a86292f2a25e3190da0821fe46f9cb20ee
--- /dev/null
+++ b/Nynja/Modules/Message/View/Views/CollectionView/MessageCollectionViewDataSource.swift
@@ -0,0 +1,180 @@
+//
+// MessageCollectionViewDataSource.swift
+// Nynja
+//
+// Created by Anton Poltoratskyi on 08.08.2018.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+final class MessageCollectionViewDataSource: NSObject {
+
+ unowned var view: MessageVC
+
+ private var cells = [BaseChatCellModel]()
+
+ init(view: MessageVC) {
+ self.view = view
+ super.init()
+ }
+
+
+ // MARK: - Data
+
+ let isReversed = true
+
+ var isEmpty: Bool {
+ return cells.isEmpty
+ }
+
+ var count: Int {
+ return cells.count
+ }
+
+ var indices: CountableRange {
+ return 0.. Int {
+ return isReversed ? count : cells.count - count
+ }
+
+ func index(where predicate: (BaseChatCellModel) -> Bool) -> Int? {
+ return isReversed ? cells.reversed().index(where: predicate) : cells.index(where: predicate)
+ }
+
+ func first(where predicate: (BaseChatCellModel) -> Bool) -> BaseChatCellModel? {
+ return isReversed ? cells.reversed().first(where: predicate) : cells.first(where: predicate)
+ }
+
+ func filter(where predicate: (BaseChatCellModel) -> Bool) -> [BaseChatCellModel] {
+ return isReversed ? cells.reversed().filter(predicate) : cells.filter(predicate)
+ }
+
+ func cellModel(at indexPath: IndexPath) -> BaseChatCellModel {
+ return cellModel(at: indexPath.row)
+ }
+
+ func cellModel(at index: Int) -> BaseChatCellModel {
+ return cells[dataIndex(from: index)]
+ }
+
+ func cellModel(before index: Int, where predicate: (BaseChatCellModel) -> Bool) -> BaseChatCellModel? {
+ let index = dataIndex(from: index)
+ return cells[.. Int64? {
+ return cells.first { $0.serverID != nil }.flatMap { $0.serverID }
+ }
+
+ private func dataIndex(from presentationIndex: Int) -> Int {
+ return cells.count - presentationIndex - 1
+ }
+
+ func presentationIndex(from dataIndex: Int) -> Int {
+ return cells.count - dataIndex - 1
+ }
+}
+
+// MARK: - UICollectionViewDataSource
+
+extension MessageCollectionViewDataSource: UICollectionViewDataSource {
+
+ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+ return count
+ }
+
+ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+ let model = cellModel(at: indexPath)
+
+ if model.unread != nil {
+ if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "unread", for: indexPath) as? UnreadCell {
+ cell.setup()
+ cell.accessibilityIdentifier = "chat_cell_unread_\(indexPath.section)"
+ return cell
+ }
+ }
+
+ if model.messageType == .system {
+ if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "system", for: indexPath) as? SystemCell {
+ cell.setup(message: model.text!)
+ cell.accessibilityIdentifier = "system_cell_unread_\(indexPath.section)"
+ return cell
+ }
+ } else if model.time == nil {
+ var cell: BaseChatCell!
+
+ if let identifier = MessageCellFactory.identifier(for: model) {
+ cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as? BaseChatCell
+ cell.accessibilityIdentifier = "message_cell_\(identifier)_\(indexPath.section)"
+ }
+
+ if view.messageTVDelegate != nil {
+ cell.delegate = view
+ }
+ cell.setup(model: model)
+ return cell
+ } else {
+ if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "time", for: indexPath) as? TimeCell {
+ cell.setup(date: model.time!)
+ cell.accessibilityIdentifier = "time_cell_unread_\(indexPath.section)"
+ return cell
+ }
+ }
+
+ return UICollectionViewCell()
+ }
+}
diff --git a/Nynja/Modules/Message/View/Views/CollectionView/MessageCollectionViewDelegate.swift b/Nynja/Modules/Message/View/Views/CollectionView/MessageCollectionViewDelegate.swift
new file mode 100644
index 0000000000000000000000000000000000000000..10275391e447ad1404cf5fed8741316095d37b9a
--- /dev/null
+++ b/Nynja/Modules/Message/View/Views/CollectionView/MessageCollectionViewDelegate.swift
@@ -0,0 +1,149 @@
+//
+// MessageCollectionViewDelegate.swift
+// Nynja
+//
+// Created by Anton Poltoratskyi on 08.08.2018.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+enum ScrollDirection {
+ case top
+ case bottom
+}
+
+final class MessageCollectionViewDelegate: NSObject, UICollectionViewDelegateFlowLayout {
+
+ unowned var view: MessageVC
+
+ init(view: MessageVC) {
+ self.view = view
+ super.init()
+ }
+
+
+ // MARK: - Collection View
+
+ func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+ setupProgress(cell: cell)
+ setupMentions(cell: cell, indexPath: indexPath)
+ }
+
+ func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+ guard let cell = cell as? BaseChatCell, let url = cell.model?.progressModel?.url.absoluteString else {
+ return
+ }
+ view.progressDictionary.removeValue(forKey: url)
+ }
+
+ func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+ return CGSize(width: view.view.bounds.width, height: heightForItem(at: indexPath))
+ }
+
+ private func heightForItem(at indexPath: IndexPath) -> CGFloat {
+ let model = view.messageDS.cellModel(at: indexPath)
+
+ if model.messageType == .system {
+ return 40
+ }
+
+ if let _ = model.type {
+ return height(for: model)
+ }
+
+ if model.unread != nil {
+ return 30
+ }
+
+ if model.time == nil {
+ return height(for: model)
+ } else {
+ return 30
+ }
+ }
+
+ func height(for model: BaseChatCellModel) -> CGFloat {
+ return model.isOwner ? BaseChatCell.size(for: model).height : OponentChatCell.size(for: model).height
+ }
+
+
+ // MARK: - Scroll View
+
+ private var scrollOffset: CGFloat = 0
+
+ var lastVisibleIndex: Int?
+
+ func scrollViewDidScroll(_ scrollView: UIScrollView) {
+ let offset = view.messageDS.isReversed
+ ? scrollView.contentSize.height - scrollView.bounds.height - scrollView.contentOffset.y
+ : scrollView.contentOffset.y
+
+ let absoluteTop: CGFloat = 0
+ let absoluteBottom: CGFloat = scrollView.contentSize.height - scrollView.frame.size.height
+
+ let isTop = scrollOffset - offset > 0 && offset > absoluteTop
+ let isBottom = scrollOffset - offset < 0 && offset < absoluteBottom
+
+ if isTop {
+ view.isScrollingTo(.top)
+ } else if isBottom {
+ view.isScrollingTo(.bottom)
+ }
+
+ if offset >= absoluteBottom {
+ view.didReachBottom()
+ }
+
+ let contentHeight = scrollView.contentSize.height
+
+ if offset < 0 && scrollView.frame.size.height < contentHeight {
+ loadData()
+ }
+ scrollOffset = offset
+ }
+
+ func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
+ view.scrollToTop()
+ return false
+ }
+
+
+ // MARK: - Private
+
+ private func setupMentions(cell: UICollectionViewCell, indexPath: IndexPath) {
+ func setup() {
+ lastVisibleIndex = indexPath.row
+ view.setupUnreadMentions(after: indexPath.row)
+ }
+ guard let index = lastVisibleIndex ?? view.lastVisibleCellIndex() else {
+ setup()
+ return
+ }
+ guard view.messageDS.isReversed ? indexPath.row < index : indexPath.row > index else {
+ return
+ }
+ setup()
+ }
+
+ private func setupProgress(cell: UICollectionViewCell) {
+ guard let cell = cell as? BaseChatCell, let url = cell.model?.progressModel?.url.absoluteString else {
+ return
+ }
+ let block: (ProgressModel) -> Void = { model in
+ cell.updateProgressClosure(model: model)
+ }
+ if view.progressDictionary[url] == nil {
+ view.progressDictionary[url] = [block]
+ } else {
+ view.progressDictionary[url]?.append(block)
+ }
+ }
+
+ private func loadData() {
+ if !view.loadingStatus {
+ view.loadingStatus = true
+ view.getNextPage()
+ }
+ }
+}
diff --git a/Nynja/Modules/Message/View/Views/CollectionView/MessageCollectionViewLayout.swift b/Nynja/Modules/Message/View/Views/CollectionView/MessageCollectionViewLayout.swift
new file mode 100644
index 0000000000000000000000000000000000000000..4072e4ca62c7e245be4559e6033338e1b0264ed1
--- /dev/null
+++ b/Nynja/Modules/Message/View/Views/CollectionView/MessageCollectionViewLayout.swift
@@ -0,0 +1,94 @@
+//
+// MessageCollectionViewLayout.swift
+// Nynja
+//
+// Created by Anton Poltoratskyi on 08.08.2018.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+import UIKit
+
+final class MessageCollectionViewLayout: UICollectionViewFlowLayout {
+
+ private var offset: CGFloat = 0
+
+ override init() {
+ super.init()
+ setup()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)
+ setup()
+ }
+
+ private func setup() {}
+
+ override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
+ if let currentBounds = collectionView?.bounds {
+ return currentBounds.width != newBounds.width
+ } else {
+ return false
+ }
+ }
+
+ override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
+ guard let attributes = super.layoutAttributesForElements(in: rect) else {
+ return nil
+ }
+ for attr in attributes {
+ attr.transform = CGAffineTransform(rotationAngle: .pi)
+ }
+ return attributes
+ }
+
+ override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
+ let attributes = super.layoutAttributesForItem(at: indexPath)
+ attributes?.transform = CGAffineTransform(rotationAngle: .pi)
+ return attributes
+ }
+
+ override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
+ guard let collectionView = collectionView else { return }
+
+ offset = 0
+
+ super.prepare(forCollectionViewUpdates: updateItems)
+
+ let currentOffset = collectionView.contentOffset.y + collectionView.contentInset.top
+
+ for item in updateItems {
+ switch item.updateAction {
+ case .insert:
+ guard let path = item.indexPathAfterUpdate, let attributes = layoutAttributesForItem(at: path) else {
+ continue
+ }
+
+ guard attributes.frame.maxY <= currentOffset else { continue }
+
+ offset += attributes.size.height + minimumLineSpacing
+ case .delete:
+ guard let path = item.indexPathBeforeUpdate, let attributes = layoutAttributesForItem(at: path) else {
+ continue
+ }
+
+ guard attributes.frame.maxY <= currentOffset else { continue }
+
+ offset -= (attributes.size.height + minimumLineSpacing)
+ default:
+ break
+ }
+ }
+ }
+
+ override func finalizeCollectionViewUpdates() {
+ super.finalizeCollectionViewUpdates()
+
+ guard let collectionView = collectionView else { return }
+
+ guard collectionView.contentSize.height + offset >= collectionView.frame.height - collectionView.contentInset.bottom else {
+ return
+ }
+ collectionView.contentOffset.y += offset
+ }
+}
diff --git a/Nynja/Modules/Message/View/Views/TableView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift b/Nynja/Modules/Message/View/Views/TableView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift
index 7fd838eba69d926d37e497d39841c7668c4a5c3d..2df83cb89c6fd8076d901265348dd9cd8d4e9a1b 100644
--- a/Nynja/Modules/Message/View/Views/TableView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift
+++ b/Nynja/Modules/Message/View/Views/TableView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift
@@ -9,7 +9,7 @@
import Foundation
import SnapKit
-class BaseChatCell: UITableViewCell, MessageBaseImageViewDataSource, MessageContentAppearance, MessageVoiceViewDelegate, ReplyCounterDelegate, MessageRepliedViewDelegate, MessageTextViewDelegate, MessageForwardViewDelegate, MessageStickerRepliedViewDelegate, MessageConvertionViewDelegate {
+class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, MessageContentAppearance, MessageVoiceViewDelegate, ReplyCounterDelegate, MessageRepliedViewDelegate, MessageTextViewDelegate, MessageForwardViewDelegate, MessageStickerRepliedViewDelegate, MessageConvertionViewDelegate {
private static let senderFont = UIFont(fontName: Constants.fonts.medium, height: CGFloat(22.0.adjustedByWidth))
private static let translatingFont = UIFont(name: Constants.fonts.regular, size: 12)
@@ -233,9 +233,11 @@ class BaseChatCell: UITableViewCell, MessageBaseImageViewDataSource, MessageCont
model?.resetHandlers()
}
+
// MARK: - Init
- override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
- super.init(style: style, reuseIdentifier: reuseIdentifier)
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
initialSetup()
}
@@ -246,7 +248,6 @@ class BaseChatCell: UITableViewCell, MessageBaseImageViewDataSource, MessageCont
// MARK: - Setup
func initialSetup() {
- selectionStyle = .none
backgroundColor = .clear
setupRecognizers()
}
@@ -294,6 +295,10 @@ class BaseChatCell: UITableViewCell, MessageBaseImageViewDataSource, MessageCont
bubble.isUserInteractionEnabled = true
bubble.addGestureRecognizer(recognizer)
+
+ let opponentAvaTap = UITapGestureRecognizer(target: self, action: #selector(opponentAvatarTapped))
+ self.fromImageView.isUserInteractionEnabled = true
+ self.fromImageView.addGestureRecognizer(opponentAvaTap)
}
func setupSenderInfo(_ model: BaseChatCellModel) {
@@ -406,6 +411,10 @@ class BaseChatCell: UITableViewCell, MessageBaseImageViewDataSource, MessageCont
delegate?.didPauseTapped(self, url: url)
}
+ @objc func opponentAvatarTapped() {
+ delegate?.opponentAvatarTapped(self)
+ }
+
func didChangeProgress(_ progress: Double) {
guard let url = model?.fileUrl else {
return
diff --git a/Nynja/Modules/Message/View/Views/TableView/Cells/ChatCells/BaseChatCell/BaseChatCellDelegate.swift b/Nynja/Modules/Message/View/Views/TableView/Cells/ChatCells/BaseChatCell/BaseChatCellDelegate.swift
index 885ebbe9453906199e0ca86588544e82ecd4aece..455ac51e14fa2b25f2aa4578cd3dbd40f70530c7 100644
--- a/Nynja/Modules/Message/View/Views/TableView/Cells/ChatCells/BaseChatCell/BaseChatCellDelegate.swift
+++ b/Nynja/Modules/Message/View/Views/TableView/Cells/ChatCells/BaseChatCell/BaseChatCellDelegate.swift
@@ -27,6 +27,7 @@ protocol BaseChatCellDelegate: class {
func openURL(_ url: URL)
func showMenuForURL(_ url: URL)
func showMention(_ mention: MentionInfo)
+ func opponentAvatarTapped(_ cell: BaseChatCell)
}
extension BaseChatCellDelegate {
@@ -49,4 +50,5 @@ extension BaseChatCellDelegate {
func openURL(_ url: URL) {}
func showMenuForURL(_ url: URL) {}
func showMention(_ mention: MentionInfo) {}
+ func opponentAvatarTapped(_ cell: BaseChatCell) {}
}
diff --git a/Nynja/Modules/Message/View/Views/TableView/Cells/Models/MessageSender.swift b/Nynja/Modules/Message/View/Views/TableView/Cells/Models/MessageSender.swift
index be4c7c0d6c0b865789fef8624f4964574a583722..86d349b4bfe5e81881da4b6aa7c542b50a6522ff 100644
--- a/Nynja/Modules/Message/View/Views/TableView/Cells/Models/MessageSender.swift
+++ b/Nynja/Modules/Message/View/Views/TableView/Cells/Models/MessageSender.swift
@@ -7,10 +7,11 @@
//
struct MessageSender {
- static let `deleted` = MessageSender(fullname: "deleted account name".localized,
+ static let `deleted` = MessageSender(phoneId: nil,
+ fullname: "deleted account name".localized,
nick: "deleted account name".localized,
avatar: nil)
-
+ let phoneId: String?
let fullname: String
let nick: String?
let avatar: String?
diff --git a/Nynja/Modules/Message/View/Views/TableView/Cells/SystemCell.swift b/Nynja/Modules/Message/View/Views/TableView/Cells/SystemCell.swift
index 57e3daf634fed5d86748f565471892d2c41c63e3..b0b0ec9c28f9a67c1f4bf3526cfc5232d46d0952 100644
--- a/Nynja/Modules/Message/View/Views/TableView/Cells/SystemCell.swift
+++ b/Nynja/Modules/Message/View/Views/TableView/Cells/SystemCell.swift
@@ -6,7 +6,7 @@
// Copyright © 2018 TecSynt Solutions. All rights reserved.
//
-final class SystemCell: UITableViewCell {
+final class SystemCell: UICollectionViewCell {
lazy var systemLabel: UILabel = {
let label = UILabel(height: Constraints.Label.height,
@@ -31,11 +31,9 @@ final class SystemCell: UITableViewCell {
// MARK: - Setup
func setup(message: String) {
- self.selectionStyle = .none
self.backgroundColor = UIColor.clear
systemLabel.text = message
}
-
}
@@ -49,5 +47,4 @@ extension SystemCell {
static let horizontalInset = 16.0.adjustedByWidth
}
}
-
}
diff --git a/Nynja/Modules/Message/View/Views/TableView/Cells/TimeCell.swift b/Nynja/Modules/Message/View/Views/TableView/Cells/TimeCell.swift
index 640d4bdb052af29f8e60f4e30471389febddb304..46f9c483d41db47628001cd977a7203addc49e48 100644
--- a/Nynja/Modules/Message/View/Views/TableView/Cells/TimeCell.swift
+++ b/Nynja/Modules/Message/View/Views/TableView/Cells/TimeCell.swift
@@ -8,9 +8,11 @@
import Foundation
-class TimeCell: UITableViewCell {
+final class TimeCell: UICollectionViewCell {
- lazy var timeStamp: UILabel = {
+ private lazy var dateFormatter = DateFormatter()
+
+ private lazy var timeStamp: UILabel = {
let v = UILabel()
v.font = UIFont(name: Constants.fonts.regular, size: 14)
v.textColor = Constants.colors.red.getColor()
@@ -22,13 +24,11 @@ class TimeCell: UITableViewCell {
}()
func setup(date: Date) {
- self.selectionStyle = .none
self.backgroundColor = UIColor.clear
- timeStamp.text = self.getText(fromDate: date).uppercased()
+ timeStamp.text = getText(fromDate: date)
}
- func getText(fromDate: Date) -> String {
- let dateFormatter = DateFormatter()
+ private func getText(fromDate: Date) -> String {
dateFormatter.dateFormat = "MMMM d".localizedDateFormat
dateFormatter.locale = Locale(identifier: Language.current.rawValue)
return dateFormatter.string(from: fromDate)
diff --git a/Nynja/Modules/Message/View/Views/TableView/Cells/UnreadCell.swift b/Nynja/Modules/Message/View/Views/TableView/Cells/UnreadCell.swift
index ad379285ebe57e57d6ab9d898d177c95c62f45e4..c048a7cb4c3cf51b92be502bdfbb6cbb9050175f 100644
--- a/Nynja/Modules/Message/View/Views/TableView/Cells/UnreadCell.swift
+++ b/Nynja/Modules/Message/View/Views/TableView/Cells/UnreadCell.swift
@@ -6,7 +6,9 @@
// Copyright © 2017 TecSynt Solutions. All rights reserved.
//
-final class UnreadCell: UITableViewCell {
+import Foundation
+
+final class UnreadCell: UICollectionViewCell {
lazy var timeStamp: UILabel = {
let v = UILabel()
@@ -56,8 +58,7 @@ final class UnreadCell: UITableViewCell {
}()
func setup() {
- self.selectionStyle = .none
- self.backgroundColor = UIColor.clear
+ backgroundColor = UIColor.clear
timeStamp.isHidden = false
line1.isHidden = false
line2.isHidden = false
diff --git a/Nynja/Modules/Message/View/Views/TableView/MessageDS.swift b/Nynja/Modules/Message/View/Views/TableView/MessageDS.swift
deleted file mode 100644
index f4e24b3a41cb0672262b9a833dbcdf4447fe5da8..0000000000000000000000000000000000000000
--- a/Nynja/Modules/Message/View/Views/TableView/MessageDS.swift
+++ /dev/null
@@ -1,71 +0,0 @@
-// MessageDS.swift
-// Nynja
-//
-// Created by Anton Makarov on 27.08.2017.
-// Copyright © 2017 TecSynt Solutions. All rights reserved.
-//
-
-import Foundation
-
-class MessageDS: NSObject, UITableViewDataSource {
-
- weak var view: MessageVC!
-
- var cells = [BaseChatCellModel]()
-
- var unreadCellIndex: Int? {
- return cells.index(where: { $0.unread == true })
- }
-
- // MARK: Init
- init(view: MessageVC) {
- self.view = view
- }
-
- // MARK: UITableViewDataSource
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return cells.count
- }
-
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let model = cells[indexPath.row]
-
- if model.unread != nil {
- if let cell = tableView.dequeueReusableCell(withIdentifier: "unread", for: indexPath) as? UnreadCell {
- cell.setup()
- cell.accessibilityIdentifier = "chat_cell_unread_\(indexPath.section)"
- return cell
- }
- }
-
- if model.messageType == .system {
- if let cell = tableView.dequeueReusableCell(withIdentifier: "system", for: indexPath) as? SystemCell {
- cell.setup(message: model.text!)
- cell.accessibilityIdentifier = "system_cell_unread_\(indexPath.section)"
- return cell
- }
- } else if model.time == nil {
- var cell: BaseChatCell!
-
- if let identifier = MessageCellFactory.identifier(for: model) {
- cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as? BaseChatCell
- cell.accessibilityIdentifier = "message_cell_\(identifier)_\(indexPath.section)"
- }
-
- if view.messageTVDelegate != nil {
- cell.delegate = view
- }
- cell.setup(model: model)
- return cell
- } else {
- if let cell = tableView.dequeueReusableCell(withIdentifier: "time", for: indexPath) as? TimeCell {
- cell.setup(date: model.time!)
- cell.accessibilityIdentifier = "time_cell_unread_\(indexPath.section)"
-
- return cell
- }
- }
-
- return UITableViewCell()
- }
-}
diff --git a/Nynja/Modules/Message/View/Views/TableView/MessageTableViewDelegate.swift b/Nynja/Modules/Message/View/Views/TableView/MessageTableViewDelegate.swift
deleted file mode 100644
index 478d7105ac7f6c56c96fbcccb3f3e1b4ba2d70b6..0000000000000000000000000000000000000000
--- a/Nynja/Modules/Message/View/Views/TableView/MessageTableViewDelegate.swift
+++ /dev/null
@@ -1,163 +0,0 @@
-//
-// MessageTableViewDelegate.swift
-// Nynja
-//
-// Created by Volodymyr Hryhoriev on 10/17/17.
-// Copyright © 2017 TecSynt Solutions. All rights reserved.
-//
-
-enum ScrollDirection {
- case top
- case bottom
-}
-
-class MessageTableViewDelegate: NSObject, UITableViewDelegate, CallInfoViewDelegate {
-
- weak var view: MessageVC?
-
- var rejoinBannerDisplayed: Bool = false
- var membersCount: Int = 0
-
- // MARK: Init
- init(view: MessageVC) {
- self.view = view
- }
-
- // MARK: UITableViewDelegate
-
- func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
-
- var callInfoView:CallInfoView? = nil
-
- if rejoinBannerDisplayed {
-
- callInfoView = CallInfoView()
-
- if let civ = callInfoView {
- civ.membersCount = membersCount
- civ.delegate = self
- }
- }
-
- return callInfoView
- }
-
- func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
- return rejoinBannerDisplayed ? CallInfoView.Constraints.baseSizes.height : 0
- }
-
- func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
- guard let model = view?.messageDS.cells[indexPath.row] else { return 0 }
-
- if model.messageType == .system {
- return 40
- }
-
- if let _ = model.type {
- return height(for: model)
- }
-
- if model.unread != nil {
- return 30
- }
-
- if model.time == nil {
- return height(for: model)
- } else {
- return 30
- }
- }
-
- func height(for model: BaseChatCellModel) -> CGFloat {
- return model.isOwner ? BaseChatCell.size(for: model).height : OponentChatCell.size(for: model).height
- }
-
- // MARK: UIScrollViewDelegate
- var scrollOffset: CGFloat = 0
- var lastVisibleIndex: Int?
-
- func scrollViewDidScroll(_ scrollView: UIScrollView) {
- let offset = scrollView.contentOffset.y
-
- let absoluteTop: CGFloat = 0
- let absoluteBottom: CGFloat = scrollView.contentSize.height - scrollView.frame.size.height
-
- let isTop = scrollOffset - offset > 0 && offset > absoluteTop
- let isBottom = scrollOffset - offset < 0 && offset < absoluteBottom
-
- if isTop {
- view?.isScrollingTo(.top)
- } else if isBottom {
- view?.isScrollingTo(.bottom)
- }
-
- if offset >= absoluteBottom {
- view?.didReachBottom()
- }
-
- let contentHeight = scrollView.contentSize.height
-
- if offset < 0 && scrollView.frame.size.height < contentHeight {
- loadData()
- }
- scrollOffset = offset
- }
-
- func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
- setupProgress(cell: cell)
- setupMentions(cell: cell, indexPath: indexPath)
- }
-
- func setupMentions(cell: UITableViewCell, indexPath: IndexPath) {
- guard let index = lastVisibleIndex ?? view?.lastVisibleCellIndex() else {
- lastVisibleIndex = indexPath.row
- view?.setupUnreadMentions(after: indexPath.row)
- return
- }
- guard indexPath.row > index else {
- return
- }
- lastVisibleIndex = indexPath.row
- view?.setupUnreadMentions(after: indexPath.row)
- }
-
- func setupProgress(cell: UITableViewCell) {
- guard let bcc = cell as? BaseChatCell else { return }
- guard let url = bcc.model?.progressModel?.url.absoluteString else { return }
- let block : (ProgressModel)-> Void = { model in
- (cell as? BaseChatCell)?.updateProgressClosure(model: model)
- }
- if view?.progressDictionary[url] == nil {
- view?.progressDictionary[url] = [block]
- } else {
- view?.progressDictionary[url]?.append(block)
- }
- }
-
- func loadData() {
- guard let v = self.view else { return }
- if !v.loadingStatus{
- v.loadingStatus = true
- v.getNextPage()
- }
- }
-
- func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
- guard let bcc = cell as? BaseChatCell else { return }
- guard let url = bcc.model?.fileUrl?.absoluteString else { return }
- view?.progressDictionary.removeValue(forKey: url)
- }
-
- // MARK: CallInfoVIewDelegate
- func didPressButtonJoinIn(callInfoView: CallInfoView) {
-
- AlertManager.sharedInstance.showAlertWithTwoActions(title: "",
- message: "are_u_sure_join_the_call".localized,
- firstActionTitle: "no".localized,
- secondActionTitle: "yes".localized,
- firstAction: nil,
- secondAction: {
- self.view?.rejoinRunningCall()
- })
- }
-}
diff --git a/Nynja/Modules/Message/WireFrame/ChatScreenAlertFactory/ChatScreenAlertFactory.swift b/Nynja/Modules/Message/WireFrame/ChatScreenAlertFactory/ChatScreenAlertFactory.swift
index cb0929915ceeb7ac0b49e9eb6f7c491ead13ce9c..03942e8ac9a3749a8b95203aaf8a1aa499bbfb05 100644
--- a/Nynja/Modules/Message/WireFrame/ChatScreenAlertFactory/ChatScreenAlertFactory.swift
+++ b/Nynja/Modules/Message/WireFrame/ChatScreenAlertFactory/ChatScreenAlertFactory.swift
@@ -102,8 +102,8 @@ private extension ChatScreenAlertFactory {
func makeDeleteForMeAndOthersAndCancelMenuConfig(deleteForOthersTitle: String,
delete: @escaping AlertActionWrapper,
deleteForOthers: @escaping AlertActionWrapper) -> DeleteMenuConfig {
- let deleteForOthersAction = makeAction(title: deleteForOthersTitle, actionBlock: delete)
- let deleteAction = makeAction(title: Strings.deleteForMe.localized, actionBlock: deleteForOthers)
+ let deleteForOthersAction = makeAction(title: deleteForOthersTitle, actionBlock: deleteForOthers)
+ let deleteAction = makeAction(title: Strings.deleteForMe.localized, actionBlock: delete)
let cancelAction = makeCancelAction()
return DeleteMenuConfig(
title: "",
diff --git a/Nynja/Modules/Message/WireFrame/MessageWireframe.swift b/Nynja/Modules/Message/WireFrame/MessageWireframe.swift
index a42b2d386a5004f842640ff820975c0fcdd79209..60acf1192a0139cdcc5b283bdbf2decb09862ff2 100644
--- a/Nynja/Modules/Message/WireFrame/MessageWireframe.swift
+++ b/Nynja/Modules/Message/WireFrame/MessageWireframe.swift
@@ -151,4 +151,9 @@ class MessageWireFrame: MessageWireframeProtocol, DocumentInteractionWireFrame {
func show(alert: UIAlertController) {
navigation?.present(alert, animated: true, completion: nil)
}
+
+ func openMarketplaceScreen() {
+ main?.openMarketplace()
+ }
+
}
diff --git a/Nynja/Modules/Participants/Interactor/ParticipantsInteractor.swift b/Nynja/Modules/Participants/Interactor/ParticipantsInteractor.swift
index 6b8f30a77142416cc81d3d3719a547eb33135d6b..babd3b0c4482bd136a25a35d649b297d32a50d29 100644
--- a/Nynja/Modules/Participants/Interactor/ParticipantsInteractor.swift
+++ b/Nynja/Modules/Participants/Interactor/ParticipantsInteractor.swift
@@ -38,7 +38,7 @@ class ParticipantsInteractor: BaseInteractor, ParticipantsInteractorInputProtoco
override func update(with changes: [StorageChange], type: SubscribeType) {
if case .room = type {
guard let dbRoom = changes.first?.entity as? DBRoom else { return }
- guard let updatedMembers = Room(room: dbRoom).members else { return }
+ guard let updatedMembers = Room(room: dbRoom).allMembers else { return }
self.includedMembers = updatedMembers
self.loadContacts()
}
diff --git a/Nynja/Modules/Participants/View/ParticipantsViewController.swift b/Nynja/Modules/Participants/View/ParticipantsViewController.swift
index 78d398bdac9444e48a88ec2f57306836a1c92339..40ef3ce68d19e5a91a5a8e7949f35e0e6727b16d 100644
--- a/Nynja/Modules/Participants/View/ParticipantsViewController.swift
+++ b/Nynja/Modules/Participants/View/ParticipantsViewController.swift
@@ -39,11 +39,16 @@ class ParticipantsViewController: BaseVC, ParticipantsViewProtocol {
tblView.sectionHeaderHeight = ParticipantsHeaderView.height
tblView.estimatedSectionHeaderHeight = tblView.sectionHeaderHeight
+ tblView.contentInset = UIEdgeInsets(top: CGFloat(Constraints.tableView.topInset.adjustedByWidth),
+ left: 0,
+ bottom: 0,
+ right: 0)
+
self.view.addSubview(tblView)
- tblView.snp.makeConstraints({ (make) in
- make.top.equalTo(navigationView.snp.bottom).offset(Constraints.tableView.topInset.adjustedByWidth)
+ tblView.snp.makeConstraints { make in
+ make.top.equalTo(navigationView.snp.bottom)
make.left.right.equalTo(navigationView)
- })
+ }
return tblView
}()
diff --git a/Nynja/Modules/Replies/Interactor/RepliesInteractor.swift b/Nynja/Modules/Replies/Interactor/RepliesInteractor.swift
index cc4e352b9886a135913e779be36df5b5a289bbaa..7791c2480ec8e54d32f5bbd2ba0868878eaa2bc3 100644
--- a/Nynja/Modules/Replies/Interactor/RepliesInteractor.swift
+++ b/Nynja/Modules/Replies/Interactor/RepliesInteractor.swift
@@ -30,7 +30,6 @@ final class RepliesInteractor: BaseInteractor, RepliesInteractorInputProtocol, M
processingManager = DefaultMessagesProcessingManager.shared
super.init()
message = MessageDAO.fetchMessage(localId: messageLocalId)
- processingManager.delegate = self
}
//MARK: - BaseInteractor
@@ -44,6 +43,7 @@ final class RepliesInteractor: BaseInteractor, RepliesInteractorInputProtocol, M
override func loadData() {
super.loadData()
+ processingManager.delegate = self
let replyIdentifiers = message.repliedby?.compactMap { Int64($0) } ?? []
loadReplies(replyIdentifiers)
presenter?.updateHeaderView(replied: message.repliedby?.count ?? 0)
@@ -97,13 +97,13 @@ final class RepliesInteractor: BaseInteractor, RepliesInteractorInputProtocol, M
func sender(for message: Message) -> MessageSender? {
let isOwner = message.from == StorageService.sharedInstance.phoneId
if let _ = (message.feed_id as? muc), let member = member(for: message) {
- return MessageSender(fullname: member.fullName ?? "", nick: member.alias, avatar: member.avatar)
+ return MessageSender(phoneId: member.phone_id, fullname: member.fullName ?? "", nick: member.alias, avatar: member.avatar)
} else {
if isOwner, let phoneId = StorageService.sharedInstance.phoneId, let myContact = ContactDAO.findContactBy(phoneId: phoneId) {
- return MessageSender(fullname: myContact.fullName ?? "", nick: myContact.nick, avatar: myContact.avatar)
+ return MessageSender(phoneId: myContact.phone_id, fullname: myContact.fullName ?? "", nick: myContact.nick, avatar: myContact.avatar)
} else {
if let to = (message.feed_id as? p2p)?.to, let contact = ContactDAO.findContactBy(phoneId: to) {
- return MessageSender(fullname: contact.fullName ?? "", nick: contact.nick, avatar: contact.avatar)
+ return MessageSender(phoneId: contact.phone_id, fullname: contact.fullName ?? "", nick: contact.nick, avatar: contact.avatar)
}
}
}
@@ -130,7 +130,9 @@ final class RepliesInteractor: BaseInteractor, RepliesInteractorInputProtocol, M
// MARK: - MessageProcessingDelegate
func updateProgress(_ progress: ProgressModel) {
- self.presenter?.updateProgress(progress)
+ dispatchAsyncMain { [weak presenter] in
+ presenter?.updateProgress(progress)
+ }
}
diff --git a/Nynja/Modules/Replies/View/RepliesCollectionViewDelegate.swift b/Nynja/Modules/Replies/View/RepliesCollectionViewDelegate.swift
new file mode 100644
index 0000000000000000000000000000000000000000..fd89cff62fe3629c3d383b00750425a0bef8ddbe
--- /dev/null
+++ b/Nynja/Modules/Replies/View/RepliesCollectionViewDelegate.swift
@@ -0,0 +1,46 @@
+//
+// RepliesCollectionViewDelegate.swift
+// Nynja
+//
+// Created by Andrey Reznik on 09.01.18.
+// Copyright © 2018 TecSynt Solutions. All rights reserved.
+//
+
+class RepliesCollectionViewDelegate: NSObject, UICollectionViewDelegateFlowLayout {
+
+ unowned var collectionView: UICollectionView
+ weak var dataSource: RepliesDS!
+
+
+ // MARK: - Init
+
+ init(collectionView: UICollectionView, dataSource: RepliesDS) {
+ self.collectionView = collectionView
+ self.dataSource = dataSource
+ }
+
+
+ // MARK: - UICollectionViewDelegate
+
+ func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+ return CGSize(width: collectionView.bounds.width, height: heightForItem(at: indexPath))
+ }
+
+ private func heightForItem(at indexPath: IndexPath) -> CGFloat {
+ let model = dataSource.cells[indexPath.item]
+
+ if model.type != nil {
+ return height(for: model)
+ } else if model.unread != nil {
+ return 30
+ } else if model.time == nil {
+ return height(for: model)
+ } else {
+ return 30
+ }
+ }
+
+ private func height(for model: BaseChatCellModel) -> CGFloat {
+ return model.isOwner ? BaseChatCell.size(for: model).height : OponentChatCell.size(for: model).height
+ }
+}
diff --git a/Nynja/Modules/Replies/View/RepliesDS.swift b/Nynja/Modules/Replies/View/RepliesDS.swift
index 28ccc1d02314f87ba466f1d45124a459a1205be5..cd65ab844474347b454cc8dcf27160d2c4cdba0c 100644
--- a/Nynja/Modules/Replies/View/RepliesDS.swift
+++ b/Nynja/Modules/Replies/View/RepliesDS.swift
@@ -6,43 +6,47 @@
// Copyright © 2018 TecSynt Solutions. All rights reserved.
//
-class RepliesDS: NSObject, UITableViewDataSource {
+final class RepliesDS: NSObject, UICollectionViewDataSource {
weak var view: RepliesVC!
var cells = [BaseChatCellModel]()
- // MARK: Init
+
+ // MARK: - Init
+
init(view: RepliesVC) {
self.view = view
}
- // MARK: UITableViewDataSource
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+
+ // MARK: - UITableViewDataSource
+
+ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cells.count
}
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let model = cells[indexPath.row]
if model.time == nil {
var cell: BaseChatCell!
if let identifier = MessageCellFactory.identifier(for: model) {
- cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as? BaseChatCell
+ cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as? BaseChatCell
}
- if view.tableViewDelegate != nil {
+ if view.collectionViewDelegate != nil {
cell.delegate = view
}
-
+
cell.setup(model: model)
-// view.cellDisplayed(at: indexPath)
+ // view.cellDisplayed(at: indexPath)
return cell
}
- return UITableViewCell()
+ return UICollectionViewCell()
}
}
diff --git a/Nynja/Modules/Replies/View/RepliesTableViewDelegate.swift b/Nynja/Modules/Replies/View/RepliesTableViewDelegate.swift
deleted file mode 100644
index a7d4b8334b6a4a5f0ec815e3628e7f692548c1b8..0000000000000000000000000000000000000000
--- a/Nynja/Modules/Replies/View/RepliesTableViewDelegate.swift
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// RepliesTableViewDelegate.swift
-// Nynja
-//
-// Created by Andrey Reznik on 09.01.18.
-// Copyright © 2018 TecSynt Solutions. All rights reserved.
-//
-
-class RepliesTableViewDelegate: NSObject, UITableViewDelegate {
-
- weak var dataSource: RepliesDS!
-
- // MARK: Init
- init(dataSource: RepliesDS) {
- self.dataSource = dataSource
- }
-
- // MARK: UITableViewDelegate
- func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
- let model = dataSource.cells[indexPath.row]
-
- if let _ = model.type {
- return height(for: model)
- }
-
- if model.unread != nil {
- return 30
- }
-
- if model.time == nil {
- return height(for: model)
- } else {
- return 30
- }
- }
-
- private func height(for model: BaseChatCellModel) -> CGFloat {
- return model.isOwner ? BaseChatCell.size(for: model).height : OponentChatCell.size(for: model).height
- }
-}
diff --git a/Nynja/Modules/Replies/View/RepliesVC.swift b/Nynja/Modules/Replies/View/RepliesVC.swift
index 1a660f6835a7191e4cbc88b74adf2344c953dd68..179e588d4b8b175349beb5ef73e5bdc096af5db5 100644
--- a/Nynja/Modules/Replies/View/RepliesVC.swift
+++ b/Nynja/Modules/Replies/View/RepliesVC.swift
@@ -11,7 +11,7 @@ import CoreLocation.CLLocation
import Photos
-class RepliesVC: BaseVC, RepliesViewProtocol {
+final class RepliesVC: BaseVC, RepliesViewProtocol {
var presenter: RepliesPresenterProtocol! {
didSet {
@@ -19,12 +19,13 @@ class RepliesVC: BaseVC, RepliesViewProtocol {
}
}
- var tableViewDataSource: RepliesDS!
- var tableViewDelegate: RepliesTableViewDelegate!
+ var collectionViewDataSource: RepliesDS?
+ var collectionViewDelegate: RepliesCollectionViewDelegate?
+
//MARK: - Subviews
- lazy var repliesNumberLabel: UILabel = {
+ private lazy var repliesNumberLabel: UILabel = {
let label = UILabel(height: Constraints.repliesNumberLabel.height,
color: Constants.colors.white.getColor(),
fontName: Constants.fonts.regular,
@@ -42,30 +43,29 @@ class RepliesVC: BaseVC, RepliesViewProtocol {
return label
}()
- lazy var tableView: UITableView = {
- let tv = UITableView()
-
- tv.separatorStyle = .none
- tv.backgroundColor = UIColor.clear
- tv.contentInset = UIEdgeInsetsMake(Constraints.tableView.topInset, 0, 0, 0)
- tv.contentOffset = CGPoint(x: 0, y: -Constraints.tableView.topInset)
+ private lazy var collectionView: UICollectionView = {
+ let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
+ collectionView.backgroundColor = .clear
+ collectionView.alwaysBounceVertical = true
- registerCells(for: tv)
-
- tv.estimatedRowHeight = 0 // NOTE: It is important for scrolling behavior.
+ collectionView.contentInset = UIEdgeInsetsMake(Constraints.tableView.topInset, 0, 0, 0)
+ collectionView.contentOffset = CGPoint(x: 0, y: -Constraints.tableView.topInset)
let bottomPadding = UIScreen.main.bounds.height * 0.11
- self.view.addSubview(tv)
- tv.snp.makeConstraints({ (make) in
- make.left.right.equalTo(self.view)
+ view.addSubview(collectionView)
+ collectionView.snp.makeConstraints { (make) in
+ make.left.right.equalToSuperview()
make.top.equalTo(self.navigationView.snp.bottom)
adjustVerticalInset(.bottom, make: make, offset: -bottomPadding)
- })
- return tv
+ }
+
+ registerCells(for: collectionView)
+
+ return collectionView
}()
- lazy var closeButton: NynjaCloseButton = {
+ private lazy var closeButton: NynjaCloseButton = {
let button = NynjaCloseButton()
button.contentMode = .scaleToFill
@@ -95,11 +95,12 @@ class RepliesVC: BaseVC, RepliesViewProtocol {
closeButton.isHidden = false
- tableViewDataSource = RepliesDS(view: self)
- tableViewDelegate = RepliesTableViewDelegate(dataSource: tableViewDataSource)
+ collectionViewDataSource = RepliesDS(view: self)
+ collectionViewDelegate = RepliesCollectionViewDelegate(collectionView: collectionView,
+ dataSource: collectionViewDataSource!)
- tableView.dataSource = tableViewDataSource
- tableView.delegate = tableViewDelegate
+ collectionView.dataSource = collectionViewDataSource
+ collectionView.delegate = collectionViewDelegate
}
//MARK: - RepliesViewProtocol
@@ -109,26 +110,30 @@ class RepliesVC: BaseVC, RepliesViewProtocol {
}
func updateTableViewDataSource(_ cells: [BaseChatCellModel]) {
- tableViewDataSource.cells = cells
- tableView.reloadData()
- tableView.layoutIfNeeded()
+ collectionViewDataSource?.cells = cells
+ collectionView.reloadData()
+ collectionView.layoutIfNeeded()
}
func removeReplies(_ ids: [String]) {
- let filtred = self.tableViewDataSource.cells.filter { model -> Bool in
+ guard let dataSource = collectionViewDataSource else {
+ return
+ }
+ let filtred = dataSource.cells.filter { model -> Bool in
ids.contains(where: { $0 == model.id })
}
let indexes = filtred.map { model -> Int in
- return self.tableViewDataSource.cells.index(where: { $0.id == model.id })!
+ return dataSource.cells.index(where: { $0.id == model.id })!
}
guard !indexes.isEmpty else {
return
}
- indexes.forEach { self.tableViewDataSource.cells.remove(at: $0) }
- tableView.beginUpdates()
- tableView.deleteRows(at: indexes.map { IndexPath(row: $0, section: 0) }, with: .none)
- tableView.endUpdates()
+ indexes.forEach { dataSource.cells.remove(at: $0) }
+
+ collectionView.performBatchUpdates({
+ collectionView.deleteItems(at: indexes.map { IndexPath(row: $0, section: 0) })
+ }, completion: nil)
}
func insertReplies(_ cells: [BaseChatCellModel]) {
@@ -136,13 +141,12 @@ class RepliesVC: BaseVC, RepliesViewProtocol {
}
func updateProgress(progressModel: ProgressModel?) {
- let url = progressModel?.url
- let list = tableViewDataSource.cells.filter({ (model) -> Bool in
- model.progressModel?.url == url
- })
- list.forEach { (model) in
- model.progressModel = progressModel
+ guard let dataSource = collectionViewDataSource else {
+ return
}
+ let url = progressModel?.url
+ let list = dataSource.cells.filter { $0.progressModel?.url == url }
+ list.forEach { $0.progressModel = progressModel }
reloadIfVisible(models: list)
}
@@ -156,17 +160,17 @@ class RepliesVC: BaseVC, RepliesViewProtocol {
//MARK: - Private methods
- private func registerCells(for tableView: UITableView) {
+ private func registerCells(for collectionView: UICollectionView) {
MessageCellFactory.selfIdentifiers.forEach {
- tableView.register(BaseChatCell.self, forCellReuseIdentifier: $0)
+ collectionView.register(BaseChatCell.self, forCellWithReuseIdentifier: $0)
}
MessageCellFactory.opponenetIdentifiers.forEach {
- tableView.register(OponentChatCell.self, forCellReuseIdentifier: $0)
+ collectionView.register(OponentChatCell.self, forCellWithReuseIdentifier: $0)
}
}
func reloadIfVisible(models: [BaseChatCellModel]) {
- tableView.visibleCells.forEach { (cell) in
+ collectionView.visibleCells.forEach { (cell) in
if let model = (cell as? BaseChatCell)?.model {
if let index = models.index(where: { (mod) -> Bool in
if mod.id != nil {
@@ -186,14 +190,16 @@ class RepliesVC: BaseVC, RepliesViewProtocol {
}
}
+// MARK: - Layout
+
extension RepliesVC {
- struct Constraints {
- struct tableView {
+ enum Constraints {
+ enum tableView {
static let topInset = CGFloat(8.0.adjustedByWidth)
}
- struct closeButton {
+ enum closeButton {
static let height: CGFloat = 44.0
static let width: CGFloat = 80.0
@@ -201,7 +207,7 @@ extension RepliesVC {
static let bottomInset: CGFloat = 26.0
}
- struct repliesNumberLabel {
+ enum repliesNumberLabel {
static let height = CGFloat(17.adjustedByWidth)
static let rightInset = 16.adjustedByWidth
static let leftOffset = 8.adjustedByWidth
diff --git a/Nynja/Modules/ScheduleMessage/Interactor/ScheduleMessageInteractor.swift b/Nynja/Modules/ScheduleMessage/Interactor/ScheduleMessageInteractor.swift
index 27ac6ddc460aec07830ea0c6d3804d4fcc32607b..3a4ded37d702702343f70a5f6befc16f02622c75 100644
--- a/Nynja/Modules/ScheduleMessage/Interactor/ScheduleMessageInteractor.swift
+++ b/Nynja/Modules/ScheduleMessage/Interactor/ScheduleMessageInteractor.swift
@@ -54,12 +54,12 @@ class ScheduleMessageInteractor: BaseInteractor, ScheduleMessageInteractorInputP
func sendScheduledMessage(with timeZone: TimeZoneLocal, date: Date) {
guard let phoneId = StorageService.sharedInstance.phoneId, let info = self.info, let (features, timestamp) = prepareDateTimeInfo(with: timeZone, date: date) else { return }
- let messages = info.targets.messages(from: info.message, phoneId: phoneId)
+ let message = info.message
+ message.created = timestamp as AnyObject
- mqttService.scheduleMessage(phoneId: phoneId,
- messages: messages,
- timestamp: timestamp,
- features: features)
+ let messages = info.targets.messages(from: message, phoneId: phoneId)
+
+ mqttService.scheduleMessage(phoneId: phoneId, messages: messages, timestamp: timestamp, features: features)
}
func editScheduledMessage(with timeZone: TimeZoneLocal, date: Date) {
diff --git a/Nynja/Modules/ScheduleMessage/View/Views/MessageContent/AudioItemView.swift b/Nynja/Modules/ScheduleMessage/View/Views/MessageContent/AudioItemView.swift
index f0894559ebc5ac0a60d74b48279062da6a7e120d..eff163027efefae768d5be9674e7addc4d9a421a 100644
--- a/Nynja/Modules/ScheduleMessage/View/Views/MessageContent/AudioItemView.swift
+++ b/Nynja/Modules/ScheduleMessage/View/Views/MessageContent/AudioItemView.swift
@@ -7,7 +7,6 @@
//
import UIKit
-import VoxImplant
struct AudioItemModel {
let url: URL
diff --git a/Nynja/Modules/SettingsGroup/Presenter/SettingsGroupPresenter.swift b/Nynja/Modules/SettingsGroup/Presenter/SettingsGroupPresenter.swift
index 33ebbc763b8ee539ec1212df84a681f2ecc4b85c..346e13c9e2bbf8ffd701dc3da646aae396ebcfa5 100644
--- a/Nynja/Modules/SettingsGroup/Presenter/SettingsGroupPresenter.swift
+++ b/Nynja/Modules/SettingsGroup/Presenter/SettingsGroupPresenter.swift
@@ -54,15 +54,18 @@ class SettingsGroupPresenter: BasePresenter, SettingsGroupPresenterProtocol, Cre
}
}
- func changeAvatar() {
+ func openAvatar(from imageView: UIImageView) {
if isAdmin {
wireFrame.changeAvatar()
- } else {
- if let urlStr = room?.data?.first?.payload, !urlStr.isEmpty {
- if let url = URL(string: urlStr) {
- wireFrame.showAvatar(url)
- }
+ } else if let urlString = room?.data?.first?.payload, let url = URL(string: urlString) {
+ guard let view = view as? UIViewController else {
+ return
}
+ let transitionInfo = ImagePreviewTransitionInfo(interactiveDismissalEnabled: true,
+ startingView: imageView,
+ endingView: imageView)
+
+ wireFrame.presentAvatarModally(imageURL: url, on: view, with: transitionInfo)
}
}
diff --git a/Nynja/Modules/SettingsGroup/SettingsProtocols.swift b/Nynja/Modules/SettingsGroup/SettingsProtocols.swift
index 2617cf66fab6737822b96560d4ade3765fb6b024..bb0ca5f62fc78089a196fe4cba51fd841727c1a3 100644
--- a/Nynja/Modules/SettingsGroup/SettingsProtocols.swift
+++ b/Nynja/Modules/SettingsGroup/SettingsProtocols.swift
@@ -16,7 +16,7 @@ protocol SettingsGroupWireframeProtocol: class {
func present(navigation: UINavigationController, main: MainWireFrame?)
func hide()
- func showAvatar(_ url: URL)
+ func presentAvatarModally(imageURL: URL, on view: UIViewController, with transitionInfo: ImagePreviewTransitionInfo)
func changeAvatar()
func changeGroupName(name: String)
func changeAlias(alias: String, nicks: [String])
@@ -43,7 +43,7 @@ protocol SettingsGroupPresenterProtocol: BasePresenterProtocol {
*/
func hide()
- func changeAvatar()
+ func openAvatar(from imageView: UIImageView)
func changeGroupName()
func changeAlias()
func showRules()
diff --git a/Nynja/Modules/SettingsGroup/View/SettingsGroupVC.swift b/Nynja/Modules/SettingsGroup/View/SettingsGroupVC.swift
index fe8fe8e8d55ce2b46062b571feb4534adbfdb337..8ceaf243e6e21cef0bb23fb426ff53dacfa7013d 100644
--- a/Nynja/Modules/SettingsGroup/View/SettingsGroupVC.swift
+++ b/Nynja/Modules/SettingsGroup/View/SettingsGroupVC.swift
@@ -123,8 +123,8 @@ class SettingsGroupViewController: BaseVC, SettingsGroupViewProtocol, SettingCel
func didTapSetting(_ type: SettingViewModelType) {
switch type {
- case .Avatar:
- presenter.changeAvatar()
+ case let .Avatar(imageView):
+ presenter.openAvatar(from: imageView)
case .Name:
presenter.changeGroupName()
case .Alias:
diff --git a/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift b/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift
index c6a85a0a6efb39bcecd93b02064e1605802aaa71..887652606040ae4ea8583aab2a314b3b90f48d88 100644
--- a/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift
+++ b/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift
@@ -55,12 +55,10 @@ class SettingsGroupWireFrame: SettingsGroupWireframeProtocol {
navigation?.popViewController(animated: true)
}
-
- func showAvatar(_ url: URL) {
- if let mainNavigation = main?.navigation {
- ImagePreviewWireFrame().presentImagePreview(navigation: mainNavigation, imageURL: url)
- mainNavigation.view.layoutIfNeeded()
- }
+ func presentAvatarModally(imageURL: URL, on view: UIViewController, with transitionInfo: ImagePreviewTransitionInfo) {
+ ImagePreviewWireFrame().presentImagePreviewModally(parentVC: view,
+ imageURL: imageURL,
+ transitionInfo: transitionInfo)
}
func changeAvatar() {
diff --git a/Nynja/Modules/TimeZoneSelector/Interactor/TimeZoneSelectorInteractor.swift b/Nynja/Modules/TimeZoneSelector/Interactor/TimeZoneSelectorInteractor.swift
index dd0223add54ec70497941deb3bc25861be454274..b61eef2eb0a999ddcd879a5afd748f93b36b170c 100644
--- a/Nynja/Modules/TimeZoneSelector/Interactor/TimeZoneSelectorInteractor.swift
+++ b/Nynja/Modules/TimeZoneSelector/Interactor/TimeZoneSelectorInteractor.swift
@@ -11,10 +11,7 @@ class TimeZoneSelectorInteractor: TimeZoneSelectorInteractorInputProtocol {
weak var presenter: TimeZoneSelectorInteractorOutputProtocol!
func filterTimeZones(text: String) -> [TimeZoneLocal] {
- let filtered = TimeZoneManager.shared.timezones.filter {
- text.isEmpty ? true : $0.utc.first!.replacingOccurrences(of: "_", with: " ").contains(text)
- }
- return filtered
+ let timezones = TimeZoneManager.shared.timezones
+ return text.isEmpty ? timezones : timezones.filter { text.isIn(string: $0.name, options: .caseInsensitive) }
}
-
}
diff --git a/Nynja/Modules/TimeZoneSelector/View/TableView/TimeZoneCellModel.swift b/Nynja/Modules/TimeZoneSelector/View/TableView/TimeZoneCellModel.swift
index f83b38c8bde8642f8bb2ffdb0ff9265f97892cc1..fd28944b55e911cddbca68c4d0813a6d7455e165 100644
--- a/Nynja/Modules/TimeZoneSelector/View/TableView/TimeZoneCellModel.swift
+++ b/Nynja/Modules/TimeZoneSelector/View/TableView/TimeZoneCellModel.swift
@@ -18,7 +18,7 @@ final class TimeZoneCellModel: CellViewModel {
}
func setup(cell: TimeZoneCell) {
- cell.titleLabel.text = timeZone.utc.first!.replacingOccurrences(of: "_", with: " ")
+ cell.titleLabel.text = timeZone.name
cell.descriptionLabel.text = timeZone.text
}
}
diff --git a/Nynja/OptionsItemsFactory.swift b/Nynja/OptionsItemsFactory.swift
index 6da1b98a5b40a97d404b2ebdc72fea1ef1f4920e..09089073b07464cd47756b60df48799c20db10d5 100644
--- a/Nynja/OptionsItemsFactory.swift
+++ b/Nynja/OptionsItemsFactory.swift
@@ -17,9 +17,10 @@ class OptionsItemsFactory: WCBaseItemsFactory {
// MARK: - Second lvl
override var secondLevelItems: ItemModels {
- return [logout, notifications, changeNumber, wheelPosition, buildNumber, support, languageSettings, theme, dataAndStorage, security, privacy, about, deleteAccount]
+// return [logout, notifications, changeNumber, wheelPosition, buildNumber, support, languageSettings, theme, dataAndStorage, security, privacy, about, deleteAccount]
+ return [languageSettings, theme, dataAndStorage, security, privacy, notifications, changeNumber, wheelPosition, buildNumber, support, logout]
}
-
+//Language, Theme, Data and Storage, Security, Privacy, notification, change number, Wheel position, Build number, support
// MARK: - Items
var logout: ImageActionItemModel {
let item = ImageActionItemModel(navItem: .logOut, action: { [weak navigateDelegate] (item, indexPath) in
diff --git a/Nynja/ProgressModel.swift b/Nynja/ProgressModel.swift
index 2ef1ce78c411c3c6fab1f89a100f3ab2f9e64d9b..a9828c2a521eecba5363b21e1ddbd1cf37f59ade 100644
--- a/Nynja/ProgressModel.swift
+++ b/Nynja/ProgressModel.swift
@@ -14,22 +14,26 @@ class ProgressModel {
case done
}
- var url: URL
- var progress: Float
- var speed: Double
-
public private (set) var speedStringRepresentation: String = ""
+ var url: URL
+ var progress: Float = 0
+ var speed: Double = 0
var result: URL?
var status: ProgressStatus
- var fileSize: Int64
+ var fileSize: Int64 = 0
- required init(url: URL, status: ProgressStatus, result: URL? = nil, transferInfo: TransferInfo) {
+ required init(url: URL, status: ProgressStatus, result: URL? = nil, transferInfo: TransferInfo? = nil) {
self.url = url
- self.progress = transferInfo.progress
- self.speed = transferInfo.speed
+
+ if let info = transferInfo {
+ self.progress = info.progress
+ self.speed = info.speed
+ self.fileSize = info.fileSize
+ }
+
self.speedStringRepresentation = ProgressModel.getSpeedStringRepresentation(with: self.speed)
- self.fileSize = transferInfo.fileSize
+
self.status = status
self.result = result
}
diff --git a/Nynja/RequestModelFactory/HistoryRequestModelFactory.swift b/Nynja/RequestModelFactory/HistoryRequestModelFactory.swift
index 71c4f9715f3c42fd043842c9dcf339d838d046ee..676ab754ff060ff24a820cf4a594f0254853472f 100644
--- a/Nynja/RequestModelFactory/HistoryRequestModelFactory.swift
+++ b/Nynja/RequestModelFactory/HistoryRequestModelFactory.swift
@@ -19,6 +19,11 @@ protocol HistoryRequestModelFactoryProtocol {
lastMessageId: Int64) throws -> HistoryRequestModel
func makeHistoryRequestModelStickers(rosterId: String) throws -> HistoryRequestModel
+
+ func makeHistoryRequestModel(rosterId: String,
+ chat: ChatModel,
+ from: MessageServerId,
+ to: MessageServerId) throws -> HistoryRequestModel
}
enum HistoryRequestModelError: Error {
@@ -48,7 +53,7 @@ final class HistoryRequestModelFactory: HistoryRequestModelFactoryProtocol {
let historyType = try findModelType(for: chat)
let input = HistoryRequestModel.RequestInput(rosterId: rosterId,
historyType: historyType,
- actionType: .get(lastMessageId: lastMessageId, pageSize: pageSize))
+ actionType: .getPage(from: lastMessageId, pageSize: pageSize))
return HistoryRequestModel(requestInput: input)
}
@@ -77,4 +82,16 @@ final class HistoryRequestModelFactory: HistoryRequestModelFactoryProtocol {
actionType: .defaultStickerPack)
return HistoryRequestModel(requestInput: input)
}
+
+ func makeHistoryRequestModel(rosterId: String,
+ chat: ChatModel,
+ from: MessageServerId,
+ to: MessageServerId) throws -> HistoryRequestModel {
+
+ let historyType = try findModelType(for: chat)
+ let input = HistoryRequestModel.RequestInput(rosterId: rosterId,
+ historyType: historyType,
+ actionType: .getBetween(from: from, to: to))
+ return HistoryRequestModel(requestInput: input)
+ }
}
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..da4a164c918651cdd1e11dca5cc62c333f097601
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/marketplace_swap_button.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/marketplace_swap_button.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..497e4b30d30da927a65b856a5ec24d298d949bb4
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/marketplace_swap_button.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_swap_button.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/marketplace_swap_button.imageset/marketplace_swap_button.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/marketplace_swap_button.imageset/marketplace_swap_button.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..02beb1afb31dec6cac23b7d2528020e659270423
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/marketplace_swap_button.imageset/marketplace_swap_button.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..da4a164c918651cdd1e11dca5cc62c333f097601
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..da4a164c918651cdd1e11dca5cc62c333f097601
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/ic_close_clear.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_apps.imageset/Contents.json
similarity index 72%
rename from Nynja/Resources/Assets.xcassets/ic_close_clear.imageset/Contents.json
rename to Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_apps.imageset/Contents.json
index cfa020576cbeaf01a9a547a863f80ba30f5bb309..ec684e47e96910cee37ec049063e6281bcbd71ff 100644
--- a/Nynja/Resources/Assets.xcassets/ic_close_clear.imageset/Contents.json
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_apps.imageset/Contents.json
@@ -2,7 +2,7 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "ic_close_wheel_clear.pdf"
+ "filename" : "marketplace_apps.pdf"
}
],
"info" : {
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_apps.imageset/marketplace_apps.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_apps.imageset/marketplace_apps.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..e861a6a1e70b0075b6f3a91e176128ef0c826653
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_apps.imageset/marketplace_apps.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_bots.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_bots.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..92ea8cf368125d87dd1aabdb58a0bbb35702708f
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_bots.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_bots.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_bots.imageset/marketplace_bots.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_bots.imageset/marketplace_bots.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..775ed007da562d6b007d7255a41a2b4af1f63ad4
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_bots.imageset/marketplace_bots.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_groups_channels.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_groups_channels.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..c9c3b967726588c6a3b8d211865efcd15a29f79b
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_groups_channels.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_groups_channels.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_groups_channels.imageset/marketplace_groups_channels.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_groups_channels.imageset/marketplace_groups_channels.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..54f5cfc9a5d80854f469bc5485e43a77149a34a1
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/access/marketplace_groups_channels.imageset/marketplace_groups_channels.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..da4a164c918651cdd1e11dca5cc62c333f097601
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_design.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_design.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..4df94903ba94a7e4732cac24ec504fd82c50bdfd
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_design.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_design.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_design.imageset/marketplace_design.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_design.imageset/marketplace_design.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..56ac37bcf4e0fff09286d6cc6ebeb46207992fb8
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_design.imageset/marketplace_design.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_interpretation.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_interpretation.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..d4e643bea8053c6a573c92457f21e1d83fe713aa
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_interpretation.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_interpretation.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_interpretation.imageset/marketplace_interpretation.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_interpretation.imageset/marketplace_interpretation.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..dc1a3df1f8405acdbcdca5f00f556c8357f7300c
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_interpretation.imageset/marketplace_interpretation.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_support.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_support.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..27daa99ac982d0dfb5706383e7ed933b1e6f676f
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_support.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_support.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_support.imageset/marketplace_support.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_support.imageset/marketplace_support.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..4c10074a6fe0b01dbdf4a70f1e8b4fa61988b138
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/freelance/marketplace_support.imageset/marketplace_support.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..da4a164c918651cdd1e11dca5cc62c333f097601
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_access.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_access.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..edb120cb996ff7ac04ab02cbd2f801fad61ff826
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_access.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_access.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_access.imageset/marketplace_access.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_access.imageset/marketplace_access.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..213a0cb865dcda85c10c5259e9f017a083c1a53c
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_access.imageset/marketplace_access.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_freelance.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_freelance.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..ccbe15f4e4c1155e8a1f2c8834121755650ccd7a
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_freelance.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_freelance.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_freelance.imageset/marketplace_freelance.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_freelance.imageset/marketplace_freelance.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..e0b16b4819314344577f7ec26654e8dde64cdf2c
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_freelance.imageset/marketplace_freelance.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_virtual_goods.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_virtual_goods.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..2d94320ac498ef6bf1f77162d96a7e27220f508b
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_virtual_goods.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_virtual_goods.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_virtual_goods.imageset/marketplace_virtual_goods.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_virtual_goods.imageset/marketplace_virtual_goods.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..73dab8cfd122619eca6047fe9bd5b1a9df3f5d44
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/main/marketplace_virtual_goods.imageset/marketplace_virtual_goods.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..da4a164c918651cdd1e11dca5cc62c333f097601
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_media_content.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_media_content.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..9c778809139f207a44ff52e09100199add701ded
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_media_content.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_media_content.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_media_content.imageset/marketplace_media_content.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_media_content.imageset/marketplace_media_content.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..ce158d51ff5457dbc753b5cc25f5b6a8cb41f506
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_media_content.imageset/marketplace_media_content.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_sticker.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_sticker.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..e71566c365403c9a8b35591fefae4b59df1ba9bd
--- /dev/null
+++ b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_sticker.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "marketplace_sticker.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_sticker.imageset/marketplace_sticker.pdf b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_sticker.imageset/marketplace_sticker.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..605b2e85d1518a07d2876595fcefa09c5d47b6da
Binary files /dev/null and b/Nynja/Resources/Assets.xcassets/Marketplace/menu/virtual goods/marketplace_sticker.imageset/marketplace_sticker.pdf differ
diff --git a/Nynja/Resources/Assets.xcassets/ic_close_clear.imageset/ic_close_wheel_clear.pdf b/Nynja/Resources/Assets.xcassets/ic_close_clear.imageset/ic_close_wheel_clear.pdf
deleted file mode 100644
index 583dc73dcaf583016363daf9e2fe23890b36064f..0000000000000000000000000000000000000000
--- a/Nynja/Resources/Assets.xcassets/ic_close_clear.imageset/ic_close_wheel_clear.pdf
+++ /dev/null
@@ -1,3664 +0,0 @@
-%PDF-1.5
%
-1 0 obj
<>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<>stream
-
-
-
-
- application/pdf
-
-
- ic_close_wheel
-
-
- 2018-05-23T12:33:26+03:00
- 2018-05-23T12:33:26+03:00
- 2018-05-23T12:33:26+03:00
- Adobe Illustrator CC 2014 (Windows)
-
-
-
- 256
- 256
- JPEG
- /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F
XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX
Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY
q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX//2Q==
-
-
-
- uuid:32433c3f-d46d-4542-b0db-d7a2e136ea7c
- xmp.did:94a44799-036f-e744-828f-48fad356141f
- uuid:5D20892493BFDB11914A8590D31508C8
- proof:pdf
-
- uuid:d1c078a0-2746-42b2-b0d1-25aedff8fb1e
- xmp.did:1b6690ed-28a8-c141-9479-b6a9cf6be651
- uuid:5D20892493BFDB11914A8590D31508C8
- proof:pdf
-
-
-
-
- saved
- xmp.iid:94a44799-036f-e744-828f-48fad356141f
- 2018-05-23T12:33:23+03:00
- Adobe Illustrator CC 2014 (Windows)
- /
-
-
-
- Print
- False
- False
- 1
-
- 34.000000
- 34.000000
- Pixels
-
-
-
-
- Default Swatch Group
- 0
-
-
-
- White
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 0.000000
-
-
- Black
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 100.000000
-
-
- CMYK Red
- CMYK
- PROCESS
- 0.000000
- 100.000000
- 100.000000
- 0.000000
-
-
- CMYK Yellow
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 100.000000
- 0.000000
-
-
- CMYK Green
- CMYK
- PROCESS
- 100.000000
- 0.000000
- 100.000000
- 0.000000
-
-
- CMYK Cyan
- CMYK
- PROCESS
- 100.000000
- 0.000000
- 0.000000
- 0.000000
-
-
- CMYK Blue
- CMYK
- PROCESS
- 100.000000
- 100.000000
- 0.000000
- 0.000000
-
-
- CMYK Magenta
- CMYK
- PROCESS
- 0.000000
- 100.000000
- 0.000000
- 0.000000
-
-
- C=15 M=100 Y=90 K=10
- CMYK
- PROCESS
- 15.000000
- 100.000000
- 90.000000
- 10.000000
-
-
- C=0 M=90 Y=85 K=0
- CMYK
- PROCESS
- 0.000000
- 90.000000
- 85.000000
- 0.000000
-
-
- C=0 M=80 Y=95 K=0
- CMYK
- PROCESS
- 0.000000
- 80.000000
- 95.000000
- 0.000000
-
-
- C=0 M=50 Y=100 K=0
- CMYK
- PROCESS
- 0.000000
- 50.000000
- 100.000000
- 0.000000
-
-
- C=0 M=35 Y=85 K=0
- CMYK
- PROCESS
- 0.000000
- 35.000000
- 85.000000
- 0.000000
-
-
- C=5 M=0 Y=90 K=0
- CMYK
- PROCESS
- 5.000000
- 0.000000
- 90.000000
- 0.000000
-
-
- C=20 M=0 Y=100 K=0
- CMYK
- PROCESS
- 20.000000
- 0.000000
- 100.000000
- 0.000000
-
-
- C=50 M=0 Y=100 K=0
- CMYK
- PROCESS
- 50.000000
- 0.000000
- 100.000000
- 0.000000
-
-
- C=75 M=0 Y=100 K=0
- CMYK
- PROCESS
- 75.000000
- 0.000000
- 100.000000
- 0.000000
-
-
- C=85 M=10 Y=100 K=10
- CMYK
- PROCESS
- 85.000000
- 10.000000
- 100.000000
- 10.000000
-
-
- C=90 M=30 Y=95 K=30
- CMYK
- PROCESS
- 90.000000
- 30.000000
- 95.000000
- 30.000000
-
-
- C=75 M=0 Y=75 K=0
- CMYK
- PROCESS
- 75.000000
- 0.000000
- 75.000000
- 0.000000
-
-
- C=80 M=10 Y=45 K=0
- CMYK
- PROCESS
- 80.000000
- 10.000000
- 45.000000
- 0.000000
-
-
- C=70 M=15 Y=0 K=0
- CMYK
- PROCESS
- 70.000000
- 15.000000
- 0.000000
- 0.000000
-
-
- C=85 M=50 Y=0 K=0
- CMYK
- PROCESS
- 85.000000
- 50.000000
- 0.000000
- 0.000000
-
-
- C=100 M=95 Y=5 K=0
- CMYK
- PROCESS
- 100.000000
- 95.000000
- 5.000000
- 0.000000
-
-
- C=100 M=100 Y=25 K=25
- CMYK
- PROCESS
- 100.000000
- 100.000000
- 25.000000
- 25.000000
-
-
- C=75 M=100 Y=0 K=0
- CMYK
- PROCESS
- 75.000000
- 100.000000
- 0.000000
- 0.000000
-
-
- C=50 M=100 Y=0 K=0
- CMYK
- PROCESS
- 50.000000
- 100.000000
- 0.000000
- 0.000000
-
-
- C=35 M=100 Y=35 K=10
- CMYK
- PROCESS
- 35.000000
- 100.000000
- 35.000000
- 10.000000
-
-
- C=10 M=100 Y=50 K=0
- CMYK
- PROCESS
- 10.000000
- 100.000000
- 50.000000
- 0.000000
-
-
- C=0 M=95 Y=20 K=0
- CMYK
- PROCESS
- 0.000000
- 95.000000
- 20.000000
- 0.000000
-
-
- C=25 M=25 Y=40 K=0
- CMYK
- PROCESS
- 25.000000
- 25.000000
- 40.000000
- 0.000000
-
-
- C=40 M=45 Y=50 K=5
- CMYK
- PROCESS
- 40.000000
- 45.000000
- 50.000000
- 5.000000
-
-
- C=50 M=50 Y=60 K=25
- CMYK
- PROCESS
- 50.000000
- 50.000000
- 60.000000
- 25.000000
-
-
- C=55 M=60 Y=65 K=40
- CMYK
- PROCESS
- 55.000000
- 60.000000
- 65.000000
- 40.000000
-
-
- C=25 M=40 Y=65 K=0
- CMYK
- PROCESS
- 25.000000
- 40.000000
- 65.000000
- 0.000000
-
-
- C=30 M=50 Y=75 K=10
- CMYK
- PROCESS
- 30.000000
- 50.000000
- 75.000000
- 10.000000
-
-
- C=35 M=60 Y=80 K=25
- CMYK
- PROCESS
- 35.000000
- 60.000000
- 80.000000
- 25.000000
-
-
- C=40 M=65 Y=90 K=35
- CMYK
- PROCESS
- 40.000000
- 65.000000
- 90.000000
- 35.000000
-
-
- C=40 M=70 Y=100 K=50
- CMYK
- PROCESS
- 40.000000
- 70.000000
- 100.000000
- 50.000000
-
-
- C=50 M=70 Y=80 K=70
- CMYK
- PROCESS
- 50.000000
- 70.000000
- 80.000000
- 70.000000
-
-
-
-
-
- Grays
- 1
-
-
-
- C=0 M=0 Y=0 K=100
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 100.000000
-
-
- C=0 M=0 Y=0 K=90
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 89.999400
-
-
- C=0 M=0 Y=0 K=80
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 79.998800
-
-
- C=0 M=0 Y=0 K=70
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 69.999700
-
-
- C=0 M=0 Y=0 K=60
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 59.999100
-
-
- C=0 M=0 Y=0 K=50
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 50.000000
-
-
- C=0 M=0 Y=0 K=40
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 39.999400
-
-
- C=0 M=0 Y=0 K=30
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 29.998800
-
-
- C=0 M=0 Y=0 K=20
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 19.999700
-
-
- C=0 M=0 Y=0 K=10
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 9.999100
-
-
- C=0 M=0 Y=0 K=5
- CMYK
- PROCESS
- 0.000000
- 0.000000
- 0.000000
- 4.998800
-
-
-
-
-
- Brights
- 1
-
-
-
- C=0 M=100 Y=100 K=0
- CMYK
- PROCESS
- 0.000000
- 100.000000
- 100.000000
- 0.000000
-
-
- C=0 M=75 Y=100 K=0
- CMYK
- PROCESS
- 0.000000
- 75.000000
- 100.000000
- 0.000000
-
-
- C=0 M=10 Y=95 K=0
- CMYK
- PROCESS
- 0.000000
- 10.000000
- 95.000000
- 0.000000
-
-
- C=85 M=10 Y=100 K=0
- CMYK
- PROCESS
- 85.000000
- 10.000000
- 100.000000
- 0.000000
-
-
- C=100 M=90 Y=0 K=0
- CMYK
- PROCESS
- 100.000000
- 90.000000
- 0.000000
- 0.000000
-
-
- C=60 M=90 Y=0 K=0
- CMYK
- PROCESS
- 60.000000
- 90.000000
- 0.003100
- 0.003100
-
-
-
-
-
-
- Adobe PDF library 11.00
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-endstream
endobj
3 0 obj
<>
endobj
7 0 obj
<>/Resources<>/Properties<>>>/Thumb 11 0 R/TrimBox[0.0 0.0 34.0 34.0]/Type/Page>>
endobj
8 0 obj
<>stream
-HlMj0:N؉mӡ.zvQwa
-c:D>Kz<ɞWΫÇf#E
>X@%[@&qD1
,pDJqni@NOg!buTzJ
UMNcSh;UznPe>tnSY__F/EunH=YȻ
- mT6pdLIPox}<=,Jp:קW \?
-endstream
endobj
11 0 obj
<>stream
-8;Xp,*>JPW(]\SI%<2~>
-endstream
endobj
12 0 obj
[/Indexed/DeviceRGB 255 13 0 R]
endobj
13 0 obj
<>stream
-8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
-b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
-E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn
-6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O(
-l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>
-endstream
endobj
5 0 obj
<>
endobj
14 0 obj
[/View/Design]
endobj
15 0 obj
<>>>
endobj
10 0 obj
<>
endobj
9 0 obj
<>
endobj
16 0 obj
<>
endobj
17 0 obj
<>stream
-%!PS-Adobe-3.0
-%%Creator: Adobe Illustrator(R) 17.0
-%%AI8_CreatorVersion: 18.0.0
-%%For: (Desi Karavelikova) ()
-%%Title: (Untitled-1)
-%%CreationDate: 5/23/2018 12:33 PM
-%%Canvassize: 16383
-%%BoundingBox: 6 -28 28 -6
-%%HiResBoundingBox: 6.91000258922577 -27.0899973277592 27.0899974107742 -6.91000274462749
-%%DocumentProcessColors:
-%AI5_FileFormat 13.0
-%AI12_BuildNumber: 18
-%AI3_ColorUsage: Color
-%AI7_ImageSettings: 0
-%%CMYKProcessColor: 1 1 1 1 ([Registration])
-%AI3_Cropmarks: 0 -34 34 0
-%AI3_TemplateBox: 17.5 -17.5 17.5 -17.5
-%AI3_TileBox: -265 -395 305 355
-%AI3_DocumentPreview: None
-%AI5_ArtSize: 14400 14400
-%AI5_RulerUnits: 6
-%AI9_ColorModel: 2
-%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0
-%AI5_TargetResolution: 800
-%AI5_NumLayers: 1
-%AI9_OpenToView: -15 1 25.56 1626 923 18 0 0 46 112 0 0 0 1 1 0 1 1 0 1
-%AI5_OpenViewLayers: 7
-%%PageOrigin:-289 -413
-%AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142
-%AI9_Flatten: 1
-%AI12_CMSettings: 00.MS
-%%EndComments
-
-endstream
endobj
18 0 obj
<>stream
-%%BoundingBox: 6 -28 28 -6
-%%HiResBoundingBox: 6.91000258922577 -27.0899973277592 27.0899974107742 -6.91000274462749
-%AI7_Thumbnail: 128 128 8
-%%BeginData: 2039 Hex Bytes
-%0000330000660000990000CC0033000033330033660033990033CC0033FF
-%0066000066330066660066990066CC0066FF009900009933009966009999
-%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66
-%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333
-%3333663333993333CC3333FF3366003366333366663366993366CC3366FF
-%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99
-%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033
-%6600666600996600CC6600FF6633006633336633666633996633CC6633FF
-%6666006666336666666666996666CC6666FF669900669933669966669999
-%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33
-%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF
-%9933009933339933669933999933CC9933FF996600996633996666996699
-%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33
-%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF
-%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399
-%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933
-%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF
-%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC
-%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699
-%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33
-%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100
-%000011111111220000002200000022222222440000004400000044444444
-%550000005500000055555555770000007700000077777777880000008800
-%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB
-%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF
-%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF
-%524C45FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF
-%FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF
-%FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF
-%FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF
-%FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF
-%FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF
-%FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFFFFFFFF
-%%EndData
-
-endstream
endobj
19 0 obj
<>stream
-HOfGIwuuu&+EЕAeI`܁l$\gspAFno{kĻ:;L,gqekpplf9ʃuzdoՇsо/ǙT/#ל6gVTW[q`xS9}>#Ie}ZBF
-A1&fLmjf&m|L)EK,FXћkeU! Fͺݦp ȓsH3lbm4Bܗ[J*gHДuȫtom5!*m9"s?G<-+d6>{о'ͼ=/b]1$eĚ}ósXM>sװ?kk}+G_ۗj=]ŋiT8h2=b6&>jNcdޛi533An
gk]{P^#]=Fؔ0BCѨ; 1p#^#|eiӯW0HmQ;0Hc~9CAtEcN[p6lR%O WCƃ.ohɝ!=<1L\̏E@4d~7tkR'Ԑz4/fA4"38 аc
s'@ДɷaDY|~jaa*Qf?DCPB&88&
b
-Ѷ02)*BVTE;Qՠ:l3
-yQd)')K{V@á% <,~c:N5};hTC o>t<1 &No
-z]@.ZpKcc`i6"QNhhZu"xQ1Νc>Rb1e#)э eک{iPabfq.ia%װjxDSJu3@' 3=H`gC-h'm%ʸM
-ahV!)˂}EU/Ӌ%09{+n3=/te~7!QOI*/zdFR#K)YQ8¥tkڣ]R1GLtN]l['d<ڲ,g˪Mi!C7X!CbBlt6igBu`֔zHȎoJѨkV #^4)hD#p7Ge=$J-tK'#+D [A +|y0K;|ަzGSM0
C>[ .!X+}c*6f#f̀ਃ6`STBߎ3HB/ Q ux!^AavC$\^SgL\3| }Zu- yOZT-YizYS'
UKLe=ɞ$4&eDP6e#%i]ުվ/L`\QP4Ԧ$Dqk98FEaȹjXxb iY`HFC@V[pcٵGS p;x3hMW&]'lȵA;~bL!.M+tƱTr9|f)P*!ф ȑv}y)u*-/dh&ծJznqqRWWU"A"> UHZZ|t\Y\+Smn4dwݡJTaܥuE\*~K@Gy$SHl>ES-ؒUR:H>Iï|?=g_b/__yo;AGGMmn!6g<)
0o툈.'La
q8JԨD3)Օx.\W6IFJ-%@t.
~צ`V.DL{1?Y:kq2\}Ԓ6{(Qpsc4R;rK7@mQ"`R*)\0(R~{!.fo(=U]uCkFϸ@̸=5K1|YΜui:R
A*+(nN&'sn3jXÒ
!WPf:H.:F8]<8,qd"jJynTC+fM ,HF7 c
C&)u-FO7MU],9~H]҈|N
yca 3! )](`(JX^n96ʜ` <;"ճ?c,bAA#'B),pMBS-G~6K2eV#ayL9HDB =Le3N.[VbGi9~~xqCoH7@(\"x[
$P)[8LZn92O}qjP܆[l[L
Cc=P@Ê7+7$B;o ǸCQu<an,_*3do
-jDX&6oaɔNΚB:NHU .OP.N7a=`<
njAq]n2x2b9$[PX~$Q Iw[mbcj|;o'yT# o蕺D#|x>~SZpߓo