From 3a0b7935f6211cfe335d938971c9e76fbe58b853 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Mon, 29 Oct 2018 13:25:47 +0200 Subject: [PATCH 01/41] 0.5.3.2.RC --- Nynja-Share/Resources/Info.plist | 2 +- Nynja/Resources/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist index 24d168755..1be95d85a 100644 --- a/Nynja-Share/Resources/Info.plist +++ b/Nynja-Share/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.3.1.RC + 0.5.3.2.RC Config $(Config) ModelsVersion diff --git a/Nynja/Resources/Info.plist b/Nynja/Resources/Info.plist index b90e8abb4..dc27d29cf 100644 --- a/Nynja/Resources/Info.plist +++ b/Nynja/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.3.1.RC + 0.5.3.2.RC ConfServerAddress $(ConfServerAddress) ConfServerPort -- GitLab From e40d2a1869f1801b5f2926c06c2719991b60b052 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Thu, 15 Nov 2018 11:34:52 +0200 Subject: [PATCH 02/41] Merge 0.5.4.RC (#1471) * Merge * [NY-5256] Turn off auto translate as default and change the text (#1476) * Fix (#1475) * [NY-4861] In case message was auto transcribed and auto translated - 3 unread messages displayed (#1477) * 0.5.4.1.RC * Update message VC when recieve push notification (#1478) * Enable set empty string to last name --- .../Base.lproj/MainInterface.storyboard | 28 +- Nynja-Share/Resources/Info.plist | 2 +- .../AttachmentProvider.swift | 44 +- .../AttachmentProviding.swift | 4 +- .../AttachmentTransformer.swift | 2 +- .../AttachmentTransformerImpl.swift | 14 +- .../ServiceFactory/ServiceFactory.swift | 31 ++ .../Entities/ServerSignal.swift | 14 + .../ForwardSelectorProtocols.swift | 26 +- .../ForwardSelectorInteractor.swift | 205 ++++---- .../Presenter/ForwardSelectorPresenter.swift | 41 +- .../View/ForwardSelectorViewController.swift | 174 +++---- .../View/Views/ProgressHUD.swift | 26 +- .../Wireframe/ForwardSelectorWireFrame.swift | 45 ++ Nynja-Share/UI/RootNavigationController.swift | 17 + Nynja.xcodeproj/project.pbxproj | 289 ++++++++--- .../xcschemes/Nynja-Share.xcscheme | 24 +- Nynja/AmazonManager.swift | 12 +- Nynja/AppDelegate.swift | 35 +- Nynja/ChatItemsFactory.swift | 12 +- Nynja/DB/Models/DBConvertMessage.swift | 23 +- Nynja/DB/Tables/ConvertMessageTable.swift | 3 + Nynja/DefaultMessageProcessingManager.swift | 29 +- Nynja/Extensions/Bundle+Keys.swift | 4 + Nynja/Extensions/Locale+Language.swift | 26 + .../Models/Desc/Desc+Messages.swift | 10 + .../Models/Feature/FeatureExtension.swift | 14 + .../Models/Message/Message+Factory.swift | 34 +- .../SwiftLibrary/Array/Array+Message.swift | 7 +- Nynja/Generated/AssetsConstants.swift | 2 + Nynja/Generated/ColorsConstants.swift | 4 +- Nynja/Generated/LocalizableConstants.swift | 76 ++- Nynja/HomeItemsFactory.swift | 1 - .../MessageFactory/MessageFactory.swift | 12 +- Nynja/Library/UI/BannerView.swift | 79 +++ Nynja/Library/UI/DateConverter.swift | 2 +- .../UI/Extensions/DateExtensions.swift | 2 +- Nynja/Library/UI/Extensions/Localizable.swift | 40 +- .../String/NSAttributedString+Bounds.swift | 18 + .../{ => String}/StringExtensions.swift | 34 +- .../UI/UIImageView/UIImageView+SetImage.swift | 36 +- .../KeyboardLayoutGuide.swift | 38 +- .../SafeArea/UIViewController+SafeArea.swift | 4 +- Nynja/Library/UI/SnackBar/SnackBar.swift | 224 ++++++++ .../UI/SnackBar/SnackBarLayoutGuide.swift | 72 +++ Nynja/Library/UI/StarDateConverter.swift | 2 +- .../ChatSettingCell/AvatarCell.swift | 3 +- .../UI/View/CollapsedView/CollapsedView.swift | 68 ++- .../Translation/TranslationAutoView.swift | 21 +- .../Translation/TranslationManualView.swift | 39 +- .../Translation/TranslationViewProtocol.swift | 2 +- .../UI/WheelContainer/WheelContainer.swift | 4 +- Nynja/LogService/LogService.swift | 73 --- Nynja/LogService/LogWriter.swift | 76 --- Nynja/MessageDAO.swift | 7 + Nynja/MessagesProcessingManager.swift | 2 +- Nynja/MigrationManager.swift | 7 + .../Presenter/AddParticipantsPresenter.swift | 64 ++- .../View/AddParticipantsViewController.swift | 6 +- .../Auth/Interactor/AuthInteractor.swift | 166 ------ .../Call/CallInProgressProtocols.swift | 8 + .../Interactor/CallInProgressInteractor.swift | 43 +- .../Presenter/CallInProgressPresenter.swift | 18 +- .../View/CallInProgressViewController.swift | 62 ++- .../Call/View/GroupCollectionViewCell.swift | 12 +- .../Call/View/MultiPageCollectionView.swift | 4 +- .../ContactsViewController.swift | 8 +- .../Camera/Interactor/CameraInteractor.swift | 1 - .../CameraQualitySettingsViewController.swift | 8 +- .../View/CameraSettingsViewController.swift | 5 +- .../ForwardSelectorProtocols+ShareExt.swift | 2 - .../ForwardSelectorProtocols.swift | 2 + .../Interactor/GroupStorageInteractor.swift | 2 +- .../View/Files/GroupFilesCell.swift | 2 +- Nynja/Modules/History/HistoryProtocols.swift | 2 + .../History/Presenter/HistoryPresenter.swift | 4 + .../History/View/HistoryViewController.swift | 25 +- .../History/WireFrame/HistoryWireframe.swift | 3 + .../Interpretation/InterpretationModel.swift | 4 +- .../View/InterpretationViewController.swift | 4 +- .../View/LanguagePickerDelegate.swift | 10 +- .../Interactor/InviteFriendsInteractor.swift | 4 +- .../LanguageSelectroInteractor.swift | 2 +- .../LanguageSelectorProtocols.swift | 8 +- .../Presenter/LanguageSelectorPresenter.swift | 55 +- .../TableView/Cell/LangCellViewModel.swift | 4 +- .../LanguageSettingProtocol.swift | 120 +++-- .../LanguageSettings+Helper.swift | 43 +- .../ChatLanguageSettingsProtocols.swift | 13 +- .../ChatLanguageSettingsInteractor.swift | 48 +- .../ChatLanguageSettingsPresenter.swift | 285 ++++++----- .../LanguageSectionCoordinator.swift | 14 - .../ChatLanguageSettingsViewController.swift | 10 +- .../View/TableView/Cell/ActionCell.swift | 30 +- .../TableView/Cell/ActionCellViewModel.swift | 2 +- .../View/TableView/Cell/DescriptionCell.swift | 22 + .../TableView/Cell/OptionallyActionCell.swift | 93 ++++ .../Cell/OptionallyActionCellViewModel.swift | 18 + .../TableView/Cell/SwitchableActionCell.swift | 13 +- .../ChatLanguageSettingsTableDataSource.swift | 12 + Nynja/Modules/Main/MainProtocols.swift | 6 + .../Main/Presenter/MainPresenter.swift | 4 + .../Main/View/MainNavigationItem.swift | 1 + .../View/MainViewController+Gallery.swift | 4 +- .../MainViewController+NavigateProtocol.swift | 9 +- .../Main/View/MainViewController.swift | 21 +- .../Main/View/MainViewControllerLayout.swift | 28 +- .../Modules/Main/View/NavigateProtocol.swift | 2 +- .../Main/WireFrame/MainWireframe.swift | 7 + .../MessageInteractor+AutoConversion.swift | 140 +++++ .../MessageInteractor+StorageSubscriber.swift | 10 +- .../MessageInteractor+Transcription.swift | 88 ++-- .../MessageInteractor+Translation.swift | 479 ++++++++---------- .../Interactor/MessageInteractor+Utils.swift | 16 + .../Interactor/MessageInteractor.swift | 63 ++- .../Message/Presenter/MessagePresenter.swift | 159 +++--- .../Message/Protocols/ConversionState.swift | 14 + .../Message/Protocols/MessageProtocols.swift | 30 +- Nynja/Modules/Message/View/MessageVC.swift | 211 +++++--- .../Message/View/MessageVCLayout.swift | 7 + .../View/Views/AvatarView/AvatarView.swift | 3 +- .../ChatCells/BaseChatCell/BaseChatCell.swift | 16 +- .../BaseChatCell/BaseChatCellLayout.swift | 18 +- .../BaseChatCell/OponentChatCell.swift | 1 + .../Views/CollectionView/Cells/TimeCell.swift | 2 +- .../Cells/Views/Base/BubbleInjectible.swift | 10 +- .../Cells/Views/Base/MessageContentView.swift | 11 + .../Cells/Views/Base/MessageViewFactory.swift | 125 +++-- .../Views/Converting/ConvertionInfoView.swift | 102 ++-- .../Converting/MessageConvertionView.swift | 27 +- .../Cells/Views/Message/MessageCallView.swift | 29 +- .../Views/Message/MessageContactView.swift | 26 +- .../Cells/Views/Message/MessageFileView.swift | 38 +- .../Views/Message/MessageForwardView.swift | 115 +++-- .../Views/Message/MessageImageView.swift | 17 +- .../Views/Message/MessageLocationView.swift | 32 +- .../Views/Message/MessagePlaceView.swift | 18 +- .../Views/Message/MessageStickerView.swift | 45 +- .../Cells/Views/Message/MessageTextView.swift | 43 +- .../Views/Message/MessageTransferView.swift | 26 +- .../Views/Message/MessageVideoView.swift | 29 +- .../Views/Message/MessageVoiceView.swift | 24 +- .../Views/Reply/MessageRepliedView.swift | 36 +- .../Reply/MessageStickerRepliedView.swift | 19 +- .../Cells/Views/Reply/ReplyInfoView.swift | 8 +- .../Message/WireFrame/MessageWireframe.swift | 4 + .../WireFrame/OtherUserWireFrame.swift | 2 +- .../ProfileScheduledMesssageCell.swift | 4 +- .../Interactor/RepliesInteractor.swift | 4 +- .../Replies/Presenter/RepliesPresenter.swift | 4 +- Nynja/Modules/Replies/RepliesProtocols.swift | 4 +- Nynja/Modules/Replies/View/RepliesVC.swift | 6 +- .../Views/DateTime/DateTimeItemView.swift | 2 +- .../LanguageSettingsInteractor.swift | 12 +- .../LanguageSettingsProtocols.swift | 12 +- .../Presenter/LanguageSettingsPresenter.swift | 6 +- .../Interactor/SecurityInteractor.swift | 2 +- .../Interactor/SettingsGroupInteractor.swift | 23 +- .../Presenter/SettingsGroupPresenter.swift | 14 +- .../SettingsGroup/SettingsProtocols.swift | 2 +- .../WireFrame/SettingsGroupWireFrame.swift | 2 +- .../Splash/Interactor/SplashInteractor.swift | 2 +- Nynja/MotionManager/MotionManager.swift | 117 ----- Nynja/NotificationManager.swift | 38 +- Nynja/P2pChatItemsFactory.swift | 2 +- Nynja/P2pChatsItemsFactory.swift | 6 +- .../Contents.json | 16 + .../ic_p2p_chat_settings.pdf | Bin 0 -> 7596 bytes Nynja/Resources/Colors.json | 2 +- Nynja/Resources/DevConfig.xcconfig | 3 +- Nynja/Resources/Info.plist | 4 +- Nynja/Resources/Nynja.entitlements | 4 + Nynja/Resources/PrereleaseConfig.xcconfig | 3 +- Nynja/Resources/ReleaseConfig.xcconfig | 1 + Nynja/Resources/en.lproj/Localizable.strings | 47 +- .../NotificationSettingProtocol.swift | 4 +- Nynja/ServerModel/Source/Decoder.swift | 2 + .../ConversationLanguageSettingService.swift | 129 +++-- ...sationLanguageSettingServiceProtocol.swift | 38 +- Nynja/Services/FeatureFactory.swift | 13 +- .../HandleServices/HistoryHandler.swift | 6 + .../HandleServices/MessageHandler.swift | 5 +- .../HandleServices/ProfileHandler.swift | 28 +- Nynja/Services/MQTT/MQTTService.swift | 2 + Nynja/Services/MQTT/MQTTServiceAuth.swift | 10 +- Nynja/Services/MQTT/MQTTServiceProfile.swift | 6 +- .../Operations/UploadOperation.swift | 4 +- .../NynjaCalls/NynjaCommunicatorService.swift | 115 ++++- .../NynjaCalls/NynjaJoinByLinkService.swift | 106 ++++ Nynja/Services/PushService.swift | 6 + .../ServiceFactory/ServiceFactory.swift | 27 +- .../TranscribeService/TranscribeService.swift | 71 +-- .../Manager/WCDataManager.swift | 1 - Nynja/SyncFileManager/SyncFileManager.swift | 29 -- .../Model/LangExtended.swift | 38 -- Nynja/TranslationService/Model/Language.swift | 26 + .../TranslationService.swift | 39 +- .../Validation/UseCaseValidationService.swift | 4 +- Podfile | 6 +- Podfile.lock | 8 +- .../Models/Message/Message+Files.swift | 12 + .../Models/Message/Message+LocalStatus.swift | 2 + .../AmazonInitializer/AmazonInitializer.swift | 13 + .../AmazonInitializerImpl.swift | 35 ++ .../SharedServiceFactory.swift | 41 ++ 205 files changed, 4189 insertions(+), 2508 deletions(-) create mode 100644 Nynja-Share/Services/ServiceFactory/ServiceFactory.swift create mode 100644 Nynja-Share/UI/ForwardSelector/Entities/ServerSignal.swift create mode 100644 Nynja-Share/UI/ForwardSelector/Wireframe/ForwardSelectorWireFrame.swift create mode 100644 Nynja-Share/UI/RootNavigationController.swift create mode 100644 Nynja/Extensions/Locale+Language.swift create mode 100644 Nynja/Library/UI/BannerView.swift create mode 100644 Nynja/Library/UI/Extensions/String/NSAttributedString+Bounds.swift rename Nynja/Library/UI/Extensions/{ => String}/StringExtensions.swift (89%) create mode 100644 Nynja/Library/UI/SnackBar/SnackBar.swift create mode 100644 Nynja/Library/UI/SnackBar/SnackBarLayoutGuide.swift delete mode 100644 Nynja/LogService/LogService.swift delete mode 100644 Nynja/LogService/LogWriter.swift delete mode 100644 Nynja/Modules/Auth/Interactor/AuthInteractor.swift delete mode 100644 Nynja/Modules/LanguageSettings/LanguageSettings/Presenter/LanguageSectionCoordinator.swift create mode 100644 Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/DescriptionCell.swift create mode 100644 Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/OptionallyActionCell.swift create mode 100644 Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/OptionallyActionCellViewModel.swift create mode 100644 Nynja/Modules/Message/Interactor/MessageInteractor+AutoConversion.swift create mode 100644 Nynja/Modules/Message/Protocols/ConversionState.swift delete mode 100644 Nynja/MotionManager/MotionManager.swift create mode 100644 Nynja/Resources/Assets.xcassets/Wheel/WheelItems/ic_p2p_chat_settings.imageset/Contents.json create mode 100644 Nynja/Resources/Assets.xcassets/Wheel/WheelItems/ic_p2p_chat_settings.imageset/ic_p2p_chat_settings.pdf create mode 100644 Nynja/Services/NynjaCalls/NynjaJoinByLinkService.swift delete mode 100644 Nynja/TranslationService/Model/LangExtended.swift create mode 100644 Nynja/TranslationService/Model/Language.swift create mode 100644 Shared/Services/AmazonInitializer/AmazonInitializer.swift create mode 100644 Shared/Services/AmazonInitializer/AmazonInitializerImpl.swift create mode 100644 Shared/Services/SharedServiceFactory/SharedServiceFactory.swift diff --git a/Nynja-Share/Resources/Base.lproj/MainInterface.storyboard b/Nynja-Share/Resources/Base.lproj/MainInterface.storyboard index 51b8c2205..77f982a08 100644 --- a/Nynja-Share/Resources/Base.lproj/MainInterface.storyboard +++ b/Nynja-Share/Resources/Base.lproj/MainInterface.storyboard @@ -1,43 +1,23 @@ - + - - + - - - - - - - - - - - - - - - - - + - + - - - diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist index 1be95d85a..917069f8e 100644 --- a/Nynja-Share/Resources/Info.plist +++ b/Nynja-Share/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.3.2.RC + 0.5.4.1.RC Config $(Config) ModelsVersion diff --git a/Nynja-Share/Services/AttachmentProvider/AttachmentProvider.swift b/Nynja-Share/Services/AttachmentProvider/AttachmentProvider.swift index 0828cb662..4280fc970 100644 --- a/Nynja-Share/Services/AttachmentProvider/AttachmentProvider.swift +++ b/Nynja-Share/Services/AttachmentProvider/AttachmentProvider.swift @@ -9,10 +9,11 @@ import Foundation final class AttachmentProvider: AttachmentProviding, InitializeInjectable { - typealias Transformer = (NSSecureCoding?) -> Attachment? - + private typealias CompletionHandler = ([Attachment]) -> Void + private let processingQueue: DispatchQueue + // MARK: - Init @@ -20,6 +21,7 @@ final class AttachmentProvider: AttachmentProviding, InitializeInjectable { init(transformer: AttachmentTransformer) { self.transformer = transformer + self.processingQueue = DispatchQueue(label: "attachment-provider-queue", qos: .default) } @@ -27,15 +29,14 @@ final class AttachmentProvider: AttachmentProviding, InitializeInjectable { let supportedAttachments: [Attachment.Kind] = Attachment.Kind.allValues - func attachment(from context: NSExtensionContext, handler: @escaping AttachmentProvidingHandler) { - guard let item = context.inputItems[0] as? NSExtensionItem, + func attachment(from context: NSExtensionContext, completion: @escaping AttachmentProvidingCompletion) { + guard let item = context.inputItems.first as? NSExtensionItem, let providers = item.attachments as? [NSItemProvider] else { - handler(nil) - return + completion(nil) + return } - self.attachments(from: providers) { attachments in - handler(attachments.first) + completion(self.mainAttachment(from: attachments)) } } @@ -43,25 +44,29 @@ final class AttachmentProvider: AttachmentProviding, InitializeInjectable { var attachments: [Attachment] = [] let group = DispatchGroup() - DispatchQueue.concurrentPerform(iterations: providers.count) { (index) in + DispatchQueue.concurrentPerform(iterations: providers.count) { index in let itemProvider = providers[index] supportedAttachments.forEach { kind in guard canHandle(itemProvider: itemProvider, kind: kind) else { return } - group.enter() itemProvider.loadItem( forTypeIdentifier: kind.utType, options: nil, - completionHandler: { [weak self] (coding, error) in + completionHandler: { [weak self] coding, error in + defer { group.leave() } - if let attachement = self?.transformer.transform(toKind: kind, coding: coding) { - attachments.append(attachement) - } - group.leave() + guard let `self` = self, let coding = coding else { + return + } + if let attachement = self.transformer.transform(toKind: kind, coding: coding) { + self.processingQueue.sync { + attachments.append(attachement) + } + } }) } } @@ -71,6 +76,15 @@ final class AttachmentProvider: AttachmentProviding, InitializeInjectable { } } + /// Extension context may have more than one attachment, for example '.text' and '.file'. + /// So, we will consider non-text attachment as main attachment. + private func mainAttachment(from attachments: [Attachment]) -> Attachment? { + guard !attachments.isEmpty else { + return nil + } + return attachments.first { $0.kind != .text } ?? attachments.first + } + private func canHandle(itemProvider: NSItemProvider, kind: Attachment.Kind) -> Bool { return itemProvider.hasItemConformingToTypeIdentifier(kind.utType) } diff --git a/Nynja-Share/Services/AttachmentProvider/AttachmentProviding.swift b/Nynja-Share/Services/AttachmentProvider/AttachmentProviding.swift index 607ad5d31..e2e6ba04f 100644 --- a/Nynja-Share/Services/AttachmentProvider/AttachmentProviding.swift +++ b/Nynja-Share/Services/AttachmentProvider/AttachmentProviding.swift @@ -8,9 +8,9 @@ import Foundation -typealias AttachmentProvidingHandler = (Attachment?) -> Void +typealias AttachmentProvidingCompletion = (Attachment?) -> Void protocol AttachmentProviding { var supportedAttachments: [Attachment.Kind] { get } - func attachment(from context: NSExtensionContext, handler: @escaping AttachmentProvidingHandler) + func attachment(from context: NSExtensionContext, completion: @escaping AttachmentProvidingCompletion) } diff --git a/Nynja-Share/Services/AttachmentProvider/AttachmentTransformer/AttachmentTransformer.swift b/Nynja-Share/Services/AttachmentProvider/AttachmentTransformer/AttachmentTransformer.swift index 26d2d4d50..b91196e9c 100644 --- a/Nynja-Share/Services/AttachmentProvider/AttachmentTransformer/AttachmentTransformer.swift +++ b/Nynja-Share/Services/AttachmentProvider/AttachmentTransformer/AttachmentTransformer.swift @@ -9,5 +9,5 @@ import Foundation protocol AttachmentTransformer { - func transform(toKind kind: Attachment.Kind, coding: NSSecureCoding?) -> Attachment? + func transform(toKind kind: Attachment.Kind, coding: NSSecureCoding) -> Attachment? } diff --git a/Nynja-Share/Services/AttachmentProvider/AttachmentTransformer/AttachmentTransformerImpl.swift b/Nynja-Share/Services/AttachmentProvider/AttachmentTransformer/AttachmentTransformerImpl.swift index 9c5de2935..011ca3f47 100644 --- a/Nynja-Share/Services/AttachmentProvider/AttachmentTransformer/AttachmentTransformerImpl.swift +++ b/Nynja-Share/Services/AttachmentProvider/AttachmentTransformer/AttachmentTransformerImpl.swift @@ -9,9 +9,9 @@ import Foundation import UIKit.UIImage -class AttachmentTransformerImpl: AttachmentTransformer { +final class AttachmentTransformerImpl: AttachmentTransformer { - func transform(toKind kind: Attachment.Kind, coding: NSSecureCoding?) -> Attachment? { + func transform(toKind kind: Attachment.Kind, coding: NSSecureCoding) -> Attachment? { switch kind { case .image: return transformToImage(fromCoding: coding) @@ -26,7 +26,7 @@ class AttachmentTransformerImpl: AttachmentTransformer { } } - private func transformToImage(fromCoding coding: NSSecureCoding?) -> Attachment? { + private func transformToImage(fromCoding coding: NSSecureCoding) -> Attachment? { if let url = coding as? URL { return transformToImage(fromUrl: url) } else if let image = coding as? UIImage { @@ -49,7 +49,7 @@ class AttachmentTransformerImpl: AttachmentTransformer { return transformToImage(fromUrl: url) } - private func transformToVideo(fromCoding coding: NSSecureCoding?) -> Attachment? { + private func transformToVideo(fromCoding coding: NSSecureCoding) -> Attachment? { guard let url = coding as? URL else { return nil } @@ -57,7 +57,7 @@ class AttachmentTransformerImpl: AttachmentTransformer { return Attachment(kind: .video, content: .url(url)) } - private func transformToFile(fromCoding coding: NSSecureCoding?) -> Attachment? { + private func transformToFile(fromCoding coding: NSSecureCoding) -> Attachment? { guard let url = coding as? URL else { return nil } @@ -65,7 +65,7 @@ class AttachmentTransformerImpl: AttachmentTransformer { return Attachment(kind: .file, content: .url(url)) } - private func transformToLink(fromCoding coding: NSSecureCoding?) -> Attachment? { + private func transformToLink(fromCoding coding: NSSecureCoding) -> Attachment? { guard let url = coding as? URL else { return nil } @@ -73,7 +73,7 @@ class AttachmentTransformerImpl: AttachmentTransformer { return Attachment(kind: .text, content: .text(url.absoluteString)) } - private func tranformToText(fromCoding coding: NSSecureCoding?) -> Attachment? { + private func tranformToText(fromCoding coding: NSSecureCoding) -> Attachment? { guard let text = coding as? String else { return nil } diff --git a/Nynja-Share/Services/ServiceFactory/ServiceFactory.swift b/Nynja-Share/Services/ServiceFactory/ServiceFactory.swift new file mode 100644 index 000000000..22523ac00 --- /dev/null +++ b/Nynja-Share/Services/ServiceFactory/ServiceFactory.swift @@ -0,0 +1,31 @@ +// +// ServiceFactory.swift +// Nynja-Share +// +// Created by Volodymyr Hryhoriev on 10/12/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol ServiceFactoryProtocol: SharedServiceFactoryProtocol { + func makeAttachmentProviding() -> AttachmentProviding + func makeAttachmentTransformer() -> AttachmentTransformer + func makeAppGroupFlagContainer() -> AppGroupFlagContainer? +} + +final class ServiceFactory: SharedServiceFactory, ServiceFactoryProtocol { + + func makeAttachmentProviding() -> AttachmentProviding { + let dependencies = makeAttachmentTransformer() + return AttachmentProvider(dependencies: dependencies) + } + + func makeAttachmentTransformer() -> AttachmentTransformer { + return AttachmentTransformerImpl() + } + + func makeAppGroupFlagContainer() -> AppGroupFlagContainer? { + return AppGroupFlagContainer(fileManager: .default, appGroup: Bundle.main.appGroupName) + } +} diff --git a/Nynja-Share/UI/ForwardSelector/Entities/ServerSignal.swift b/Nynja-Share/UI/ForwardSelector/Entities/ServerSignal.swift new file mode 100644 index 000000000..935e0e725 --- /dev/null +++ b/Nynja-Share/UI/ForwardSelector/Entities/ServerSignal.swift @@ -0,0 +1,14 @@ +// +// ServerSignal.swift +// Nynja-Share +// +// Created by Volodymyr Hryhoriev on 10/31/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +enum ServerSignal { + case noToken + case noUser + case mismatchUserData + case sessionNotFound +} diff --git a/Nynja-Share/UI/ForwardSelector/ForwardSelectorProtocols.swift b/Nynja-Share/UI/ForwardSelector/ForwardSelectorProtocols.swift index df06b3541..50a388541 100644 --- a/Nynja-Share/UI/ForwardSelector/ForwardSelectorProtocols.swift +++ b/Nynja-Share/UI/ForwardSelector/ForwardSelectorProtocols.swift @@ -7,6 +7,11 @@ // import Foundation +import UIKit.UINavigationController + +protocol ForwardSelectorWireFrameProtocol: class { + func presentForwardSelector(navigation: UINavigationController) +} protocol ForwardSelectorViewProtocol: ForwardSelectorViewSetupProtocol { @@ -15,13 +20,15 @@ protocol ForwardSelectorViewProtocol: ForwardSelectorViewSetupProtocol { /** * Add here your methods for communication VIEW -> PRESENTER */ + var isSendEnabled: Bool { get set } } protocol ForwardSelectorPresenterProtocol: class { - var view: ForwardSelectorViewProtocol! { get set } + var view: ForwardSelectorViewProtocol! { get } var interactor: ForwardSelectorInteractorInputProtocol! { get } + var wireFrame: ForwardSelectorWireFrameProtocol! { get } /** * Add here your methods for communication VIEW -> PRESENTER @@ -35,6 +42,8 @@ protocol ForwardSelectorPresenterProtocol: class { func didFinishSelection(progress: ((Double)->())?, success: @escaping ()->(), failure: ()->(), forbidden: () -> ()) func didSelectTarget(_ forwardTarget: ForwardTarget) + + func cancelTapped() } protocol ForwardSelectorInteractorOutputProtocol: class { @@ -54,18 +63,17 @@ protocol ForwardSelectorInteractorOutputProtocol: class { protocol ForwardSelectorInteractorInputProtocol: class { - var presenter: ForwardSelectorInteractorOutputProtocol! { get set } - var mode: ForwardSelectorMode { get } + var presenter: ForwardSelectorInteractorOutputProtocol! { get } + + /** + * Add here your methods for communication PRESENTER -> INTERACTOR + */ + var attachment: Attachment? { get } var handlerServerSignals: ((ServerSignal)->())? { get set } var handlerSuccess: (()->())? { get set} - init(mode: ForwardSelectorMode) - - /** - * Add here your methods for communication PRESENTER -> INTERACTOR - */ func configure(with extensionContext: NSExtensionContext?) func fetchContacts() @@ -75,4 +83,6 @@ protocol ForwardSelectorInteractorInputProtocol: class { func filterGroups(with text: String) func send(for targets: ForwardTargets, progress: ((Double)->())?, success: @escaping ()->(), failure: ()->()) + + func cancelUploading() } diff --git a/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift b/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift index 384c34370..7acf73a6e 100644 --- a/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift +++ b/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift @@ -10,106 +10,85 @@ import Foundation import AVFoundation import UIKit import AWSS3 +import SDWebImage -enum ServerSignal { - case noToken - case noUser - case mismatchUserData - case sessionNotFound -} -final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, ProfileHandlerDelegate, MessageHandlerDelegate, IoHandlerDelegate, ContactHandlerDelegate { +final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, ProfileHandlerDelegate, MessageHandlerDelegate, IoHandlerDelegate, ContactHandlerDelegate, SetInjectable { weak var presenter: ForwardSelectorInteractorOutputProtocol! - private let appGroupContainer = AppGroupFlagContainer(fileManager: .default, appGroup: Bundle.main.appGroupName)! - - private let typingSenderService: TypingSenderServiceProtocol - - private let attachmentProviding = AttachmentProvider(dependencies: AttachmentTransformerImpl()) // TODO: share - - private(set) var mode: ForwardSelectorMode + private var appGroupFlagContainer: AppGroupFlagContainer? + private var attachmentProvider: AttachmentProviding! + private var amazonManager: AmazonManager! + private var amazonInitializer: AmazonInitializer! + private var typingSenderService: TypingSenderServiceProtocol! var handlerServerSignals: ((ServerSignal)->())? var handlerSuccess: (()->())? - var targets: ForwardTargets? { - switch mode { - case let .edit(targets: targets): - return targets - default: - return nil - } - } - - private var messageLocalId: String? { - switch mode { - case let .create(localId: localId): - return localId - default: - return nil - } - } + private(set) var attachment: Attachment? private var roster: Roster? private var contacts: [ForwardTarget]? private var groups: [ForwardTarget]? - - private(set) var attachment: Attachment? // MARK: - Init - required init(mode: ForwardSelectorMode) { - self.mode = mode - self.typingSenderService = TypingSenderService( - dependencies: .init(mqttService: MQTTService.sharedInstance, - storageService: StorageService.sharedInstance) - ) - ProfileHandler.delegate = self - MessageHandler.delegate = self - ContactHandler.delegate = self - IoHandler.delegate = self - setupAmazon() - notifyHostApplication() - } - deinit { notifyHostApplicationAboutClose() } - - - // MARK: - Host App Notification - - func notifyHostApplication() { + + private func notifyHostApplicationAboutClose() { do { - try appGroupContainer.prepare() - try appGroupContainer.setFlag(.shareExtension) + try appGroupFlagContainer?.removeFlagIfExists(.shareExtension) } catch { LogService.log(topic: .fileSystem) { error.localizedDescription } } } - func notifyHostApplicationAboutClose() { - do { - try appGroupContainer.removeFlagIfExists(.shareExtension) - } catch { - LogService.log(topic: .fileSystem) { error.localizedDescription } - } - } + + // MARK: - ForwardSelectorInteractorInputProtocol func configure(with extensionContext: NSExtensionContext?) { guard let extensionContext = extensionContext else { - fatalError("Extension Context should be") // TODO: share + // Should never happen + fatalError("Extension Context should be") } + initialize() connectToServer() - attachmentProviding.attachment(from: extensionContext) { [weak self] (attachment) in + attachmentProvider.attachment(from: extensionContext) { [weak self] (attachment) in self?.attachment = attachment } } - // MARK: - + private func initialize() { + ProfileHandler.delegate = self + MessageHandler.delegate = self + ContactHandler.delegate = self + IoHandler.delegate = self + amazonInitializer.initialize() + setupImageCache() + notifyHostApplication() + } + + private func setupImageCache() { + SDImageCache.shared().config.shouldDecompressImages = false + SDWebImageDownloader.shared().shouldDecompressImages = false + } + + private func notifyHostApplication() { + do { + try appGroupFlagContainer?.prepare() + try appGroupFlagContainer?.setFlag(.shareExtension) + } catch { + LogService.log(topic: .fileSystem) { error.localizedDescription } + } + } + + + // MARK: - Connect func connectToServer() { if let token = StorageService.sharedInstance.token { @@ -120,6 +99,9 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P } } + + // MARK: - Fetch Data + func fetchContacts() { allContacts { (contacts) in presenter.didFetchContacts(contacts) @@ -173,9 +155,7 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P } private func makeForwardTarget(with contact: Contact) -> ForwardTarget { - let selectedContacts = targets?.contacts ?? [] - let isSelected = selectedContacts.contains(where: { $0.phoneId == contact.phoneId }) - return ForwardTarget(content: .contact(contact), isSelected: isSelected) + return ForwardTarget(content: .contact(contact), isSelected: false) } private func allGroups(completion: ([ForwardTarget]) -> Void) { @@ -185,13 +165,9 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P guard let rooms = roster?.roomlist else { return completion([]) } - let selectedRooms = targets?.groups ?? [] let fetchedGroups: [ForwardTarget] = rooms - .map { room in - let isSelected = selectedRooms.contains(where: { $0.name != nil && $0.name == room.name }) - return ForwardTarget(content: .room(room), isSelected: isSelected) - } + .map { ForwardTarget(content: .room($0), isSelected: false) } .sorted(by: self.sortComparator) self.groups = fetchedGroups @@ -199,6 +175,9 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P completion(fetchedGroups) } } + + + // MARK: - Send Message func send(for targets: ForwardTargets, progress: ((Double) -> ())?, success: @escaping () -> (), failure: () -> ()) { guard let attachment = self.attachment else { @@ -210,12 +189,25 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P let id = getRoomID(targets: targets) if let url = attachment.contentUrl { - sendContent(roomID: id, with: url, type: .image, targets: targets, progress: progress) + sendContent(roomID: id, with: url, type: messageType(for: attachment), targets: targets, progress: progress) } else if let text = attachment.text { sendContent(with: text, type: .text, targets: targets) } } + private func messageType(for attachment: Attachment) -> SendMessageType { + switch attachment.kind { + case .audio, .file: + return .file + case .image: + return .image + case .video: + return .video + case .link, .text: + return .text + } + } + private func getRoomID(targets: ForwardTargets) -> String { var result = "fileID" if let id = targets.contacts.first?.phoneId { @@ -241,16 +233,17 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P guard let phoneId = StorageService.sharedInstance.phoneId, let url = url else { return } + func upload(_ thumbnail: Desc?) { - AmazonManager.shared.upload(chatID: roomID, + amazonManager.upload(chatID: roomID, filePath: url, downloadProgressCompletion: { part, full in progress?(Double(part / full)) typingModel.map { self.sendStatus(to: targets, type: $0) } }, completion: { fileUrl, fileName, size in - let info: String? + if type == .image, let resolution = UIImage(fileUrl: url)?.scaledSize { info = resolution.resolutionString } else if type == .video { @@ -292,7 +285,7 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P desc.payload = thumbUrl.path desc.mime = SendMessageType.thumbnails.rawValue - AmazonManager.shared.upload(chatID: nil, filePath: thumbUrl, downloadProgressCompletion: nil) { + self.amazonManager.upload(chatID: nil, filePath: thumbUrl, downloadProgressCompletion: nil) { fileUrl, fileName, size in desc.payload = String(describing: fileUrl) desc.data = Feature.thumbRepresentation(size: size, @@ -344,8 +337,17 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P return msg } + func cancelUploading() { + guard let url = attachment?.contentUrl else { + return + } + amazonManager.processingURLs[url.absoluteString]?.cancel() + } + - //MARK: - MessageHandlerDelegate + // MARK: - Handlers + + // MARK: MessageHandlerDelegate func getMessageSuccess(message: Message) { guard message.from == StorageService.sharedInstance.phoneId else { @@ -355,7 +357,7 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P } - //MARK: - ProfileHandlerDelegate + // MARK: ProfileHandlerDelegate func getProfileSuccess(model: Profile) { guard let myRoster = model.rosters?.first(where: { ($0 as? Roster)?.id == StorageService.sharedInstance.rosterId }) as? Roster, myRoster.names != nil else { @@ -367,7 +369,7 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P } - //MARK: - ContactHandlerDelegate + // MARK: ContactHandlerDelegate func didReceiveUpdatedContact(contact: Contact) { if [.ban, .banned].contains(contact.originalStatus) { @@ -384,7 +386,7 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P } - //MARK: - IoHandlerDelegate + // MARK: IoHandlerDelegate func sessionNotFound() { handlerServerSignals?(.sessionNotFound) @@ -399,28 +401,27 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P private func sortComparator(lhs: ForwardTarget, rhs: ForwardTarget) -> Bool { return lhs.displayName < rhs.displayName } +} + + +// MARK: - SetInjectable + +extension ForwardSelectorInteractor { + struct Dependencies { + let presenter: ForwardSelectorInteractorOutputProtocol + let appGroupFlagContainer: AppGroupFlagContainer? + let attachmentProvider: AttachmentProviding + let amazonManager: AmazonManager + let amazonInitializer: AmazonInitializer + let typingSenderService: TypingSenderServiceProtocol + } - func setupAmazon() { - let amazonService = ThirdPartyServicesFactory.amazon - - let credentialsProvider = AWSStaticCredentialsProvider( - accessKey: amazonService.serviceConfig.accessKey, - secretKey: amazonService.serviceConfig.secretKey - ) - - let defaultConfiguration = AWSServiceConfiguration( - region: .USWest2, - credentialsProvider: credentialsProvider - )! - AWSS3TransferManager.register(with: defaultConfiguration, forDestination: .default) - - let stickerStorageConfiguration = AWSServiceConfiguration( - region: .EUCentral1, - credentialsProvider: credentialsProvider - )! - AWSS3TransferManager.register(with: stickerStorageConfiguration, forDestination: .stickerPacks) - - AWSServiceManager.default().defaultServiceConfiguration = defaultConfiguration + func inject(dependencies: ForwardSelectorInteractor.Dependencies) { + presenter = dependencies.presenter + appGroupFlagContainer = dependencies.appGroupFlagContainer + attachmentProvider = dependencies.attachmentProvider + amazonManager = dependencies.amazonManager + amazonInitializer = dependencies.amazonInitializer + typingSenderService = dependencies.typingSenderService } } - diff --git a/Nynja-Share/UI/ForwardSelector/Presenter/ForwardSelectorPresenter.swift b/Nynja-Share/UI/ForwardSelector/Presenter/ForwardSelectorPresenter.swift index 03f9503b1..95433c1be 100644 --- a/Nynja-Share/UI/ForwardSelector/Presenter/ForwardSelectorPresenter.swift +++ b/Nynja-Share/UI/ForwardSelector/Presenter/ForwardSelectorPresenter.swift @@ -8,13 +8,12 @@ import Foundation -final class ForwardSelectorPresenter: ForwardSelectorPresenterProtocol, ForwardSelectorInteractorOutputProtocol { + +final class ForwardSelectorPresenter: ForwardSelectorPresenterProtocol, ForwardSelectorInteractorOutputProtocol, SetInjectable { weak var view: ForwardSelectorViewProtocol! - var interactor: ForwardSelectorInteractorInputProtocol! = { - let interactor = ForwardSelectorInteractor(mode: .create(localId: "")) - return interactor - }() + var interactor: ForwardSelectorInteractorInputProtocol! + var wireFrame: ForwardSelectorWireFrameProtocol! private var selectedContacts: [ForwardTarget] = [] private var selectedGroups: [ForwardTarget] = [] @@ -26,8 +25,6 @@ final class ForwardSelectorPresenter: ForwardSelectorPresenterProtocol, ForwardS private var state: ViewState = .contacts - private var mode: ForwardSelectorDisplayMode! - var selectedItems: [ForwardTarget] { switch self.state { case .contacts: @@ -37,20 +34,13 @@ final class ForwardSelectorPresenter: ForwardSelectorPresenterProtocol, ForwardS } } - init() { - interactor.presenter = self - } - // MARK: - ForwardSelectorPresenterProtocol func viewLoaded(with extensionContext: NSExtensionContext?) { - mode = ForwardSelectorDisplayMode(mode: interactor.mode) - - view.setupUI(with: self.mode) view.isSendEnabled = false - interactor.configure(with: extensionContext) + openContacts() } func openContacts() { @@ -98,6 +88,10 @@ final class ForwardSelectorPresenter: ForwardSelectorPresenterProtocol, ForwardS updateIsSendEnabled() } + func cancelTapped() { + interactor.cancelUploading() + } + // MARK: - ForwardSelectorInteractorOutputProtocol @@ -167,3 +161,20 @@ final class ForwardSelectorPresenter: ForwardSelectorPresenterProtocol, ForwardS view.isSendEnabled = !selectedItems.isEmpty && interactor.attachment != nil } } + + +// MARK: - SetInjectable + +extension ForwardSelectorPresenter { + struct Dependencies { + let view: ForwardSelectorViewProtocol + let interactor: ForwardSelectorInteractorInputProtocol + let wireFrame: ForwardSelectorWireFrameProtocol + } + + func inject(dependencies: ForwardSelectorPresenter.Dependencies) { + view = dependencies.view + interactor = dependencies.interactor + wireFrame = dependencies.wireFrame + } +} diff --git a/Nynja-Share/UI/ForwardSelector/View/ForwardSelectorViewController.swift b/Nynja-Share/UI/ForwardSelector/View/ForwardSelectorViewController.swift index 758a0653f..19a61f0c3 100644 --- a/Nynja-Share/UI/ForwardSelector/View/ForwardSelectorViewController.swift +++ b/Nynja-Share/UI/ForwardSelector/View/ForwardSelectorViewController.swift @@ -10,9 +10,9 @@ import UIKit import NynjaUIKit import SnapKit -final class ForwardSelectorViewController: UIViewController, ForwardSelectorViewProtocol, KeyboardInteractive, UITableViewDelegate, SelectionAvatarViewDelegate { +final class ForwardSelectorViewController: UIViewController, ForwardSelectorViewProtocol, KeyboardInteractive, UITableViewDelegate, SelectionAvatarViewDelegate, SetInjectable { - var presenter: ForwardSelectorPresenterProtocol! = ForwardSelectorPresenter() + var presenter: ForwardSelectorPresenterProtocol! private let dataSource = ForwardSelectorDataSource() @@ -44,19 +44,10 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView bottomActionsView.selectAction(at: 0) presenter.openGroups() } - listHeaderLabel.text = state.title } } - var screenTitle: String? { - didSet { - if oldValue != screenTitle { - adjustNavigationView() - } - } - } - // MARK: - Views @@ -64,43 +55,35 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView let hud = ProgressHUD() view.addSubview(hud) hud.snp.makeConstraints({ make in - make.width.equalTo(250) - make.height.equalTo(140) + make.width.equalTo(Constraints.hud.width.adjustedByWidth) + make.height.equalTo(Constraints.hud.height.adjustedByWidth) make.center.equalToSuperview() }) return hud }() private lazy var backgroundImage: UIImageView = { - let view = UIImageView() - view.isUserInteractionEnabled = true - setupBackground(for: view) - self.view.addSubview(view) + let imageView = UIImageView() + imageView.isUserInteractionEnabled = true + imageView.image = UIImage(named: Theme.default.backgroundName) - view.snp.makeConstraints({ (make) in - make.top.left.right.bottom.equalTo(self.view) + view.addSubview(imageView) + imageView.snp.makeConstraints({ (make) in + make.edges.equalToSuperview() }) - return view + + return imageView }() private lazy var navigationView: NavigationView = { let navView = NavigationView() navView.clipsToBounds = true - - let config = NavigationView.Config( - isVisibleSeparator: false, - isVisibleBackButton: false, - title: nil, - navigationHandler: nil, - backButtonImage: nil) - - navView.configure(config: config) - self.view.addSubview(navView) + view.addSubview(navView) navView.snp.makeConstraints({ (make) in self.adjustVerticalInset(.top, make: make) make.left.right.equalToSuperview() - make.height.equalTo(0) + make.height.equalTo(NavigationView.calculatedHeight) }) return navView @@ -110,15 +93,15 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView let avatarsView = SelectionAvatarView() avatarsView.register(viewModel: ForwardAvatarViewModel.self) - avatarsView.placeHolderText = String.localizable.noForwardTargetSelected - self.view.addSubview(avatarsView) + view.addSubview(avatarsView) avatarsView.snp.makeConstraints { maker in maker.height.equalTo(Constraints.avatarsView.height.adjustedByWidth) maker.top.equalTo(navigationView.snp.bottom).offset(Constraints.avatarsView.topInset.adjustedByWidth) maker.left.right.equalToSuperview() } + return avatarsView }() @@ -131,8 +114,7 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView let horizontalInset = Constraints.listHeaderLabel.horizontalInset.adjustedByWidth let verticalInset = Constraints.listHeaderLabel.verticalInset.adjustedByWidth - self.listHeaderView.addSubview(label) - + listHeaderView.addSubview(label) label.snp.makeConstraints { maker in maker.left.equalToSuperview().offset(horizontalInset) maker.right.equalToSuperview().inset(horizontalInset) @@ -140,6 +122,7 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView maker.bottom.equalToSuperview().inset(verticalInset) maker.height.equalTo(height) } + return label }() @@ -148,14 +131,14 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView listHeaderView.backgroundColor = UIColor.nynja.clear - self.view.addSubview(listHeaderView) - + view.addSubview(listHeaderView) listHeaderView.snp.makeConstraints { maker in - maker.top.equalTo(self.avatarsView.snp.bottom) + maker.top.equalTo(avatarsView.snp.bottom) maker.left.equalToSuperview() maker.right.equalToSuperview() - maker.bottom.equalTo(self.separatorView.snp.top) + maker.bottom.equalTo(separatorView.snp.top) } + return listHeaderView }() @@ -165,7 +148,7 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView view.addSubview(separatorView) separatorView.snp.makeConstraints { maker in maker.left.right.equalToSuperview() - maker.bottom.equalTo(self.tableView.snp.top) + maker.bottom.equalTo(tableView.snp.top) } return separatorView @@ -179,13 +162,15 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView tblView.tableFooterView = UIView() tblView.showsHorizontalScrollIndicator = false + tblView.register(viewModel: ForwardCellViewModel.self) tblView.rowHeight = ForwardCellViewModel.Cell.Constraints.height tblView.estimatedRowHeight = tblView.rowHeight - self.view.addSubview(tblView) + view.addSubview(tblView) tblView.snp.makeConstraints { maker in maker.left.right.equalTo(avatarsView) } + return tblView }() @@ -196,19 +181,21 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView let grView = GradientView(colors: colors) grView.isUserInteractionEnabled = false - self.view.addSubview(grView) + view.addSubview(grView) grView.snp.makeConstraints { make in make.height.equalTo(Constraints.gradientView.height.adjustedByWidth) make.left.right.equalToSuperview() make.bottom.equalTo(self.bottomActionsView.snp.top) } + return grView }() private let bottomInset = Constraints.actionsView.bottomInset.adjustedByWidth private lazy var bottomActionsView: SearchActionsView = { - let actions = self.bottomActions(for: .create) + let actions = self.bottomActions() + let actionsView = SearchActionsView(actions: actions) actionsView.onClose = { [weak self] in self?.closeShareExtension() @@ -219,16 +206,16 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView } self.view.addSubview(actionsView) - actionsView.snp.makeConstraints { make in make.left.right.equalToSuperview() make.top.equalTo(tableView.snp.bottom) adjustVerticalInset(.bottom, make: make, offset: -bottomInset) } + return actionsView }() - private func bottomActions(for mode: ForwardSelectorDisplayMode) -> [ActionsView.Action] { + private func bottomActions() -> [ActionsView.Action] { let itemBgColor = UIColor.nynja.darkLight let groupsAction = ActionsView.Action( @@ -265,10 +252,7 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView sSelf.hud.show() sSelf.hud.setFileName(fromUrl: attachment.contentUrl) sSelf.hud.cancelAction = { - guard let url = attachment.contentUrl else { - return - } - AmazonManager.shared.processingURLs[url.absoluteString]?.cancel() + self?.presenter.cancelTapped() self?.lockInteraction(false) } @@ -292,17 +276,53 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView override func viewDidLoad() { super.viewDidLoad() - presenter.view = self presenter.interactor.handlerServerSignals = { [weak self] signal in self?.showLoginAlert() } - backgroundImage.isHidden = false - screenTitle = String.localizable.forwardScreenLogo - hud.isHidden = true + setupUI() presenter.viewLoaded(with: extensionContext) } + private func setupUI() { + activateViews() + setupSubviews() + } + + private func activateViews() { + backgroundImage.isHidden = false + listHeaderView.isHidden = false + gradientView.isHidden = false + bottomActionsView.isHidden = false + hud.isHidden = true + } + + private func setupSubviews() { + setupNavigationView() + setupTableView() + setupAvatarsView() + } + + private func setupNavigationView() { + navigationView.configure( + config: .init( + isVisibleSeparator: false, + isVisibleBackButton: false, + title: String.localizable.forwardScreenLogo, + navigationHandler: nil, + backButtonImage: nil)) + } + + private func setupTableView() { + tableView.delegate = self + tableView.dataSource = self.dataSource + } + + private func setupAvatarsView() { + avatarsView.delegate = self + avatarsView.dataSource = self.dataSource + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.view.bringSubview(toFront: navigationView) @@ -331,24 +351,6 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView } } - func setupUI(with mode: ForwardSelectorDisplayMode) { - screenTitle = String.localizable.forwardScreenLogo - - tableView.register(viewModel: ForwardCellViewModel.self) - tableView.delegate = self - tableView.dataSource = self.dataSource - - avatarsView.delegate = self - avatarsView.dataSource = self.dataSource - - listHeaderView.isHidden = false - gradientView.isHidden = false - bottomActionsView.isHidden = false - - bottomActionsView.actions = bottomActions(for: mode) - showContacts() - } - func setupForwardTargets(_ targets: [ForwardTarget], selected: [ForwardTarget]) { dataSource.items = targets.map { ForwardCellViewModel(target: $0) } dataSource.avatars = selected.map { ForwardAvatarViewModel(target: $0) } @@ -417,25 +419,11 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView // MARK: - Private methods - private func setupBackground(for imageView: UIImageView, theme: Theme = .default) { - imageView.image = UIImage(named: theme.backgroundName) - } - - private func adjustNavigationView() { - navigationView.updateTitle(title: screenTitle) - - let height = screenTitle != nil ? NavigationView.calculatedHeight : 0 - navigationView.snp.updateConstraints { (make) in - make.height.equalTo(height) - } - } - private func lockInteraction(_ isLock : Bool) { avatarsView.isUserInteractionEnabled = !isLock tableView.isUserInteractionEnabled = !isLock bottomActionsView.isUserInteractionEnabled = !isLock } - } @@ -475,12 +463,28 @@ extension ForwardSelectorViewController { } +// MARK: - SetInjectible + +extension ForwardSelectorViewController { + typealias Dependencies = ForwardSelectorPresenterProtocol + + func inject(dependencies: ForwardSelectorPresenterProtocol) { + self.presenter = dependencies + } +} + + // MARK: - Layout extension ForwardSelectorViewController { enum Constraints { + enum hud { + static let width = 250.0 + static let height = 140.0 + } + enum avatarsView { static let height: CGFloat = 44.0 diff --git a/Nynja-Share/UI/ForwardSelector/View/Views/ProgressHUD.swift b/Nynja-Share/UI/ForwardSelector/View/Views/ProgressHUD.swift index bbbc07aed..af76f4ecf 100644 --- a/Nynja-Share/UI/ForwardSelector/View/Views/ProgressHUD.swift +++ b/Nynja-Share/UI/ForwardSelector/View/Views/ProgressHUD.swift @@ -155,20 +155,22 @@ class ProgressHUD : UIView { } func setFileName(fromUrl url: URL?) { - if let url = url { - let fileName = url.lastPathComponent - let dotIndex = fileName.range(of: ".", options: .backwards) - let name = fileName.substring(to: dotIndex!.lowerBound) - var type = fileName.substring(from: dotIndex!.upperBound).lowercased() - if type == "mov" { - type = "mp4" - } - // let time = Int(NSDate().timeIntervalSince1970) - // let remoteName = name + String(time) + "." + type - fileNameLabel.text = name + "." + type - } else { + guard let url = url else { fileNameLabel.text = "" + return } + let fileName = url.lastPathComponent + guard let dotIndex = fileName.range(of: ".", options: .backwards) else { + fileNameLabel.text = fileName + return + } + + let name = fileName.substring(to: dotIndex.lowerBound) + var type = fileName.substring(from: dotIndex.upperBound).lowercased() + if type == "mov" { + type = "mp4" + } + fileNameLabel.text = name + "." + type } } diff --git a/Nynja-Share/UI/ForwardSelector/Wireframe/ForwardSelectorWireFrame.swift b/Nynja-Share/UI/ForwardSelector/Wireframe/ForwardSelectorWireFrame.swift new file mode 100644 index 000000000..a9dbdd37b --- /dev/null +++ b/Nynja-Share/UI/ForwardSelector/Wireframe/ForwardSelectorWireFrame.swift @@ -0,0 +1,45 @@ +// +// ForwardSelectorWireFrame.swift +// Nynja-Share +// +// Created by Volodymyr Hryhoriev on 10/12/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import UIKit + +final class ForwardSelectorWireFrame: ForwardSelectorWireFrameProtocol { + + weak var navigation: UINavigationController? + + func presentForwardSelector(navigation: UINavigationController) { + self.navigation = navigation + + // Dependencies + let serviceFactory = ServiceFactory() + + // Module components + let view = ForwardSelectorViewController() + let presenter = ForwardSelectorPresenter() + let interactor = ForwardSelectorInteractor() + + // Connecting + view.inject(dependencies: presenter) + presenter.inject( + dependencies: .init( + view: view, + interactor: interactor, + wireFrame: self)) + interactor.inject( + dependencies: .init( + presenter: presenter, + appGroupFlagContainer: serviceFactory.makeAppGroupFlagContainer(), + attachmentProvider: serviceFactory.makeAttachmentProviding(), + amazonManager: serviceFactory.makeAmazonManager(), + amazonInitializer: serviceFactory.makeAmazonInitializer(), + typingSenderService: serviceFactory.makeTypingSenderService() + )) + + navigation.pushViewController(view, animated: false) + } +} diff --git a/Nynja-Share/UI/RootNavigationController.swift b/Nynja-Share/UI/RootNavigationController.swift new file mode 100644 index 000000000..22509c3f4 --- /dev/null +++ b/Nynja-Share/UI/RootNavigationController.swift @@ -0,0 +1,17 @@ +// +// RootNavigationController.swift +// Nynja-Share +// +// Created by Volodymyr Hryhoriev on 10/12/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import UIKit + +class RootNavigationController: UINavigationController { + + override func viewDidLoad() { + super.viewDidLoad() + ForwardSelectorWireFrame().presentForwardSelector(navigation: self) + } +} diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 3eac970f9..56a33c002 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -94,6 +94,12 @@ 1E615EDDA6522EF693319BA5 /* EditGroupNameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F8C8AFEB81C734967FE902 /* EditGroupNameViewController.swift */; }; 1F8247691F2779AD00E5B749 /* iCarousel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F8247681F2779AD00E5B749 /* iCarousel.m */; }; 24AC9EAFA26353C7B95B60BF /* DateTimePickerPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1746BDC1030434814FE63E0A /* DateTimePickerPresenter.swift */; }; + 2600CCB1216B8DC600EDC9C3 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265EB71F20A86A5400C1483E /* Language.swift */; }; + 2600CCB9216C43BF00EDC9C3 /* DescriptionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2600CCB8216C43BF00EDC9C3 /* DescriptionCell.swift */; }; + 2600CCBF216D447200EDC9C3 /* OptionallyActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2600CCBE216D447200EDC9C3 /* OptionallyActionCell.swift */; }; + 2600CCC1216D47DC00EDC9C3 /* OptionallyActionCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2600CCC0216D47DC00EDC9C3 /* OptionallyActionCellViewModel.swift */; }; + 2600CCC2216E1B2600EDC9C3 /* DescExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B752B602163A5C900E852B9 /* DescExtension.swift */; }; + 2600CCC4216E419E00EDC9C3 /* MessageInteractor+AutoConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2600CCC3216E419E00EDC9C3 /* MessageInteractor+AutoConversion.swift */; }; 2600DAD420346AD300A2D4F7 /* ButtonHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2600DAD320346AD300A2D4F7 /* ButtonHeaderView.swift */; }; 2600DAD6203479D000A2D4F7 /* ReturnToHomeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2600DAD5203479D000A2D4F7 /* ReturnToHomeHeaderView.swift */; }; 260225DD20F379EF004FC238 /* MessageConvertionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260225DC20F379EF004FC238 /* MessageConvertionView.swift */; }; @@ -184,7 +190,6 @@ 263D66301FE8D20100A509F8 /* TypingExtension+BERT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263D662F1FE8D20100A509F8 /* TypingExtension+BERT.swift */; }; 263D66311FE8D30200A509F8 /* TypingExtension+BERT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263D662F1FE8D20100A509F8 /* TypingExtension+BERT.swift */; }; 263D66331FE8D95100A509F8 /* TypingHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263D66321FE8D95100A509F8 /* TypingHandler.swift */; }; - 264312EC210DE4040057E8B0 /* LanguageSectionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 264312EB210DE4040057E8B0 /* LanguageSectionCoordinator.swift */; }; 26441A121F9FBF9300E724B5 /* MainViewController+NavigateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26441A111F9FBF9300E724B5 /* MainViewController+NavigateProtocol.swift */; }; 2646381E1FFFC5CB002590E6 /* RepliesProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2646381D1FFFC5CB002590E6 /* RepliesProtocols.swift */; }; 264638201FFFD6A2002590E6 /* RepliesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2646381F1FFFD6A2002590E6 /* RepliesVC.swift */; }; @@ -233,7 +238,7 @@ 265AEA181FE9B1BD00AC4806 /* MemberModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265AEA161FE9AFD400AC4806 /* MemberModel.swift */; }; 265D5F44203F0CE600FFB513 /* ScheduleItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265D5F43203F0CE600FFB513 /* ScheduleItemProtocol.swift */; }; 265EB71D20A86A1900C1483E /* ConvertionProgressModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265EB71C20A86A1900C1483E /* ConvertionProgressModel.swift */; }; - 265EB72020A86A5400C1483E /* LangExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265EB71F20A86A5400C1483E /* LangExtended.swift */; }; + 265EB72020A86A5400C1483E /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265EB71F20A86A5400C1483E /* Language.swift */; }; 265F5D24209B6987008ACCC8 /* Place.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265F5D22209B6987008ACCC8 /* Place.swift */; }; 265F5D25209B6987008ACCC8 /* LocationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265F5D23209B6987008ACCC8 /* LocationType.swift */; }; 265F5D27209B6D39008ACCC8 /* GCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DD72F5F1F1547AC008CFF83 /* GCD.swift */; }; @@ -310,6 +315,9 @@ 269D9DF01FC3AF0D00324263 /* CGSizeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 269D9DEF1FC3AF0D00324263 /* CGSizeExtension.swift */; }; 26A0CFE12005138C006F6617 /* MemberExtension+BERT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A0CFE02005138C006F6617 /* MemberExtension+BERT.swift */; }; 26A0CFE2200513B4006F6617 /* MemberExtension+BERT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A0CFE02005138C006F6617 /* MemberExtension+BERT.swift */; }; + 26A2349021789B9700E73CF5 /* Locale+Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B7CB682178982200C83ED8 /* Locale+Language.swift */; }; + 26A421CD217E027600120542 /* SnackBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A421CC217E027600120542 /* SnackBar.swift */; }; + 26A421CF217E541800120542 /* SnackBarLayoutGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A421CE217E541700120542 /* SnackBarLayoutGuide.swift */; }; 26A8561D2074C34200C642EA /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79117891F97874D00462D68 /* GradientView.swift */; }; 26A8561E2074C38F00C642EA /* NavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E74597741FA2222600D3C88C /* NavigationView.swift */; }; 26A856212074C4C000C642EA /* NynjaCloseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85433F2B204D5AA500B373A7 /* NynjaCloseButton.swift */; }; @@ -330,8 +338,7 @@ 26A856382075080400C642EA /* ForwardSelectorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A856372075080400C642EA /* ForwardSelectorPresenter.swift */; }; 26A8563A20750B5D00C642EA /* ScheduleInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB9CDF2035CAE700385520 /* ScheduleInfo.swift */; }; 26A8563C20750BA100C642EA /* ForwardSelectorInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A8563B20750BA100C642EA /* ForwardSelectorInteractor.swift */; }; - 26A8CD0E2177AEC200A33A20 /* Injectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11786D620A9AABD007A9A1B /* Injectable.swift */; }; - 26A8CD0F2177AEC300A33A20 /* Injectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11786D620A9AABD007A9A1B /* Injectable.swift */; }; + 26AB1419218775BB00F2BB83 /* ConversionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26AB1418218775BB00F2BB83 /* ConversionState.swift */; }; 26ABCA3E21189DA400EA4782 /* Aps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26ABCA3D21189DA400EA4782 /* Aps.swift */; }; 26ABCA4A211B321100EA4782 /* Bundle+provision.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26ABCA49211B321100EA4782 /* Bundle+provision.swift */; }; 26ABF27B2059FE2500438975 /* URL+valid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26ABF27A2059FE2500438975 /* URL+valid.swift */; }; @@ -352,6 +359,7 @@ 26B32B971FE20BB500888A0A /* DescExtension+BERT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B32B951FE20BAB00888A0A /* DescExtension+BERT.swift */; }; 26B5F7421FB0FF7B00CEC6AE /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B5F7411FB0FF7B00CEC6AE /* FileManager.swift */; }; 26B5F7431FB0FF7B00CEC6AE /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B5F7411FB0FF7B00CEC6AE /* FileManager.swift */; }; + 26B7CB692178982200C83ED8 /* Locale+Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B7CB682178982200C83ED8 /* Locale+Language.swift */; }; 26C061C01FEAA04A00A2EBE4 /* FeatureExtension+BERT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C061BF1FEAA04A00A2EBE4 /* FeatureExtension+BERT.swift */; }; 26C061C21FEAA26500A2EBE4 /* FeatureExtension+BERT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C061BF1FEAA04A00A2EBE4 /* FeatureExtension+BERT.swift */; }; 26C0C1A12073C11300C530DA /* ForwardSelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C0C1A02073C11300C530DA /* ForwardSelectorViewController.swift */; }; @@ -573,6 +581,12 @@ 4B348FFE2163850100CCB0E3 /* DeletedIndexesCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B348FFD2163850100CCB0E3 /* DeletedIndexesCalculator.swift */; }; 4B348FFF2163850100CCB0E3 /* DeletedIndexesCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B348FFD2163850100CCB0E3 /* DeletedIndexesCalculator.swift */; }; 4B3B1A2A20248EDD001A47AF /* ThumbnailGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B736D4820238FA40028F2CB /* ThumbnailGenerator.swift */; }; + 4B3B35D2217111BC005A214A /* SharedServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3B35D1217111BC005A214A /* SharedServiceFactory.swift */; }; + 4B3B35D32171120F005A214A /* SharedServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3B35D1217111BC005A214A /* SharedServiceFactory.swift */; }; + 4B3B35D7217119A0005A214A /* AmazonInitializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3B35D6217119A0005A214A /* AmazonInitializer.swift */; }; + 4B3B35D9217119BF005A214A /* AmazonInitializerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3B35D8217119BF005A214A /* AmazonInitializerImpl.swift */; }; + 4B3B35DA217119C9005A214A /* AmazonInitializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3B35D6217119A0005A214A /* AmazonInitializer.swift */; }; + 4B3B35DB217119C9005A214A /* AmazonInitializerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3B35D8217119BF005A214A /* AmazonInitializerImpl.swift */; }; 4B3F055F2043F871002E0F54 /* ScheduleMessageConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F055E2043F871002E0F54 /* ScheduleMessageConfiguration.swift */; }; 4B4266BA204D898900194BC1 /* ForwardSelectorDisplayMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4266B9204D898900194BC1 /* ForwardSelectorDisplayMode.swift */; }; 4B4266BC204D89A100194BC1 /* ForwardSelectorMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4266BB204D89A100194BC1 /* ForwardSelectorMode.swift */; }; @@ -626,6 +640,9 @@ 4B7C73FB215A5522007924DB /* UILabel+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C73F8215A5522007924DB /* UILabel+Debug.swift */; }; 4B7C73FC215A552C007924DB /* LogService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C73EF215A5508007924DB /* LogService.swift */; }; 4B7C73FD215A553F007924DB /* LogWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C73EE215A5508007924DB /* LogWriter.swift */; }; + 4B7E93382170D1BC001558CF /* RootNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7E93372170D1BC001558CF /* RootNavigationController.swift */; }; + 4B7E933B2170D410001558CF /* ForwardSelectorWireFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7E933A2170D410001558CF /* ForwardSelectorWireFrame.swift */; }; + 4B7E933E2170D4FF001558CF /* ServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7E933D2170D4FF001558CF /* ServiceFactory.swift */; }; 4B8996C8204ECE9B00DCB183 /* ContactDAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8996C7204ECE9B00DCB183 /* ContactDAO.swift */; }; 4B8996CA204ECEA700DCB183 /* ContactDAOProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8996C9204ECEA700DCB183 /* ContactDAOProtocol.swift */; }; 4B8996CD204ED33400DCB183 /* StarDAOProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8996CC204ED33400DCB183 /* StarDAOProtocol.swift */; }; @@ -692,6 +709,12 @@ 4BF090CD21635FED00DCCA5C /* Message+Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090CB21635FDC00DCCA5C /* Message+Type.swift */; }; 4BF090CE21635FEE00DCCA5C /* Message+Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090CB21635FDC00DCCA5C /* Message+Type.swift */; }; 4BF090CF2163601400DCCA5C /* Message+LinkedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090C421635E8600DCCA5C /* Message+LinkedId.swift */; }; + 4BF2C3E02188B27500E59F6C /* Injectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF2C3DF2188B27500E59F6C /* Injectable.swift */; }; + 4BF2C3E12188B27F00E59F6C /* Injectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF2C3DF2188B27500E59F6C /* Injectable.swift */; }; + 4BF2C3E32188BA7B00E59F6C /* IdentifierComponentValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85380EE52109FF340048042D /* IdentifierComponentValue.swift */; }; + 4BF2C3E42188BABC00E59F6C /* UIDeviceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7A77FD71FACC360004AE609 /* UIDeviceExtension.swift */; }; + 4BF2C3E92189B49500E59F6C /* Localizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D485DE41F0AD96D00E12FB1 /* Localizable.swift */; }; + 4BF2C3F02189F58F00E59F6C /* ServerSignal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF2C3EF2189F58F00E59F6C /* ServerSignal.swift */; }; 4C5EEA13EBC6A8398F08DCD1 /* MainWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AAB8F770774CE3AE3FD6E1 /* MainWireframe.swift */; }; 4D53FE7454959323B1CCFD96 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270F638DBB2D8FC1BDEB633 /* ProfileViewController.swift */; }; 4DAEBCF361B86B0AD3C98749 /* EditUsernameInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBE3BAC9B7EA418FB463EF04 /* EditUsernameInteractor.swift */; }; @@ -796,7 +819,6 @@ 8509FC7B2158CCA800734D93 /* MessageInteractor+Reply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC7A2158CCA800734D93 /* MessageInteractor+Reply.swift */; }; 8509FC7D2158DCFB00734D93 /* Feature+Construct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC7C2158DCFB00734D93 /* Feature+Construct.swift */; }; 8509FC7E2158DD9A00734D93 /* Feature+Construct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC7C2158DCFB00734D93 /* Feature+Construct.swift */; }; - 8509FC7F2158DD9B00734D93 /* Feature+Construct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC7C2158DCFB00734D93 /* Feature+Construct.swift */; }; 8509FC812158E11000734D93 /* TableAlternationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC802158E11000734D93 /* TableAlternationExtension.swift */; }; 8509FC852158F7D100734D93 /* AppGroupFlagContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC842158F7D100734D93 /* AppGroupFlagContainer.swift */; }; 8509FC872158F7FC00734D93 /* DirectoryWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC862158F7FC00734D93 /* DirectoryWatcher.swift */; }; @@ -940,7 +962,6 @@ 85458CFB212D7B2100BA8814 /* FeatureExtension+BERT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C061BF1FEAA04A00A2EBE4 /* FeatureExtension+BERT.swift */; }; 85458CFD212D7B8C00BA8814 /* Desc+Construct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85458CFC212D7B8C00BA8814 /* Desc+Construct.swift */; }; 85458CFE212D7BCA00BA8814 /* Desc+Construct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85458CFC212D7B8C00BA8814 /* Desc+Construct.swift */; }; - 85458CFF212D7BCB00BA8814 /* Desc+Construct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85458CFC212D7B8C00BA8814 /* Desc+Construct.swift */; }; 85458D00212D7C0C00BA8814 /* Int+AnyObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D669E120BD955F00FBD803 /* Int+AnyObject.swift */; }; 85458D01212D7C1A00BA8814 /* StringAtomExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B052CAF203614D400BC2A9B /* StringAtomExtension.swift */; }; 8546D464213EEC4E0024FE66 /* BidirectionalCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8546D463213EEC4E0024FE66 /* BidirectionalCollection.swift */; }; @@ -1032,6 +1053,7 @@ 8580BAF020BD9AAE00239D9D /* ConstraintMaker+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8580BAEF20BD9AAE00239D9D /* ConstraintMaker+Extensions.swift */; }; 8580BAF320BD9B8000239D9D /* String+Suffix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8580BAF120BD9B7F00239D9D /* String+Suffix.swift */; }; 8580BAF420BD9B8000239D9D /* Range+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8580BAF220BD9B7F00239D9D /* Range+Extension.swift */; }; + 8581B996217DD440006441BB /* NSAttributedString+Bounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8581B995217DD440006441BB /* NSAttributedString+Bounds.swift */; }; 8584C90E20920F3C001A0BBB /* StickerGridCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8584C90C20920F3C001A0BBB /* StickerGridCollectionViewCell.swift */; }; 8584C90F20920F3C001A0BBB /* StickerGridCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8584C90D20920F3C001A0BBB /* StickerGridCellModel.swift */; }; 8586CAC5203335C7009F2A75 /* ForwardAvatarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8586CAC4203335C7009F2A75 /* ForwardAvatarViewModel.swift */; }; @@ -1156,6 +1178,7 @@ 990A25B2C84CE09B4CE64533 /* MyGroupAliasPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9D0CBC2BAD6DC6C7047A26 /* MyGroupAliasPresenter.swift */; }; 99B9D27D2F0EFE051E6581ED /* CreateGroupProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE9DC6ADA0E71241C49A328 /* CreateGroupProtocols.swift */; }; 9B0C32F12153CF1600094ECF /* HintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0C32F02153CF1600094ECF /* HintView.swift */; }; + 9B2C6693216F82AB00116486 /* NynjaJoinByLinkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2C6692216F82AB00116486 /* NynjaJoinByLinkService.swift */; }; 9B81AD92215A5EEA00993A8C /* ActiveSpeakerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B81AD91215A5EEA00993A8C /* ActiveSpeakerView.swift */; }; 9B96705F214BE3FE0058E98F /* MultiPageCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B96705E214BE3FE0058E98F /* MultiPageCollectionView.swift */; }; 9B967098215151760058E98F /* LeaveVoiceMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B967097215151760058E98F /* LeaveVoiceMessageViewController.swift */; }; @@ -1170,6 +1193,7 @@ 9BD8E40720F3576F001384EC /* CallInProgressWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BD8E40620F3576F001384EC /* CallInProgressWireframe.swift */; }; 9BD8E41120F39AE3001384EC /* CallInProgressPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BD8E41020F39AE3001384EC /* CallInProgressPresenter.swift */; }; 9BD8E41320F3A2E2001384EC /* CallInProgressInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BD8E41220F3A2E2001384EC /* CallInProgressInteractor.swift */; }; + 9BFFE61B2178DD00004FE2CA /* BannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BFFE61A2178DCFF004FE2CA /* BannerView.swift */; }; 9E9DD4C7F700872D7CCEE227 /* ProfileInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFF6A30BDE89BF7887B67DA0 /* ProfileInteractor.swift */; }; A1AD6864F4F49D9FC8997D59 /* SelectCountryPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5522F1F73FC8C564BF0254BF /* SelectCountryPresenter.swift */; }; A402A1CC20DE694A005BFA20 /* PartialCheckableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A402A1CB20DE694A005BFA20 /* PartialCheckableButton.swift */; }; @@ -2438,6 +2462,10 @@ 23C1C356E8CBF0A080B629CB /* AddParticipantsViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddParticipantsViewController.swift; sourceTree = ""; }; 23CE9A2F01B3FFE156ED77E2 /* QRCodeReaderViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = QRCodeReaderViewController.swift; sourceTree = ""; }; 2401660C3B7F1E6E5FD8FCD4 /* MapSearchViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MapSearchViewController.swift; sourceTree = ""; }; + 2600CCB8216C43BF00EDC9C3 /* DescriptionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionCell.swift; sourceTree = ""; }; + 2600CCBE216D447200EDC9C3 /* OptionallyActionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionallyActionCell.swift; sourceTree = ""; }; + 2600CCC0216D47DC00EDC9C3 /* OptionallyActionCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionallyActionCellViewModel.swift; sourceTree = ""; }; + 2600CCC3216E419E00EDC9C3 /* MessageInteractor+AutoConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageInteractor+AutoConversion.swift"; sourceTree = ""; }; 2600DAD320346AD300A2D4F7 /* ButtonHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonHeaderView.swift; sourceTree = ""; }; 2600DAD5203479D000A2D4F7 /* ReturnToHomeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReturnToHomeHeaderView.swift; sourceTree = ""; }; 260225DC20F379EF004FC238 /* MessageConvertionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageConvertionView.swift; sourceTree = ""; }; @@ -2516,7 +2544,6 @@ 263D662C1FE8D03400A509F8 /* TypingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TypingModel.swift; path = Services/Models/TypingModel.swift; sourceTree = ""; }; 263D662F1FE8D20100A509F8 /* TypingExtension+BERT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "TypingExtension+BERT.swift"; path = "Nynja/MQTTModels/TypingExtension+BERT.swift"; sourceTree = SOURCE_ROOT; }; 263D66321FE8D95100A509F8 /* TypingHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TypingHandler.swift; path = Services/HandleServices/TypingHandler.swift; sourceTree = ""; }; - 264312EB210DE4040057E8B0 /* LanguageSectionCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageSectionCoordinator.swift; sourceTree = ""; }; 26441A111F9FBF9300E724B5 /* MainViewController+NavigateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainViewController+NavigateProtocol.swift"; sourceTree = ""; }; 2646381D1FFFC5CB002590E6 /* RepliesProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepliesProtocols.swift; sourceTree = ""; }; 2646381F1FFFD6A2002590E6 /* RepliesVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepliesVC.swift; sourceTree = ""; }; @@ -2559,7 +2586,7 @@ 265AEA161FE9AFD400AC4806 /* MemberModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemberModel.swift; sourceTree = ""; }; 265D5F43203F0CE600FFB513 /* ScheduleItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleItemProtocol.swift; sourceTree = ""; }; 265EB71C20A86A1900C1483E /* ConvertionProgressModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConvertionProgressModel.swift; sourceTree = ""; }; - 265EB71F20A86A5400C1483E /* LangExtended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LangExtended.swift; sourceTree = ""; }; + 265EB71F20A86A5400C1483E /* Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = ""; }; 265F5D22209B6987008ACCC8 /* Place.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Place.swift; sourceTree = ""; }; 265F5D23209B6987008ACCC8 /* LocationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationType.swift; sourceTree = ""; }; 265F5D28209B6E1E008ACCC8 /* ParticipantsViewControllerLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParticipantsViewControllerLayout.swift; sourceTree = ""; }; @@ -2617,9 +2644,12 @@ 269D9DED1FC3987200324263 /* URLExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLExtensions.swift; sourceTree = ""; }; 269D9DEF1FC3AF0D00324263 /* CGSizeExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGSizeExtension.swift; sourceTree = ""; }; 26A0CFE02005138C006F6617 /* MemberExtension+BERT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MemberExtension+BERT.swift"; sourceTree = ""; }; + 26A421CC217E027600120542 /* SnackBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackBar.swift; sourceTree = ""; }; + 26A421CE217E541700120542 /* SnackBarLayoutGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackBarLayoutGuide.swift; sourceTree = ""; }; 26A856232074C50D00C642EA /* ActionsView+ScheduleAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActionsView+ScheduleAction.swift"; sourceTree = ""; }; 26A856372075080400C642EA /* ForwardSelectorPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardSelectorPresenter.swift; sourceTree = ""; }; 26A8563B20750BA100C642EA /* ForwardSelectorInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardSelectorInteractor.swift; sourceTree = ""; }; + 26AB1418218775BB00F2BB83 /* ConversionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversionState.swift; sourceTree = ""; }; 26ABCA3D21189DA400EA4782 /* Aps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Aps.swift; path = Services/Aps.swift; sourceTree = ""; }; 26ABCA49211B321100EA4782 /* Bundle+provision.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+provision.swift"; sourceTree = ""; }; 26ABF27A2059FE2500438975 /* URL+valid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+valid.swift"; sourceTree = ""; }; @@ -2635,6 +2665,7 @@ 26B32B921FE20B8B00888A0A /* mucExtension+BERT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "mucExtension+BERT.swift"; path = "Nynja/mucExtension+BERT.swift"; sourceTree = SOURCE_ROOT; }; 26B32B951FE20BAB00888A0A /* DescExtension+BERT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "DescExtension+BERT.swift"; path = "Nynja/MQTTModels/DescExtension+BERT.swift"; sourceTree = SOURCE_ROOT; }; 26B5F7411FB0FF7B00CEC6AE /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = ""; }; + 26B7CB682178982200C83ED8 /* Locale+Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Locale+Language.swift"; sourceTree = ""; }; 26C061BF1FEAA04A00A2EBE4 /* FeatureExtension+BERT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "FeatureExtension+BERT.swift"; path = "Nynja/MQTTModels/FeatureExtension+BERT.swift"; sourceTree = SOURCE_ROOT; }; 26C0C1A02073C11300C530DA /* ForwardSelectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardSelectorViewController.swift; sourceTree = ""; }; 26C0C1D02073CB5800C530DA /* ForwardTargets+Schedule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ForwardTargets+Schedule.swift"; sourceTree = ""; }; @@ -2824,6 +2855,9 @@ 4B348FF12163808300CCB0E3 /* DeletedIndexesCalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedIndexesCalculatorTests.swift; sourceTree = ""; }; 4B348FF92163836D00CCB0E3 /* ReversableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReversableDataSource.swift; sourceTree = ""; }; 4B348FFD2163850100CCB0E3 /* DeletedIndexesCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedIndexesCalculator.swift; sourceTree = ""; }; + 4B3B35D1217111BC005A214A /* SharedServiceFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedServiceFactory.swift; sourceTree = ""; }; + 4B3B35D6217119A0005A214A /* AmazonInitializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmazonInitializer.swift; sourceTree = ""; }; + 4B3B35D8217119BF005A214A /* AmazonInitializerImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmazonInitializerImpl.swift; sourceTree = ""; }; 4B3F055E2043F871002E0F54 /* ScheduleMessageConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleMessageConfiguration.swift; sourceTree = ""; }; 4B4266B9204D898900194BC1 /* ForwardSelectorDisplayMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardSelectorDisplayMode.swift; sourceTree = ""; }; 4B4266BB204D89A100194BC1 /* ForwardSelectorMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardSelectorMode.swift; sourceTree = ""; }; @@ -2871,6 +2905,9 @@ 4B7C73F6215A5522007924DB /* DebugLogs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugLogs.swift; sourceTree = ""; }; 4B7C73F7215A5522007924DB /* UIView+Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Debug.swift"; sourceTree = ""; }; 4B7C73F8215A5522007924DB /* UILabel+Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+Debug.swift"; sourceTree = ""; }; + 4B7E93372170D1BC001558CF /* RootNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootNavigationController.swift; sourceTree = ""; }; + 4B7E933A2170D410001558CF /* ForwardSelectorWireFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardSelectorWireFrame.swift; sourceTree = ""; }; + 4B7E933D2170D4FF001558CF /* ServiceFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceFactory.swift; sourceTree = ""; }; 4B8996C7204ECE9B00DCB183 /* ContactDAO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDAO.swift; sourceTree = ""; }; 4B8996C9204ECEA700DCB183 /* ContactDAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDAOProtocol.swift; sourceTree = ""; }; 4B8996CC204ED33400DCB183 /* StarDAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarDAOProtocol.swift; sourceTree = ""; }; @@ -2918,6 +2955,8 @@ 4BF090C421635E8600DCCA5C /* Message+LinkedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+LinkedId.swift"; sourceTree = ""; }; 4BF090C721635F3300DCCA5C /* Message+LocalStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+LocalStatus.swift"; sourceTree = ""; }; 4BF090CB21635FDC00DCCA5C /* Message+Type.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+Type.swift"; sourceTree = ""; }; + 4BF2C3DF2188B27500E59F6C /* Injectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Injectable.swift; sourceTree = ""; }; + 4BF2C3EF2189F58F00E59F6C /* ServerSignal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSignal.swift; sourceTree = ""; }; 4CDA2BE900351F21464CE687 /* DateTimePickerInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DateTimePickerInteractor.swift; sourceTree = ""; }; 4D247CBC45C1C1267BBBB289 /* QRCodeReaderInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = QRCodeReaderInteractor.swift; sourceTree = ""; }; 4F7C039B61A0663D43BE5AE5 /* SelectCountryProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SelectCountryProtocols.swift; sourceTree = ""; }; @@ -3228,6 +3267,7 @@ 8580BAEF20BD9AAE00239D9D /* ConstraintMaker+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ConstraintMaker+Extensions.swift"; sourceTree = ""; }; 8580BAF120BD9B7F00239D9D /* String+Suffix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Suffix.swift"; sourceTree = ""; }; 8580BAF220BD9B7F00239D9D /* Range+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Range+Extension.swift"; sourceTree = ""; }; + 8581B995217DD440006441BB /* NSAttributedString+Bounds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Bounds.swift"; sourceTree = ""; }; 8584C90C20920F3C001A0BBB /* StickerGridCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerGridCollectionViewCell.swift; sourceTree = ""; }; 8584C90D20920F3C001A0BBB /* StickerGridCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerGridCellModel.swift; sourceTree = ""; }; 8586CAC4203335C7009F2A75 /* ForwardAvatarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardAvatarViewModel.swift; sourceTree = ""; }; @@ -3347,6 +3387,7 @@ 92F29C1A91BF5FD3A0AEEA0D /* AddContactInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddContactInteractor.swift; sourceTree = ""; }; 997E1A59FCAB5602A049C6E7 /* EditUsernamePresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditUsernamePresenter.swift; sourceTree = ""; }; 9B0C32F02153CF1600094ECF /* HintView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HintView.swift; sourceTree = ""; }; + 9B2C6692216F82AB00116486 /* NynjaJoinByLinkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NynjaJoinByLinkService.swift; sourceTree = ""; }; 9B810991D7143259040DCA31 /* LanguageSettingsViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LanguageSettingsViewController.swift; sourceTree = ""; }; 9B81AD91215A5EEA00993A8C /* ActiveSpeakerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSpeakerView.swift; sourceTree = ""; }; 9B96705E214BE3FE0058E98F /* MultiPageCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiPageCollectionView.swift; sourceTree = ""; }; @@ -3362,6 +3403,7 @@ 9BD8E40620F3576F001384EC /* CallInProgressWireframe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInProgressWireframe.swift; sourceTree = ""; }; 9BD8E41020F39AE3001384EC /* CallInProgressPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInProgressPresenter.swift; sourceTree = ""; }; 9BD8E41220F3A2E2001384EC /* CallInProgressInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInProgressInteractor.swift; sourceTree = ""; }; + 9BFFE61A2178DCFF004FE2CA /* BannerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannerView.swift; sourceTree = ""; }; 9C4192D925259B75441492A9 /* FavoritesPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FavoritesPresenter.swift; sourceTree = ""; }; 9DE44A136617140435B23343 /* GroupStorageWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GroupStorageWireframe.swift; sourceTree = ""; }; 9E82188EE0AC1D1C05470692 /* Pods-NynjaUnitTests.channels.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUnitTests.channels.xcconfig"; path = "Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests.channels.xcconfig"; sourceTree = ""; }; @@ -4081,7 +4123,6 @@ F11786C820A8E4FD007A9A1B /* MuteState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MuteState.swift; sourceTree = ""; }; F11786D020A98685007A9A1B /* MessageFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageFactory.swift; sourceTree = ""; }; F11786D320A98D16007A9A1B /* MessageSendingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSendingService.swift; sourceTree = ""; }; - F11786D620A9AABD007A9A1B /* Injectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Injectable.swift; sourceTree = ""; }; F11786DD20A9ED65007A9A1B /* DownloadOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadOperation.swift; sourceTree = ""; }; F11786DF20A9F11D007A9A1B /* UploadOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadOperation.swift; sourceTree = ""; }; F11786E220AACEBF007A9A1B /* DescInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescInfo.swift; sourceTree = ""; }; @@ -4765,7 +4806,6 @@ isa = PBXGroup; children = ( 2603137D20A0A4B9009AC66D /* ChatLanguageSettingsPresenter.swift */, - 264312EB210DE4040057E8B0 /* LanguageSectionCoordinator.swift */, ); path = Presenter; sourceTree = ""; @@ -4800,13 +4840,16 @@ 2603138220A0A4B9009AC66D /* Cell */ = { isa = PBXGroup; children = ( + 2600CCB8216C43BF00EDC9C3 /* DescriptionCell.swift */, 2603138320A0A4B9009AC66D /* SwitchableActionCell.swift */, + 2603138920A0A4B9009AC66D /* SwitchableActionCellViewModel.swift */, + 2600CCBE216D447200EDC9C3 /* OptionallyActionCell.swift */, + 2600CCC0216D47DC00EDC9C3 /* OptionallyActionCellViewModel.swift */, 2603138420A0A4B9009AC66D /* DirectableActionCell.swift */, + 2603138720A0A4B9009AC66D /* DirectableActionCellViewModel.swift */, 2603138520A0A4B9009AC66D /* ActionCell.swift */, 2603138620A0A4B9009AC66D /* ActionCellViewModel.swift */, - 2603138720A0A4B9009AC66D /* DirectableActionCellViewModel.swift */, 2603138820A0A4B9009AC66D /* BaseCell.swift */, - 2603138920A0A4B9009AC66D /* SwitchableActionCellViewModel.swift */, ); path = Cell; sourceTree = ""; @@ -5240,7 +5283,7 @@ 265EB71E20A86A4800C1483E /* Model */ = { isa = PBXGroup; children = ( - 265EB71F20A86A5400C1483E /* LangExtended.swift */, + 265EB71F20A86A5400C1483E /* Language.swift */, ); path = Model; sourceTree = ""; @@ -5424,6 +5467,15 @@ path = Helpers; sourceTree = ""; }; + 26A421CB217E026100120542 /* SnackBar */ = { + isa = PBXGroup; + children = ( + 26A421CC217E027600120542 /* SnackBar.swift */, + 26A421CE217E541700120542 /* SnackBarLayoutGuide.swift */, + ); + path = SnackBar; + sourceTree = ""; + }; 26B32B701FE1722E00888A0A /* ContextMenuOLD */ = { isa = PBXGroup; children = ( @@ -5454,6 +5506,7 @@ 26C0C19F2073C10100C530DA /* UI */ = { isa = PBXGroup; children = ( + 4B7E93372170D1BC001558CF /* RootNavigationController.swift */, 4B7E93342170BD0D001558CF /* ForwardSelector */, ); path = UI; @@ -5817,9 +5870,9 @@ 357809A41F9765CF00C9680C /* Nynja-Share */ = { isa = PBXGroup; children = ( + 359EB2721F9A27EB00147437 /* Services */, A49E1BCF20A9A9D90074DFD3 /* Library */, 26C0C19F2073C10100C530DA /* UI */, - 359EB2721F9A27EB00147437 /* Services */, 356275711F9D32AE00D2A7F0 /* Resources */, ); path = "Nynja-Share"; @@ -5828,6 +5881,7 @@ 359EB2721F9A27EB00147437 /* Services */ = { isa = PBXGroup; children = ( + 4B7E933C2170D4F0001558CF /* ServiceFactory */, 4B5A0B79216E3C6F002C4160 /* AttachmentProvider */, 356275751F9D337400D2A7F0 /* Handlers */, ); @@ -5994,6 +6048,7 @@ 3A82187C1EDEEDF400337B05 /* UI */ = { isa = PBXGroup; children = ( + 26A421CB217E026100120542 /* SnackBar */, 4B749EF2214FEABB002F3A33 /* LoginView */, 4BB0EFBA2151347900704136 /* AlertManager.swift */, 4BB0EFB62151347900704136 /* CustomPopup */, @@ -6032,6 +6087,7 @@ 855A393C213E76E20002B8DC /* LoadingInteractive.swift */, 6DEEE1921F1F9CF6000FAF09 /* UIViewController+Child.swift */, 26F47051201B7248005D3192 /* ReturnToCallView.swift */, + 9BFFE61A2178DCFF004FE2CA /* BannerView.swift */, 3AF8E26E1F42E33300D81390 /* ReturnToCallContentView.swift */, 0014FAD920603D6400A4022F /* NoInternetNavigationView.swift */, E73315EF1FB0B60E00C273FF /* ContainerViewController.swift */, @@ -6185,7 +6241,7 @@ 85D669E120BD955F00FBD803 /* Int+AnyObject.swift */, 85D669E220BD955F00FBD803 /* UIButtonExtensions.swift */, 85D669E320BD956000FBD803 /* UIView+Shadow.swift */, - 3AF4A3D91EFD7DFA0059B405 /* StringExtensions.swift */, + 8581B994217DD417006441BB /* String */, 3AC07E3B1F055B3F00ADBE26 /* DoubleExtensions.swift */, 8E6C4BE51FF83D93009C8374 /* IntExtensions.swift */, 6D485DE41F0AD96D00E12FB1 /* Localizable.swift */, @@ -6596,6 +6652,23 @@ path = DataSource; sourceTree = ""; }; + 4B3B35D421711977005A214A /* SharedServiceFactory */ = { + isa = PBXGroup; + children = ( + 4B3B35D1217111BC005A214A /* SharedServiceFactory.swift */, + ); + path = SharedServiceFactory; + sourceTree = ""; + }; + 4B3B35D521711981005A214A /* AmazonInitializer */ = { + isa = PBXGroup; + children = ( + 4B3B35D6217119A0005A214A /* AmazonInitializer.swift */, + 4B3B35D8217119BF005A214A /* AmazonInitializerImpl.swift */, + ); + path = AmazonInitializer; + sourceTree = ""; + }; 4B4266BD204D914B00194BC1 /* ActionsView */ = { isa = PBXGroup; children = ( @@ -6822,9 +6895,11 @@ isa = PBXGroup; children = ( 4B5A0B77216E3BFD002C4160 /* ForwardSelectorProtocols.swift */, + 4BF2C3EE2189F58100E59F6C /* Entities */, 4B7E93332170BD05001558CF /* View */, 4B7E93352170BD18001558CF /* Presenter */, 4B7E93362170BD25001558CF /* Interactor */, + 4B7E93392170D397001558CF /* Wireframe */, ); path = ForwardSelector; sourceTree = ""; @@ -6845,6 +6920,22 @@ path = Interactor; sourceTree = ""; }; + 4B7E93392170D397001558CF /* Wireframe */ = { + isa = PBXGroup; + children = ( + 4B7E933A2170D410001558CF /* ForwardSelectorWireFrame.swift */, + ); + path = Wireframe; + sourceTree = ""; + }; + 4B7E933C2170D4F0001558CF /* ServiceFactory */ = { + isa = PBXGroup; + children = ( + 4B7E933D2170D4FF001558CF /* ServiceFactory.swift */, + ); + path = ServiceFactory; + sourceTree = ""; + }; 4B8996C6204ECE8500DCB183 /* Contact */ = { isa = PBXGroup; children = ( @@ -7035,6 +7126,7 @@ children = ( 4BE2C5E52142EB5A00A73DD9 /* NynjaCommunicatorService.swift */, 4BE2C5E62142EB5A00A73DD9 /* NynjaRingingService.swift */, + 9B2C6692216F82AB00116486 /* NynjaJoinByLinkService.swift */, ); name = NynjaCalls; path = Services/NynjaCalls; @@ -7051,6 +7143,14 @@ path = LogService; sourceTree = ""; }; + 4BF2C3EE2189F58100E59F6C /* Entities */ = { + isa = PBXGroup; + children = ( + 4BF2C3EF2189F58F00E59F6C /* ServerSignal.swift */, + ); + path = Entities; + sourceTree = ""; + }; 4BFABA332028CD8800299EE7 /* Contacts */ = { isa = PBXGroup; children = ( @@ -8623,6 +8723,7 @@ 8580BAC520BD983400239D9D /* MentionTransitionProtocol.swift */, A458FABC20EB8B320075D55E /* MessageChannelActionsProtocol.swift */, 85629EC92137EF2400A79C97 /* VoiceAudioInteractive.swift */, + 26AB1418218775BB00F2BB83 /* ConversionState.swift */, ); path = Protocols; sourceTree = ""; @@ -8683,6 +8784,15 @@ path = Gestures; sourceTree = ""; }; + 8581B994217DD417006441BB /* String */ = { + isa = PBXGroup; + children = ( + 3AF4A3D91EFD7DFA0059B405 /* StringExtensions.swift */, + 8581B995217DD440006441BB /* NSAttributedString+Bounds.swift */, + ); + path = String; + sourceTree = ""; + }; 8584C90720920E1D001A0BBB /* Sticker */ = { isa = PBXGroup; children = ( @@ -10355,6 +10465,7 @@ isa = PBXGroup; children = ( A45F10FC20B4218D00F45004 /* MessageInteractor.swift */, + 2600CCC3216E419E00EDC9C3 /* MessageInteractor+AutoConversion.swift */, 26342CB320ECFAB600D2196B /* MessageInteractor+Transcription.swift */, 2606F3BB20BFE20400CF7F15 /* MessageInteractor+Translation.swift */, 8580BABC20BD981800239D9D /* MessageInteractor+Mentions.swift */, @@ -10632,13 +10743,6 @@ path = DebuggingPreventer; sourceTree = ""; }; - A4875961212DC26500AD454A /* Database */ = { - isa = PBXGroup; - children = ( - ); - path = Database; - sourceTree = ""; - }; A48C154020EF76DB002DA994 /* Link */ = { isa = PBXGroup; children = ( @@ -10742,6 +10846,8 @@ A4A242452060371A00B0A804 /* Services */ = { isa = PBXGroup; children = ( + 4B3B35D521711981005A214A /* AmazonInitializer */, + 4B3B35D421711977005A214A /* SharedServiceFactory */, 852C3DD0216E3A4300447878 /* Messaging */, A4A242462060372100B0A804 /* Handlers */, ); @@ -12188,6 +12294,7 @@ 850C0B2720E01F7F003341D0 /* NotificationCenter+WheelNotifications.swift */, 85991DB42113437D0056F3E0 /* UITextView+Extensions.swift */, 26ABCA49211B321100EA4782 /* Bundle+provision.swift */, + 26B7CB682178982200C83ED8 /* Locale+Language.swift */, ); path = Extensions; sourceTree = ""; @@ -12697,7 +12804,7 @@ F11786D520A9AAB1007A9A1B /* Interfaces */ = { isa = PBXGroup; children = ( - F11786D620A9AABD007A9A1B /* Injectable.swift */, + 4BF2C3DF2188B27500E59F6C /* Injectable.swift */, F11DF06720BD996200F3E005 /* NavigationProtocol.swift */, ); name = Interfaces; @@ -12736,8 +12843,8 @@ isa = PBXGroup; children = ( F10B0E1E20B4CC5300528E7A /* CameraSettingsCoordinator.swift */, - F11786F520ACF017007A9A1B /* CameraQualitySettings */, F11786FF20ACF017007A9A1B /* CameraSettings */, + F11786F520ACF017007A9A1B /* CameraQualitySettings */, ); path = CameraSettingsFlow; sourceTree = ""; @@ -12745,11 +12852,11 @@ F11786F520ACF017007A9A1B /* CameraQualitySettings */ = { isa = PBXGroup; children = ( - F11786F620ACF017007A9A1B /* Presenter */, - F11786F820ACF017007A9A1B /* Wireframe */, - F11786FA20ACF017007A9A1B /* View */, F11786FC20ACF017007A9A1B /* CameraQualitySettingsProtocols.swift */, + F11786FA20ACF017007A9A1B /* View */, + F11786F620ACF017007A9A1B /* Presenter */, F11786FD20ACF017007A9A1B /* Interactor */, + F11786F820ACF017007A9A1B /* Wireframe */, ); path = CameraQualitySettings; sourceTree = ""; @@ -12790,10 +12897,10 @@ isa = PBXGroup; children = ( F117870020ACF017007A9A1B /* CameraSettingsProtocols.swift */, - F117870120ACF017007A9A1B /* Presenter */, - F117870320ACF017007A9A1B /* Wireframe */, F117870520ACF017007A9A1B /* View */, + F117870120ACF017007A9A1B /* Presenter */, F117870B20ACF017007A9A1B /* Interactor */, + F117870320ACF017007A9A1B /* Wireframe */, ); path = CameraSettings; sourceTree = ""; @@ -13299,7 +13406,6 @@ FE21ACA82113AA7F006010A0 /* NynjaIntegrationTests */ = { isa = PBXGroup; children = ( - A4875961212DC26500AD454A /* Database */, FE21ACB62113AAF7006010A0 /* Services */, FE21ACAB2113AA7F006010A0 /* Info.plist */, ); @@ -13865,7 +13971,7 @@ buildConfigurationList = 3ABCE8FF1EC9330D00A80B15 /* Build configuration list for PBXNativeTarget "Nynja" */; buildPhases = ( DCC1B381470DE28336A7AE52 /* [CP] Check Pods Manifest.lock */, - FBD885722147F93B0099B8C3 /* SwiftGen */, + 26E5E15C218C860600539BD6 /* SwiftGen */, F1313B0520889A3F00E04092 /* KeyWordsHighlighting */, 3ABCE8E91EC9330D00A80B15 /* Sources */, 3ABCE8EA1EC9330D00A80B15 /* Frameworks */, @@ -13898,7 +14004,7 @@ F1C37AA6209A1BF4005EA197 /* Frameworks */, F1C37AA7209A1BF4005EA197 /* Resources */, 780EEB768E01F8C395A83806 /* [CP] Embed Pods Frameworks */, - BEC7BC2C0AD0F224889EDFFA /* [CP] Copy Pods Resources */, + 6E434A6F985EEBC8DE5338E4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -13963,6 +14069,9 @@ com.apple.Push = { enabled = 1; }; + com.apple.SafariKeychain = { + enabled = 1; + }; }; }; F1C37AA8209A1BF4005EA197 = { @@ -14086,6 +14195,20 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 26E5E15C218C860600539BD6 /* SwiftGen */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = SwiftGen; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftgen >/dev/null; then\nswiftgen\nfi"; + }; 3ABCE9081EC93B4A00A80B15 /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -14136,6 +14259,26 @@ 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; }; + 6E434A6F985EEBC8DE5338E4 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests-resources.sh", + "${PODS_ROOT}/GoogleMaps/Maps/Frameworks/GoogleMaps.framework/Resources/GoogleMaps.bundle", + "${PODS_ROOT}/GooglePlaces/Frameworks/GooglePlaces.framework/Resources/GooglePlaces.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMaps.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GooglePlaces.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 71452A851E6744333580C858 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -14244,26 +14387,6 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - BEC7BC2C0AD0F224889EDFFA /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests-resources.sh", - "${PODS_ROOT}/GoogleMaps/Maps/Frameworks/GoogleMaps.framework/Resources/GoogleMaps.bundle", - "${PODS_ROOT}/GooglePlaces/Frameworks/GooglePlaces.framework/Resources/GooglePlaces.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMaps.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GooglePlaces.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; DCC1B381470DE28336A7AE52 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -14336,20 +14459,6 @@ shellPath = /bin/sh; shellScript = "exit 0\nKEYWORDS=\"TODO:|FIXME:|\\?\\?\\?:|\\!\\!\\!:\"\nfind \"${SRCROOT}/Nynja-Share\" \\( -name \"*.h\" -or -name \"*.m\" -or -name \"*.swift\" \\) -print0 | xargs -0 egrep --with-filename --line-number --only-matching \"($KEYWORDS).*\\$\" | perl -p -e \"s/($KEYWORDS)/ warning: \\$1/\""; }; - FBD885722147F93B0099B8C3 /* SwiftGen */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = SwiftGen; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if which swiftgen >/dev/null; then\nswiftgen\nfi"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -14402,6 +14511,7 @@ E785F15A1FF3E38D006C52D9 /* UIImageView+Rounded.swift in Sources */, A42CE56A20692EDB000889CC /* Typing.swift in Sources */, 26EEA5482091F84E0066D3B0 /* CollectionsExtensions.swift in Sources */, + 4B3B35DB217119C9005A214A /* AmazonInitializerImpl.swift in Sources */, 4B5A0B78216E3BFD002C4160 /* ForwardSelectorProtocols.swift in Sources */, A42CE5D820692EDB000889CC /* error_Spec.swift in Sources */, 261522642084D5EC00AF72A5 /* Testable.swift in Sources */, @@ -14521,6 +14631,7 @@ A42CE59A20692EDB000889CC /* writer.swift in Sources */, A42CE60620692EDB000889CC /* History_Spec.swift in Sources */, A42CE61820692EDB000889CC /* Tag_Spec.swift in Sources */, + 4BF2C3E12188B27F00E59F6C /* Injectable.swift in Sources */, A42CE5F820692EDB000889CC /* Friend_Spec.swift in Sources */, A42CE61620692EDB000889CC /* Roster_Spec.swift in Sources */, A42CE54420692EDA000889CC /* Loc.swift in Sources */, @@ -14529,6 +14640,7 @@ A42CE59020692EDB000889CC /* Task.swift in Sources */, 2676D032217F72B80058838F /* ThirdPartyServices.swift in Sources */, A42CE58A20692EDB000889CC /* boundaryEvent.swift in Sources */, + 4B7E933E2170D4FF001558CF /* ServiceFactory.swift in Sources */, 85458CDA212D6FFE00BA8814 /* String+Split.swift in Sources */, A42CE5A420692EDB000889CC /* Search.swift in Sources */, 26A8562F2074CBC600C642EA /* UIView+Gradient.swift in Sources */, @@ -14536,6 +14648,7 @@ 26A0CFE2200513B4006F6617 /* MemberExtension+BERT.swift in Sources */, A4F3DA9D2084910C00FF71C7 /* ContactHandler.swift in Sources */, 4B7C73FC215A552C007924DB /* LogService.swift in Sources */, + 4B7E933B2170D410001558CF /* ForwardSelectorWireFrame.swift in Sources */, 35B1ABB01FA34B2600E65233 /* SearchModel.swift in Sources */, 4B052CB12036193900BC2A9B /* StringAtomExtension.swift in Sources */, 35B98F9E1F9BFDE1009B8DEC /* SendModel.swift in Sources */, @@ -14568,10 +14681,10 @@ A45F114C20B4221C00F45004 /* SendingStatus.swift in Sources */, 26A856282074C79C00C642EA /* SearchActionsView.swift in Sources */, A45F114F20B4222F00F45004 /* InternetStatus.swift in Sources */, - 26A8CD0F2177AEC300A33A20 /* Injectable.swift in Sources */, 26C0C1D32073CBA700C530DA /* Room+DialogCellModel.swift in Sources */, 26A8561D2074C34200C642EA /* GradientView.swift in Sources */, 26C0C1C12073C72000C530DA /* ForwardTableViewCell.swift in Sources */, + 26A2349021789B9700E73CF5 /* Locale+Language.swift in Sources */, 8503B51D20503B4C006F0593 /* BaseNynjaButton.swift in Sources */, A497F56E20EFA8B6005CC60F /* ErrorsHandler.swift in Sources */, 263D66311FE8D30200A509F8 /* TypingExtension+BERT.swift in Sources */, @@ -14624,6 +14737,7 @@ FB61D663214FF96200CB2A1F /* ColorsConstants.swift in Sources */, A42CE5CA20692EDB000889CC /* Profile_Spec.swift in Sources */, A42CE58E20692EDB000889CC /* ok2.swift in Sources */, + 4BF2C3F02189F58F00E59F6C /* ServerSignal.swift in Sources */, F11786EB20AC3778007A9A1B /* Configurable.swift in Sources */, 26B32B6B1FE1717900888A0A /* WeakRef.swift in Sources */, A42CE5D220692EDB000889CC /* receiveTask_Spec.swift in Sources */, @@ -14636,6 +14750,7 @@ 26352916207572AA00DC6FBD /* JobExtension.swift in Sources */, 8566BB12215BC39D00320E15 /* FetchType.swift in Sources */, 8520040120D4672E007C0036 /* StickerPack.swift in Sources */, + 4B7E93382170D1BC001558CF /* RootNavigationController.swift in Sources */, 26A8562E2074CB9500C642EA /* Array+UIView.swift in Sources */, 26C0C1D22073CBA200C530DA /* DialogCellModel.swift in Sources */, 26C0C1CC2073C93F00C530DA /* ForwardSelectorMode.swift in Sources */, @@ -14647,10 +14762,12 @@ 85D669E820BD95B300FBD803 /* UIView+Shadow.swift in Sources */, 2651094020ADBB0200F1B38B /* NotificationSettingProtocol.swift in Sources */, F11DF06920BD9E7900F3E005 /* NavigationProtocol.swift in Sources */, + 2600CCB1216B8DC600EDC9C3 /* Language.swift in Sources */, 26A8562A2074C7FB00C642EA /* UIView+SafeArea.swift in Sources */, 26C0C1CD2073C94400C530DA /* ForwardSelectorDisplayMode.swift in Sources */, FEA65472216775CD00B44029 /* ServiceWalletExtension.swift in Sources */, 26C0C1E92073DB1000C530DA /* Localizable.swift in Sources */, + 2600CCC2216E1B2600EDC9C3 /* DescExtension.swift in Sources */, 268C62E32008DA0900433705 /* UIImageExtensions.swift in Sources */, A4330A552109D60D0060BD93 /* QueryFactoryProtocol.swift in Sources */, A4330A572109D6120060BD93 /* QueryFactory.swift in Sources */, @@ -14663,7 +14780,9 @@ A42CE5F220692EDB000889CC /* chain_Spec.swift in Sources */, A42CE5BC20692EDB000889CC /* Cursor_Spec.swift in Sources */, A42CE56420692EDB000889CC /* Message.swift in Sources */, + 4B3B35DA217119C9005A214A /* AmazonInitializer.swift in Sources */, A42CE58220692EDB000889CC /* CDR.swift in Sources */, + 4B3B35D32171120F005A214A /* SharedServiceFactory.swift in Sources */, A4B544EB20EFB36100EB7B0F /* errors.swift in Sources */, 8551CF12217094CD00829CF1 /* Array+Desc.swift in Sources */, 85458CE3212D731200BA8814 /* MessageIdentifiers.swift in Sources */, @@ -14708,6 +14827,7 @@ 4B6D20EC2164D4AB003ADB29 /* DeliveryStatus.swift in Sources */, 4B7C73F3215A5509007924DB /* LogWriter.swift in Sources */, 262D43872033417F002F1E45 /* FriendExtansion+BERT.swift in Sources */, + 2600CCC1216D47DC00EDC9C3 /* OptionallyActionCellViewModel.swift in Sources */, FE58F9B1208F00FE004AFDD3 /* MessageEditActionTable.swift in Sources */, F105C6A0209F71BF0091786A /* CameraInteractor.swift in Sources */, 4B4266BA204D898900194BC1 /* ForwardSelectorDisplayMode.swift in Sources */, @@ -14836,6 +14956,7 @@ E749C5651FD448DA0048DEAC /* ProgressModel.swift in Sources */, 8EE30FC61FBC983100E47F81 /* DateConverter.swift in Sources */, A46032522105D3E1009783DA /* TextView.swift in Sources */, + 8581B996217DD440006441BB /* NSAttributedString+Bounds.swift in Sources */, 8580BAE120BD99D200239D9D /* InputBar+Mentions.swift in Sources */, 3A2374DB1F26458300701045 /* FriendRequstModel.swift in Sources */, 26245F42204EF58E00C8D3DD /* BaseInteractorProtocol.swift in Sources */, @@ -14875,6 +14996,7 @@ 4B1D7DFA2029BF3400703228 /* HistoryItemsFactory.swift in Sources */, FEA65616216779F700B44029 /* WalletParams.swift in Sources */, E79385871FC32ACC00744CB0 /* DBProfile.swift in Sources */, + 26AB1419218775BB00F2BB83 /* ConversionState.swift in Sources */, 9BC9657620FF042E00052AE1 /* CallInProgressProtocols.swift in Sources */, 4B1D7E0D2029DACF00703228 /* ByNumberItemsFactory.swift in Sources */, F11DF06C20BEF43A00F3E005 /* ResourceManager.swift in Sources */, @@ -14898,6 +15020,7 @@ A42D51CC206A361400EEB952 /* ok.swift in Sources */, FEA656002167777F00B44029 /* WalletBalancesWireFrame.swift in Sources */, FEA656062167777F00B44029 /* WalletBalancesViewModel.swift in Sources */, + 4B3B35D2217111BC005A214A /* SharedServiceFactory.swift in Sources */, 26131E02210399BA00BE94F9 /* TranscribeService.swift in Sources */, 853FB0752049B4FF000996C5 /* TextTableViewCell.swift in Sources */, A42D52DC206A53AB00EEB952 /* process_Spec.swift in Sources */, @@ -14905,6 +15028,7 @@ 855EF421202CC6F800541BE3 /* GetExtendedStarsModel.swift in Sources */, A45F113420B4218D00F45004 /* UnreadCell.swift in Sources */, E7ABD2FF1FC2EDBC00E233F7 /* TagTable.swift in Sources */, + 26B7CB692178982200C83ED8 /* Locale+Language.swift in Sources */, 265AEA151FE9AFA700AC4806 /* MemberHandler.swift in Sources */, E7C36C311FC4399B00740630 /* DBService.swift in Sources */, B77C11DE2109242200CCB42E /* AssigningInterpreterInteractor.swift in Sources */, @@ -15052,7 +15176,6 @@ A48C154420EF7A15002DA994 /* LinkHandler.swift in Sources */, A45F113320B4218D00F45004 /* MessageCellFactory.swift in Sources */, A45F113120B4218D00F45004 /* BaseChatCell.swift in Sources */, - 26A8CD0E2177AEC200A33A20 /* Injectable.swift in Sources */, FEA655D62167777E00B44029 /* CreateWalletInteractor.swift in Sources */, F105C6AF20A11DDB0091786A /* TimerView.swift in Sources */, A415132220DBD59B00C2C01F /* Link_Spec.swift in Sources */, @@ -15166,7 +15289,7 @@ 85D66A0520BD963C00FBD803 /* MessagePayloadParser.swift in Sources */, FEA65618216779F700B44029 /* TransferParams.swift in Sources */, FEA656032167777F00B44029 /* WalletBalancesTableViewCell.swift in Sources */, - 265EB72020A86A5400C1483E /* LangExtended.swift in Sources */, + 265EB72020A86A5400C1483E /* Language.swift in Sources */, 5BC1D38220D3B54B002A44B3 /* CallInfoViewLayout.swift in Sources */, A42D51AF206A361400EEB952 /* receiveTask.swift in Sources */, 857C070620DB8A3D00626EEB /* StickerInputState.swift in Sources */, @@ -15222,7 +15345,6 @@ 852003FE20D46680007C0036 /* StickerPack.swift in Sources */, B723C636204DA56600884FFD /* SettingsDataAndStorageTableDelegate.swift in Sources */, 3AC07E3C1F055B3F00ADBE26 /* DoubleExtensions.swift in Sources */, - 264312EC210DE4040057E8B0 /* LanguageSectionCoordinator.swift in Sources */, 85579882209322A8007050B8 /* StickerMenuDataSource.swift in Sources */, 8506F001206BF5DA008B2D7F /* ChatPlaceholderWheelItemView.swift in Sources */, 85C16C3C20D261C000EDB77E /* MessageStickerView.swift in Sources */, @@ -15693,6 +15815,7 @@ 4B8FC31A2163CC6700602D6B /* BaseChatCellModel+ModelType.swift in Sources */, 8504DEAD2069438D006722AC /* Media.swift in Sources */, 26B06C8020602643005BF9AF /* CarouselPickerCollectionViewCell.swift in Sources */, + 2600CCC4216E419E00EDC9C3 /* MessageInteractor+AutoConversion.swift in Sources */, C9C6952E202349DA00A57297 /* SelectCountryCellLayout.swift in Sources */, 3A237BCD1F30E5D400C42B6E /* RosterHandler.swift in Sources */, A4868F2F2121D349001F624E /* DetectorDebuggingPreventer.swift in Sources */, @@ -15923,6 +16046,7 @@ FEA655D42167777E00B44029 /* CreateWalletWireFrame.swift in Sources */, 26F87DF62142B40F000ED2C8 /* SenderService.swift in Sources */, 26FF00A61FCC2ED5002170B1 /* MQTTService.swift in Sources */, + 4B3B35D7217119A0005A214A /* AmazonInitializer.swift in Sources */, 26D621F42069778400595E13 /* ChatWheelItemView.swift in Sources */, E7E06C661F792AEF00BFC8FA /* LoginWheelContainerDelegate.swift in Sources */, FB16E79D20EFCF15009FA203 /* CryptoMoney.swift in Sources */, @@ -15943,6 +16067,7 @@ 260313A320A0A4BA009AC66D /* ActionCellViewModel.swift in Sources */, E764919C1F7A5485001E741C /* MainWheelContainerDataSource.swift in Sources */, A4A242482060373000B0A804 /* BaseHandler.swift in Sources */, + 2600CCBF216D447200EDC9C3 /* OptionallyActionCell.swift in Sources */, 853E595120D6AF59007799B9 /* Desc+Room.swift in Sources */, A4679B8820B2DA550021FE9C /* ArrayExtension.swift in Sources */, 4B058F03204EA928004C7D9F /* DAOProtocol.swift in Sources */, @@ -16063,6 +16188,7 @@ 8580BAEC20BD9A7100239D9D /* LinkRecognizable.swift in Sources */, 26C8555D215123B00037F106 /* AudioPlayable.swift in Sources */, A4F3DAAF2084944900FF71C7 /* RoomModel.swift in Sources */, + 4BF2C3E02188B27500E59F6C /* Injectable.swift in Sources */, A44B4D5620CE9BDF00CA700A /* ArrowCell.swift in Sources */, 4BE2C5E32142EB0F00A73DD9 /* AudioManagerDelegate.swift in Sources */, 85D66A2320BD970400FBD803 /* BBTagBuilder.swift in Sources */, @@ -16206,6 +16332,7 @@ 8580BAC620BD983400239D9D /* MessageProtocols.swift in Sources */, 2683F77A203F38E30003181A /* UIPickerView.swift in Sources */, A4679BAB20B2DD100021FE9C /* SubscribersCollectionDataSource.swift in Sources */, + 9B2C6693216F82AB00116486 /* NynjaJoinByLinkService.swift in Sources */, B7EF8ED7210C598800E0E981 /* InterpretationTypeCellLayout.swift in Sources */, 2648C40D2069B52100863614 /* ChangeNumberStep1Protocols.swift in Sources */, A43B25B920AB1E7600FF8107 /* String+Range.swift in Sources */, @@ -16335,10 +16462,12 @@ 8562853220D140FC000C9739 /* InputBar+ButtonType.swift in Sources */, A43B25AC20AB1DFA00FF8107 /* BaseInputView.swift in Sources */, F127F3BA20BF03BF007A6F87 /* DateFormatterExtension.swift in Sources */, + 4B3B35D9217119BF005A214A /* AmazonInitializerImpl.swift in Sources */, B7B546AE210D9C8C002DCA55 /* CircleView.swift in Sources */, E79061B81FBF2243009FD83A /* FeatureTable.swift in Sources */, 5B5EE777EF301CFC1FDCF307 /* CreateGroupInteractor.swift in Sources */, 8580BAD820BD98E700239D9D /* CounterView.swift in Sources */, + 26A421CD217E027600120542 /* SnackBar.swift in Sources */, A4E6EDBB20724AAA004B456C /* BadgeNumberServiceProtocol.swift in Sources */, A415132820DBE40F00C2C01F /* LinkTable.swift in Sources */, C02DD71CA3832908D422B83C /* CreateGroupWireframe.swift in Sources */, @@ -16386,6 +16515,7 @@ 1A9DFA4A2ED5ACE55035FA17 /* SelectCountryWireframe.swift in Sources */, A42D51CA206A361400EEB952 /* ExtendedStar.swift in Sources */, 01AA377709C2831ACE2F08D0 /* AddContactByUsernameProtocols.swift in Sources */, + 9BFFE61B2178DD00004FE2CA /* BannerView.swift in Sources */, A408A0BB20C174040029F54B /* ChannelsListViewController.swift in Sources */, A43B25AA20AB1DFA00FF8107 /* ALTextInputUtilities.swift in Sources */, B767F48C215D1AA500FA9B27 /* ComingSoonProtocol.swift in Sources */, @@ -16399,6 +16529,7 @@ 4B749F06214FEE4F002F3A33 /* VerifyNumberViewController.swift in Sources */, A42D52AF206A53AA00EEB952 /* Room_Spec.swift in Sources */, 26053123212741C2002E1CF1 /* LogOutputWireFrame.swift in Sources */, + 2600CCB9216C43BF00EDC9C3 /* DescriptionCell.swift in Sources */, A45F112B20B4218D00F45004 /* MessageContainerView.swift in Sources */, A7285B8B56BFCA857AD9BA8A /* AddContactByUsernameWireframe.swift in Sources */, 2603139A20A0A4B9009AC66D /* LanguageSelectorTableDelegate.swift in Sources */, @@ -16474,6 +16605,7 @@ E27620AE3F571711EE70C0C8 /* LanguageSettingsPresenter.swift in Sources */, BC1BA70218B40F3F64841848 /* LanguageSettingsInteractor.swift in Sources */, F922EF38E4C1662D54CE533D /* LanguageSettingsWireframe.swift in Sources */, + 26A421CF217E541800120542 /* SnackBarLayoutGuide.swift in Sources */, E8AFC57E49EED25C3F5001B7 /* SecurityProtocols.swift in Sources */, 3219C8F242591BA17953FF33 /* SecurityViewController.swift in Sources */, A45F110720B4218D00F45004 /* ChatConfiguration.swift in Sources */, @@ -16501,9 +16633,9 @@ FB816EF520B5B87300093DCD /* Bert.swift in Sources */, FB61D664214FF96200CB2A1F /* ColorsConstants.swift in Sources */, 85458CE8212D73E600BA8814 /* MucExtension.swift in Sources */, + 4BF2C3E32188BA7B00E59F6C /* IdentifierComponentValue.swift in Sources */, 4B348FFB2163836D00CCB0E3 /* ReversableDataSource.swift in Sources */, FB61D64E214FEC7D00CB2A1F /* FontsConstants.swift in Sources */, - 85458CFF212D7BCB00BA8814 /* Desc+Construct.swift in Sources */, FB816EF220B5B6F100093DCD /* BaseMQTTModel.swift in Sources */, 4B8FC31D2163D6A700602D6B /* Cloneable.swift in Sources */, 4B752B5E2163A4F900E852B9 /* GApiResponse.swift in Sources */, @@ -16519,7 +16651,6 @@ FB816EF820B5B89700093DCD /* StringAtom.swift in Sources */, 4BF090BE21635B9400DCCA5C /* LogServiceProtocol.swift in Sources */, 4B8FC3122163C50E00602D6B /* SpeedStringRepresentable.swift in Sources */, - 8509FC7F2158DD9B00734D93 /* Feature+Construct.swift in Sources */, A4CB153A21039BD800C3B68B /* JailbreakDetectorTest.swift in Sources */, FB816EF420B5B85E00093DCD /* Room.swift in Sources */, A4AB8E562105ECD7005F9B0C /* TextViewTest.swift in Sources */, @@ -16544,6 +16675,7 @@ A4AB8E582105F47C005F9B0C /* InputsCachePolicyTest.swift in Sources */, FB816EF920B5B8A700093DCD /* Desc.swift in Sources */, 852003FC20D45B48007C0036 /* BertBinConvertible.swift in Sources */, + 4BF2C3E42188BABC00E59F6C /* UIDeviceExtension.swift in Sources */, FB816EF320B5B85900093DCD /* Contact.swift in Sources */, 4BF090C621635F0200DCCA5C /* Message+LinkedId.swift in Sources */, A4AB8E542105EC9A005F9B0C /* InputsCachePolicy.swift in Sources */, @@ -16583,6 +16715,7 @@ FE21ACBD2113ABBF006010A0 /* DictionaryExtension.swift in Sources */, FE21ACBA2113AB4B006010A0 /* KeychainService.swift in Sources */, 4B348FD62163668500CCB0E3 /* LogServiceStub.swift in Sources */, + 4BF2C3E92189B49500E59F6C /* Localizable.swift in Sources */, 85458CDC212D6FFF00BA8814 /* String+Split.swift in Sources */, FB61D652214FEC8300CB2A1F /* LocalizableConstants.swift in Sources */, 85458CE5212D731300BA8814 /* MessageIdentifiers.swift in Sources */, @@ -16672,7 +16805,7 @@ 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 = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; DEBUG_INFORMATION_FORMAT = dwarf; @@ -16685,7 +16818,7 @@ OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DSHARE_EXTENSION -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=20"; PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "ec098bfa-89f0-4ff0-b3b1-79c269e0abd0"; + PROVISIONING_PROFILE = "ca70fa1d-39bf-4113-949d-a679780f3059"; PROVISIONING_PROFILE_SPECIFIER = DevBundle_AdHocExt; SKIP_INSTALL = YES; SWIFT_VERSION = 4.0; @@ -16848,7 +16981,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Nynja/Resources/Nynja.entitlements; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -16861,7 +16994,7 @@ OTHER_SWIFT_FLAGS = "$(inherited) -D SQLITE_HAS_CODEC -D GRDBCIPHER $(inherited) \"-D\" \"COCOAPODS\" -Xfrontend -debug-time-function-bodies -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=20"; PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "6b20b280-0985-4b2b-b941-4913fb8150de"; + PROVISIONING_PROFILE = "11e65b1d-3e07-422b-92aa-fe98acb114fd"; PROVISIONING_PROFILE_SPECIFIER = DevBundle_adhoc; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h"; @@ -17175,7 +17308,7 @@ MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "e4b654e5-ea23-4135-bb19-3edc95e49642"; + PROVISIONING_PROFILE = "a872b156-bcb3-4a37-9b6d-2b3664c04e52"; PROVISIONING_PROFILE_SPECIFIER = NynjaRC_adhoc; SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h"; @@ -17207,7 +17340,7 @@ OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DSHARE_EXTENSION"; PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "5dca1418-4061-4c52-958f-3edf7251f19b"; + PROVISIONING_PROFILE = "a5cb03c0-04d6-475e-a5d2-b72ef5079b14"; PROVISIONING_PROFILE_SPECIFIER = NynjaRC_adhocExt; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; diff --git a/Nynja.xcodeproj/xcshareddata/xcschemes/Nynja-Share.xcscheme b/Nynja.xcodeproj/xcshareddata/xcschemes/Nynja-Share.xcscheme index 8c835175d..f3fea11b6 100644 --- a/Nynja.xcodeproj/xcshareddata/xcschemes/Nynja-Share.xcscheme +++ b/Nynja.xcodeproj/xcshareddata/xcschemes/Nynja-Share.xcscheme @@ -49,6 +49,20 @@ ReferencedContainer = "container:Nynja.xcodeproj"> + + + + - - - - Void) -> Bool { + + guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let url = userActivity.webpageURL else { + return false + } + + return NynjaJoinByLinkService.shared().handleJoin(by: url) + } + func applicationWillResignActive(_ application: UIApplication) { window?.addSubview(backgroundImageView) } @@ -115,6 +125,8 @@ private extension AppDelegate { } private func wipeStorage() { + MQTTService.sharedInstance.wasRunApp = storageService.wasRun + if !storageService.wasRun { LogService.log(topic: .db) { return "Clear storage: AppDelegate - if it is first runs" } storageService.clearStorage() @@ -161,26 +173,7 @@ private extension AppDelegate { } func setupAmazon() { - let amazonService = ThirdPartyServicesFactory.amazon - - let credentialsProvider = AWSStaticCredentialsProvider( - accessKey: amazonService.serviceConfig.accessKey, - secretKey: amazonService.serviceConfig.secretKey - ) - - let defaultConfiguration = AWSServiceConfiguration( - region: .USWest2, - credentialsProvider: credentialsProvider - )! - AWSS3TransferManager.register(with: defaultConfiguration, forDestination: .default) - - let stickerStorageConfiguration = AWSServiceConfiguration( - region: .EUCentral1, - credentialsProvider: credentialsProvider - )! - AWSS3TransferManager.register(with: stickerStorageConfiguration, forDestination: .stickerPacks) - - AWSServiceManager.default().defaultServiceConfiguration = defaultConfiguration + ServiceFactory().makeAmazonInitializer().initialize() } private func setupIntercom() { diff --git a/Nynja/ChatItemsFactory.swift b/Nynja/ChatItemsFactory.swift index c414c37da..14cc2957a 100644 --- a/Nynja/ChatItemsFactory.swift +++ b/Nynja/ChatItemsFactory.swift @@ -9,10 +9,9 @@ class ChatItemsFactory: WCBaseItemsFactory { var location: ImageActionItemModel { - let item = ImageActionItemModel(nameImage: "ic_location", navItem: .location, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showLocation(indexPath: indexPath) + let item = ImageActionItemModel(nameImage: "ic_location", navItem: .location, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showMap(indexPath: indexPath) }) - item.subitems = [starred, send, recents] return item } @@ -36,7 +35,6 @@ class ChatItemsFactory: WCBaseItemsFactory { let item = ImageActionItemModel(nameImage: "ic_gallery", navItem: .media, action: { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showMedia(indexPath: indexPath) }) - item.subitems = [gallery, recentsMedia] return item } @@ -132,4 +130,10 @@ class ChatItemsFactory: WCBaseItemsFactory { return item } + var chatOptions: ImageActionItemModel { + return ImageActionItemModel(nameImage: "ic_p2p_chat_settings", navItem: .chatOptions, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showChatOptions(indexPath: indexPath) + }) + } + } diff --git a/Nynja/DB/Models/DBConvertMessage.swift b/Nynja/DB/Models/DBConvertMessage.swift index 22c14c406..e64ac6c90 100644 --- a/Nynja/DB/Models/DBConvertMessage.swift +++ b/Nynja/DB/Models/DBConvertMessage.swift @@ -15,23 +15,38 @@ class DBConvertMessage: Record, DBModelProtocol { var process: Int var value: String var language: String + var isAuto: Bool - init(messageId: String, type: Int, process: Int, value: String, language: String) { + init(messageId: String, + type: Int, + process: Int, + value: String, + language: String, + isAuto: Bool) { + self.messageId = messageId self.type = type self.process = process self.value = value self.language = language + self.isAuto = isAuto super.init() } - convenience init(messageId: String, type: Type, process: Process, value: String, language: String) { + convenience init(messageId: String, + type: Type, + process: Process, + value: String, + language: String, + isAuto: Bool) { + self.init(messageId: messageId, type: type.rawValue, process: process.rawValue, value: value, - language: language) + language: language, + isAuto: isAuto) } // MARK: - Record @@ -46,6 +61,7 @@ class DBConvertMessage: Record, DBModelProtocol { process = row[ConvertMessageTable.Column.process.title] value = row[ConvertMessageTable.Column.value.title] language = row[ConvertMessageTable.Column.language.title] + isAuto = row[ConvertMessageTable.Column.auto.title] super.init() } @@ -55,6 +71,7 @@ class DBConvertMessage: Record, DBModelProtocol { container[ConvertMessageTable.Column.process.title] = process container[ConvertMessageTable.Column.value.title] = value container[ConvertMessageTable.Column.language.title] = language + container[ConvertMessageTable.Column.auto.title] = isAuto } diff --git a/Nynja/DB/Tables/ConvertMessageTable.swift b/Nynja/DB/Tables/ConvertMessageTable.swift index 5fb93c51d..439560a59 100644 --- a/Nynja/DB/Tables/ConvertMessageTable.swift +++ b/Nynja/DB/Tables/ConvertMessageTable.swift @@ -21,6 +21,8 @@ class ConvertMessageTable: Table { t.column(Column.process, .integer) t.column(Column.value, .text) t.column(Column.language, .text) + t.column(Column.auto, .boolean).defaults(to: false) + t.primaryKey([Column.messageId.title, Column.type.title], onConflict: .replace) } } @@ -35,6 +37,7 @@ extension ConvertMessageTable { case process case value case language + case auto } } diff --git a/Nynja/DefaultMessageProcessingManager.swift b/Nynja/DefaultMessageProcessingManager.swift index 3ed5bb085..7896a2f0d 100644 --- a/Nynja/DefaultMessageProcessingManager.swift +++ b/Nynja/DefaultMessageProcessingManager.swift @@ -32,7 +32,6 @@ class DefaultMessagesProcessingManager: DefaultMessagesProcessingManagerInterfac }() static var shared = DefaultMessagesProcessingManager() - private let typesWithAttachment = [SendMessageType.audio, .image, .file, .video] private let typingStatusCache: ITypingStatusCache = TypingStatusCache(config: TypingStatusCache.Config(cacheValueMinimalRenewTime: 4)) @@ -75,14 +74,13 @@ class DefaultMessagesProcessingManager: DefaultMessagesProcessingManagerInterfac extension DefaultMessagesProcessingManager { @discardableResult func uploadMessage(_ message: Message) -> ProgressModel? { - guard let typeString = message.mainFile?.mime, - let type = SendMessageType(rawValue: typeString) else { + guard let type = message.sendType else { return nil } - + postSendingStatusIfNeeded(type, message: message) - if typesWithAttachment.contains(type) { + if message.isDownloadable { queueMessages.append(message) return sendMessageWithAttach(message) } else { @@ -93,17 +91,13 @@ extension DefaultMessagesProcessingManager { } func isValidForUploading(_ message: Message) -> Bool { - guard let typeString = message.mainFile?.mime, let type = SendMessageType(rawValue: typeString) else { - return false - } - return typesWithAttachment.contains(type) + return message.isDownloadable } func downloadMessage(_ message: Message) { guard let url = message.mainUrl, let mainFile = message.mainFile, - let typeString = mainFile.mime, - let type = SendMessageType(rawValue: typeString) else { + let type = message.sendType else { return } @@ -114,14 +108,11 @@ extension DefaultMessagesProcessingManager { progress.fileSize = fileSize } - guard case .notStarted = progress.status else { - return notify(with: progress) - } - - guard typesWithAttachment.contains(type) else { + guard message.isDownloadable, + case .notStarted = progress.status else { return } - + TransferManager.shared.download(url: url, listener: self) } @@ -204,7 +195,7 @@ extension DefaultMessagesProcessingManager { // MARK: - MessageProcessingDelegate extension DefaultMessagesProcessingManager { - func updateProgress(_ progress: ProgressModel) { + func update(progress: ProgressModel) { notify(with: progress) } } @@ -237,7 +228,7 @@ private extension DefaultMessagesProcessingManager { } func notify(with progress: ProgressModel) { - self.delegate?.updateProgress(progress) + delegate?.update(progress: progress) } func sendTypingStatus(_ status: TypingModelType, message: Message) { diff --git a/Nynja/Extensions/Bundle+Keys.swift b/Nynja/Extensions/Bundle+Keys.swift index 94eaffcfe..05d3a2dd9 100644 --- a/Nynja/Extensions/Bundle+Keys.swift +++ b/Nynja/Extensions/Bundle+Keys.swift @@ -71,4 +71,8 @@ extension Bundle { let secure: NSString = NSString(string:value) return secure.boolValue } + + var associatedDomain: String { + return object(forInfoDictionaryKey: "AssociatedDomain") as! String + } } diff --git a/Nynja/Extensions/Locale+Language.swift b/Nynja/Extensions/Locale+Language.swift new file mode 100644 index 000000000..86a9217c3 --- /dev/null +++ b/Nynja/Extensions/Locale+Language.swift @@ -0,0 +1,26 @@ +// +// Locale+Language.swift +// Nynja +// +// Created by Andrey Reznik on 18.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation +import CoreFoundation + +extension Locale { + + static var systemLanguage: Language { + let language = Language(language: .english) + let enLocale = Locale(identifier: language.language) + return Locale.current.languageCode + .flatMap { + guard let name = enLocale.localizedString(forLanguageCode: $0) else { + return nil + } + return Language(language: $0, name: name) + } + ?? language + } +} diff --git a/Nynja/Extensions/Models/Desc/Desc+Messages.swift b/Nynja/Extensions/Models/Desc/Desc+Messages.swift index 455768787..f1e8b1937 100644 --- a/Nynja/Extensions/Models/Desc/Desc+Messages.swift +++ b/Nynja/Extensions/Models/Desc/Desc+Messages.swift @@ -128,3 +128,13 @@ extension Desc { return (origin: originalText, translate: translatedText) } } + + +extension Array where Element: Desc { + + func files(without types: [SendMessageType]) -> [Desc] { + return filter { element -> Bool in + return !types.contains(where: { $0 == element.type }) + } + } +} diff --git a/Nynja/Extensions/Models/Feature/FeatureExtension.swift b/Nynja/Extensions/Models/Feature/FeatureExtension.swift index 99ce7663b..554992abd 100644 --- a/Nynja/Extensions/Models/Feature/FeatureExtension.swift +++ b/Nynja/Extensions/Models/Feature/FeatureExtension.swift @@ -150,6 +150,20 @@ extension Feature { guard let value = value else { return nil } return Double(value) } + + var boolValue: Bool? { + guard let value = value else { return nil } + return Bool(value) + } + + var languageValue: Language { + guard let signature = value?.split(separator: ":"), + let name = signature.first, + let lang = signature.last else { + return .init(language: "", name: "") + } + return .init(language: String(lang), name: String(name)) + } } // MARK: - Files diff --git a/Nynja/Extensions/Models/Message/Message+Factory.swift b/Nynja/Extensions/Models/Message/Message+Factory.swift index 591f67e2b..454375d63 100644 --- a/Nynja/Extensions/Models/Message/Message+Factory.swift +++ b/Nynja/Extensions/Models/Message/Message+Factory.swift @@ -41,21 +41,39 @@ extension Message { message.linkedId = link message.status = nil - message.files = message.files?.filter({ - $0.mime != "translate" - }) + message.files = message.files?.files(without: [.translate, .autotranslate, .transcribe]) return message } - + + var clone: Message { + return Message(message: self) + } } extension Message { - var canBeTranslated: Bool { - return mainFile?.type == .text && - !isOwn && - !isSystem + func isСonvertible(relatively serverId: Int64) -> Bool { + guard let id = id else { + return false + } + return id > serverId && isСonvertible + } + + var isСonvertible: Bool { + return !isOwn && !isSystem + } + + var isTranslatable: Bool { + return mainFile?.type == .text + } + + var isTranslatableTranscribe: Bool { + return isExistFile(for: .transcribe) + } + + var isTranscribable: Bool { + return mainFile?.type == .audio } func isFrom(chatId: String) -> Bool { diff --git a/Nynja/Extensions/SwiftLibrary/Array/Array+Message.swift b/Nynja/Extensions/SwiftLibrary/Array/Array+Message.swift index 7279f792d..87a6e7fbc 100644 --- a/Nynja/Extensions/SwiftLibrary/Array/Array+Message.swift +++ b/Nynja/Extensions/SwiftLibrary/Array/Array+Message.swift @@ -9,9 +9,10 @@ import Foundation extension Array where Element == Message { - var messagesUrls:[URL] { - return self.compactMap { message -> URL? in - return message.mainUrl + + var messagesUrls: [URL] { + return self.compactMap { + $0.isDownloadable ? $0.mainUrl : nil } } } diff --git a/Nynja/Generated/AssetsConstants.swift b/Nynja/Generated/AssetsConstants.swift index 085c087d9..78d609564 100644 --- a/Nynja/Generated/AssetsConstants.swift +++ b/Nynja/Generated/AssetsConstants.swift @@ -604,6 +604,8 @@ internal extension Image { static var icNewVideoCall: ImageAsset { return ImageAsset(name: "ic_new_video_call") } /// "ic_options" static var icOptions: ImageAsset { return ImageAsset(name: "ic_options") } + /// "ic_p2p_chat_settings" + static var icP2pChatSettings: ImageAsset { return ImageAsset(name: "ic_p2p_chat_settings") } /// "ic_recents" static var icRecents: ImageAsset { return ImageAsset(name: "ic_recents") } /// "ic_search" diff --git a/Nynja/Generated/ColorsConstants.swift b/Nynja/Generated/ColorsConstants.swift index 05d058ee5..713effb5a 100644 --- a/Nynja/Generated/ColorsConstants.swift +++ b/Nynja/Generated/ColorsConstants.swift @@ -33,8 +33,8 @@ internal extension SGColor { static let callGradientEnd = #colorLiteral(red: 0.17254902, green: 0.18039216, blue: 0.2, alpha: 0.0) /// 0x2c2e33ff (r: 44, g: 46, b: 51, a: 255) static let callGradientStart = #colorLiteral(red: 0.17254902, green: 0.18039216, blue: 0.2, alpha: 1.0) - /// 0x00e359ff (r: 0, g: 227, b: 89, a: 255) - static let callGreen = #colorLiteral(red: 0.0, green: 0.8901961, blue: 0.34901962, alpha: 1.0) + /// 0x01ae45ff (r: 1, g: 174, b: 69, a: 255) + static let callGreen = #colorLiteral(red: 0.003921569, green: 0.68235296, blue: 0.27058825, alpha: 1.0) /// 0x00000000 (r: 0, g: 0, b: 0, a: 0) static let clear = #colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0) /// 0x505255ff (r: 80, g: 82, b: 85, a: 255) diff --git a/Nynja/Generated/LocalizableConstants.swift b/Nynja/Generated/LocalizableConstants.swift index 1847b5909..6d8e67dfc 100644 --- a/Nynja/Generated/LocalizableConstants.swift +++ b/Nynja/Generated/LocalizableConstants.swift @@ -126,7 +126,9 @@ internal extension String { static var callConnecting: String { return localizable.tr("Localizable", "call_connecting") } /// Decline static var callDecline: String { return localizable.tr("Localizable", "call_decline") } - /// Call failed + /// Connection lost, reconnecting... + static var callDisconnected: String { return localizable.tr("Localizable", "call_disconnected") } + /// Connection dropped static var callFailed: String { return localizable.tr("Localizable", "call_failed") } /// Incoming Voice Call... static var callIncoming: String { return localizable.tr("Localizable", "call_incoming") } @@ -138,6 +140,24 @@ internal extension String { static func callInfoBannerMembers(_ p1: Int) -> String { return localizable.tr("Localizable", "call_info_banner_members", p1) } + /// Invite + static var callInvite: String { return localizable.tr("Localizable", "call_invite") } + /// Copied to Clipboard! + static var callInviteCopyBannerText: String { return localizable.tr("Localizable", "call_invite_copy_banner_text") } + /// Copy Invitation Link + static var callInviteCopyInvitationLink: String { return localizable.tr("Localizable", "call_invite_copy_invitation_link") } + /// Group Participants + static var callInviteGroupParticipants: String { return localizable.tr("Localizable", "call_invite_group_participants") } + /// You have a call in progress. Would you like to end the current call and accept the new one? + static var callJoinByLinkAlertQuestion: String { return localizable.tr("Localizable", "call_join_by_link_alert_question") } + /// Link is broken + static var callJoinByLinkErrorBroken: String { return localizable.tr("Localizable", "call_join_by_link_error_broken") } + /// Link is expired + static var callJoinByLinkErrorExpired: String { return localizable.tr("Localizable", "call_join_by_link_error_expired") } + /// Failed to join conference + static var callJoinByLinkErrorFailedToJoin: String { return localizable.tr("Localizable", "call_join_by_link_error_failed_to_join") } + /// You can't join the call as the participant limit has been reached + static var callJoinByLinkErrorLimitReached: String { return localizable.tr("Localizable", "call_join_by_link_error_limit_reached") } /// You have selected maximum amount of call members static var callMaximumParticipantsReached: String { return localizable.tr("Localizable", "call_maximum_participants_reached") } /// There is no place for new users in the call @@ -576,8 +596,30 @@ internal extension String { static var lsAutoTOnReceipt: String { return localizable.tr("Localizable", "ls_auto_t_on_receipt") } /// Auto-translate on sending static var lsAutoTOnSending: String { return localizable.tr("Localizable", "ls_auto_t_on_sending") } + /// Auto transcribe voice messages + static var lsAutoTranscribeVoice: String { return localizable.tr("Localizable", "ls_auto_transcribe_voice") } + /// Auto translate incoming text messages + static var lsAutoTranslateInc: String { return localizable.tr("Localizable", "ls_auto_translate_inc") } + /// Auto translate outgoing text messages + static var lsAutoTranslateOut: String { return localizable.tr("Localizable", "ls_auto_translate_out") } + /// Auto translate voice messages + static var lsAutoTranslateVoice: String { return localizable.tr("Localizable", "ls_auto_translate_voice") } /// Language for translation on sending static var lsLanguageForTranslation: String { return localizable.tr("Localizable", "ls_language_for_translation") } + /// Language to translate incoming message + static var lsLanguageSelectorTitleOnIncomingTranslate: String { return localizable.tr("Localizable", "ls_language_selector_title_on_incoming_translate") } + /// Language to send translated message + static var lsLanguageSelectorTitleOnOutgoingTranslate: String { return localizable.tr("Localizable", "ls_language_selector_title_on_outgoing_translate") } + /// Language that other user speaks to you + static var lsLanguageSelectorTitleOnTranscribe: String { return localizable.tr("Localizable", "ls_language_selector_title_on_transcribe") } + /// Auto + static var lsModeAuto: String { return localizable.tr("Localizable", "ls_mode_auto") } + /// Manual + static var lsModeManual: String { return localizable.tr("Localizable", "ls_mode_manual") } + /// Off + static var lsModeOff: String { return localizable.tr("Localizable", "ls_mode_off") } + /// Automatically translate outgoing text messages? + static var lsModeSheetTitle: String { return localizable.tr("Localizable", "ls_mode_sheet_title") } /// My language in chat static var lsMyLanguage: String { return localizable.tr("Localizable", "ls_my_language") } /// None @@ -586,8 +628,28 @@ internal extension String { static var lsOnce: String { return localizable.tr("Localizable", "ls_once") } /// LANGUAGE SETTINGS static var lsTitle: String { return localizable.tr("Localizable", "ls_title") } + /// What language does %@ speak to you in? + static func lsTrancribeDescription1(_ p1: String) -> String { + return localizable.tr("Localizable", "ls_trancribe_description_1", p1) + } + /// NYNJA will automatically transcribe incoming voice messages to text when this feature is turned on. When turned off, you can long press on any voice message in a chat to convert it to text manually. + static var lsTrancribeDescription2: String { return localizable.tr("Localizable", "ls_trancribe_description_2") } + /// Turn on if you want NYNJA to translate voice messages automatically after they have been transcribed to text. + static var lsTrancribeDescription3: String { return localizable.tr("Localizable", "ls_trancribe_description_3") } + /// Transcribe voice messages to text + static var lsTranscribe: String { return localizable.tr("Localizable", "ls_transcribe") } /// Language for transcription static var lsTranscriptionLanguage: String { return localizable.tr("Localizable", "ls_transcription_language") } + /// NYNJA will automatically translate incoming text messages when this feature is turned on. When turned off, you can long press on any text message in a chat to translate it manually. + static var lsTranslateIncDescription: String { return localizable.tr("Localizable", "ls_translate_inc_description") } + /// Translate incoming messages + static var lsTranslateIncoming: String { return localizable.tr("Localizable", "ls_translate_incoming") } + /// What language do you want to send translated messages in? + static var lsTranslateOutDescription1: String { return localizable.tr("Localizable", "ls_translate_out_description_1") } + /// When set to Auto, NYNJA will automatically translate your outgoing messages into the language you choose. On manual, you will see an up arrow icon when sending a message in a chat, where you can check the translation before sending each message. When off, NYNJA will not translate your outgoing messages. + static var lsTranslateOutDescription2: String { return localizable.tr("Localizable", "ls_translate_out_description_2") } + /// Translate outgoing messages + static var lsTranslateOutcoming: String { return localizable.tr("Localizable", "ls_translate_outcoming") } /// Undefined static var mainUndefined: String { return localizable.tr("Localizable", "main_undefined") } /// Hybrid @@ -712,7 +774,7 @@ internal extension String { static var numberOrRegionEmpty: String { return localizable.tr("Localizable", "number_or_region_empty") } /// NYNJA static var nynja: String { return localizable.tr("Localizable", "nynja") } - /// Hey, I'm using NYNJA to chat. Join me! Download it here: https://www.nynja.biz/ + /// I'm using the new SuperApp! called NYNJA! It is an amazing productivity and communications app for everyone like you've never seen before https://beta.nynja.net/ static var nynjaShareString: String { return localizable.tr("Localizable", "nynja_share_string") } /// NYNJA Support static var nynjaSupport: String { return localizable.tr("Localizable", "nynjaSupport") } @@ -792,7 +854,7 @@ internal extension String { static var profileGoToChats: String { return localizable.tr("Localizable", "profile_go_to_chats") } /// Go to groups static var profileGoToGroups: String { return localizable.tr("Localizable", "profile_go_to_groups") } - /// Go to history + /// Go to contact history static var profileGoToHistory: String { return localizable.tr("Localizable", "profile_go_to_history") } /// Go to starred messages static var profileGoToStarredMessages: String { return localizable.tr("Localizable", "profile_go_to_starred_messages") } @@ -1050,12 +1112,18 @@ internal extension String { static var tpStartToType: String { return localizable.tr("Localizable", "tp_start_to_type") } /// Transcribing... static var tpTranscribing: String { return localizable.tr("Localizable", "tp_transcribing") } + /// Transcribing failed. Please try again. + static var tpTranscribingFailed: String { return localizable.tr("Localizable", "tp_transcribing_failed") } /// Transcription static var tpTranscription: String { return localizable.tr("Localizable", "tp_transcription") } /// Translating... static var tpTranslating: String { return localizable.tr("Localizable", "tp_translating") } /// Translation static var tpTranslation: String { return localizable.tr("Localizable", "tp_translation") } + /// Translation failed. Please try again. + static var tpTranslationFailed: String { return localizable.tr("Localizable", "tp_translation_failed") } + /// Original and translated message are the same language. + static var tpTranslationLanguageIsEqual: String { return localizable.tr("Localizable", "tp_translation_language_is_equal") } /// Transcribe with Google static var transcribe: String { return localizable.tr("Localizable", "transcribe") } /// Another language @@ -1268,6 +1336,8 @@ internal extension String { static var wheelItemChangeNumber: String { return localizable.tr("Localizable", "wheel_item_changeNumber") } /// Channels static var wheelItemChannels: String { return localizable.tr("Localizable", "wheel_item_channels") } + /// Chat Options + static var wheelItemChatOptions: String { return localizable.tr("Localizable", "wheel_item_chatOptions") } /// Chats static var wheelItemChats: String { return localizable.tr("Localizable", "wheel_item_chats") } /// Contact diff --git a/Nynja/HomeItemsFactory.swift b/Nynja/HomeItemsFactory.swift index 61da44877..c4c64d1b6 100644 --- a/Nynja/HomeItemsFactory.swift +++ b/Nynja/HomeItemsFactory.swift @@ -32,7 +32,6 @@ class HomeItemsFactory: WCBaseItemsFactory { call.subitems = [videoCall, voiceCall] - return [call, edit, myQR, help] } diff --git a/Nynja/Library/MessageFactory/MessageFactory.swift b/Nynja/Library/MessageFactory/MessageFactory.swift index 13aa28f75..2b7161bd2 100644 --- a/Nynja/Library/MessageFactory/MessageFactory.swift +++ b/Nynja/Library/MessageFactory/MessageFactory.swift @@ -310,13 +310,13 @@ private extension MessageFactory { } func autotranslationDesc(for translation: TranslationManualView.TranlationResult, with originalPayload: String) -> Desc? { - guard case let .lang(lang) = translation.translatedLanguage else { - return nil - } let translationPayload = payloadBuilder.buildPayload(for: translation.translatedText, with: translation.mentions) - let data = Feature.translateRepresentation(language: lang.language, translation: translationPayload, users: nil) - - return makeDesc(mime: .autotranslate, payload: originalPayload, data: data) + let desc = Desc() + desc.id = IdBuilder(format: .defaultId).build() + desc.mime = SendMessageType.autotranslate.rawValue + desc.payload = originalPayload + desc.data = Feature.translateRepresentation(language: translation.translatedLanguage.language, translation: translationPayload, users: nil) + return desc } } diff --git a/Nynja/Library/UI/BannerView.swift b/Nynja/Library/UI/BannerView.swift new file mode 100644 index 000000000..d1483d914 --- /dev/null +++ b/Nynja/Library/UI/BannerView.swift @@ -0,0 +1,79 @@ +// +// BannerView.swift +// Nynja +// +// Created by Bozhko Terziev on 18.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +class BannerView: NSObject{ + + let bannerHeight:Float = 40 + let bannerTag:Int = 1234 + + // MARK: - Properties + private static var bannerView: BannerView = { + let bv = BannerView() + + // Configuration + // ... + + return bv + }() + + // Initialization + private override init() { + + super.init() + } + + // MARK: - Accessors + class func shared() -> BannerView { + return bannerView + } + + func showBannerWith(text:String) { + + let window = UIApplication.shared.keyWindow! + guard nil == window.viewWithTag(self.bannerTag) else { + return + } + + let label = UILabel() + + label.text = text + label.textAlignment = .center + label.numberOfLines = 1 + label.adjustsFontSizeToFitWidth = true + label.minimumScaleFactor = 0.5 + label.textColor = UIColor.nynja.white + label.backgroundColor = UIColor.nynja.gray + label.tag = self.bannerTag + + label.font = UIFont.makeFont(with: FontFamily.NotoSans.medium.name, height: CGFloat(19.0.adjustedByWidth)) + + window.addSubview(label) + label.snp.makeConstraints { maker in + maker.left.right.top.equalToSuperview() + if #available(iOS 11, *) { + maker.height.equalTo(self.bannerHeight.adjustedByWidth + Float(window.safeAreaInsets.top)) + } else { + maker.height.equalTo(self.bannerHeight.adjustedByWidth) + } + } + + window.windowLevel = UIWindowLevelStatusBar+1 + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + self.removeBannerView() + } + } + + private func removeBannerView(){ + let window = UIApplication.shared.keyWindow! + window.viewWithTag(self.bannerTag)?.removeFromSuperview() + window.windowLevel = UIWindowLevelStatusBar - 1 + } +} + diff --git a/Nynja/Library/UI/DateConverter.swift b/Nynja/Library/UI/DateConverter.swift index efc93a49b..a29cb27f7 100644 --- a/Nynja/Library/UI/DateConverter.swift +++ b/Nynja/Library/UI/DateConverter.swift @@ -42,7 +42,7 @@ class DialogDateConverter : DateConverter { return Strings.yesterday.localized } - formatter.locale = Locale(identifier: Language.current.rawValue) + formatter.locale = Locale(identifier: AppLanguage.current.rawValue) if timeInterval < TimeConstraint.week.rawValue { formatter.dateFormat = "EEEE".localizedDateFormat diff --git a/Nynja/Library/UI/Extensions/DateExtensions.swift b/Nynja/Library/UI/Extensions/DateExtensions.swift index fe6b673a8..01dacd82e 100644 --- a/Nynja/Library/UI/Extensions/DateExtensions.swift +++ b/Nynja/Library/UI/Extensions/DateExtensions.swift @@ -33,7 +33,7 @@ extension Date { var messageString: String { let formatter = Date.messageFormatter - formatter.locale = Locale(identifier: Language.current.rawValue) + formatter.locale = Locale(identifier: AppLanguage.current.rawValue) return formatter.string(from: self) } diff --git a/Nynja/Library/UI/Extensions/Localizable.swift b/Nynja/Library/UI/Extensions/Localizable.swift index 57dd6a276..005317c32 100644 --- a/Nynja/Library/UI/Extensions/Localizable.swift +++ b/Nynja/Library/UI/Extensions/Localizable.swift @@ -13,7 +13,7 @@ import UIKit private let appleLanguagesKey = "AppleLanguages" -enum Language: String { +enum AppLanguage: String { case english = "en" case german = "de" @@ -33,11 +33,11 @@ enum Language: String { russian, chinese_traditional, chinese_simplified, japanese, korean] - static var allValuesSelectedFirst : [Language] { + static var allValuesSelectedFirst : [AppLanguage] { get { - var values = [Language]() - Language.allValues.forEach { - if $0 == Language.current { + var values = [AppLanguage]() + AppLanguage.allValues.forEach { + if $0 == AppLanguage.current { values.insert($0, at: 0) } else { values.append($0) @@ -47,8 +47,8 @@ enum Language: String { } } - static var allValuesWithDefault: [Language] { - var languages = Language.allValues + static var allValuesWithDefault: [AppLanguage] { + var languages = AppLanguage.allValues languages.append(.empty_default) return languages } @@ -78,10 +78,10 @@ enum Language: String { return .forceLeftToRight } - static var current: Language { + static var current: AppLanguage { get { if let languageCode = UserDefaults.standard.string(forKey: appleLanguagesKey), - let language = Language(rawValue: languageCode) { + let language = AppLanguage(rawValue: languageCode) { return language } else { let preferredLanguage = NSLocale.preferredLanguages[0] as String @@ -89,8 +89,8 @@ enum Language: String { preferredLanguage.startIndex, offsetBy: 2 ) - guard let localization = Language(rawValue: preferredLanguage.substring(to: index)) else { - return Language.english + guard let localization = AppLanguage(rawValue: preferredLanguage.substring(to: index)) else { + return AppLanguage.english } return localization } @@ -109,7 +109,7 @@ enum Language: String { } static var locale: Locale { - return Locale(identifier: Language.current.rawValue) + return Locale(identifier: AppLanguage.current.rawValue) } func hasResource() -> Bool { @@ -164,7 +164,7 @@ extension String { } private var MMMM_d_format: String { - switch Language.current { + switch AppLanguage.current { case .russian: return "d MMMM" default: @@ -173,7 +173,7 @@ extension String { } private var EEEE_MMMM_d_yyyy_format: String { - switch Language.current { + switch AppLanguage.current { case .russian: return "EEEE d MMMM, yyyy" default: @@ -182,7 +182,7 @@ extension String { } private var MMMM_d_yyyy_format: String { - switch Language.current { + switch AppLanguage.current { case .russian: return "d MMMM, yyyy" default: @@ -191,7 +191,7 @@ extension String { } private var EEEE_h_mm_a_format: String { - switch Language.current { + switch AppLanguage.current { case .russian: return "EEEE - HH:mm" default: @@ -200,7 +200,7 @@ extension String { } private var h_mm_a_format: String { - switch Language.current { + switch AppLanguage.current { case .russian: return "HH:mm" default: @@ -209,7 +209,7 @@ extension String { } private var hh_mm_a_format: String { - switch Language.current { + switch AppLanguage.current { case .russian: return "HH:mm" default: @@ -218,7 +218,7 @@ extension String { } private var MMM_dd_YYYY_at_hh_mm_a: String { - switch Language.current { + switch AppLanguage.current { case .russian: return "MMM dd, YYYY 'в' HH:mm" default: @@ -234,7 +234,7 @@ extension Bundle { //it creates different bundles //we take appropriate bundle according to language static var localizedBundle: Bundle { - let languageCode = Language.current.rawValue + let languageCode = AppLanguage.current.rawValue guard let path = Bundle.main.path(forResource: languageCode, ofType: "lproj") else { return Bundle.main } diff --git a/Nynja/Library/UI/Extensions/String/NSAttributedString+Bounds.swift b/Nynja/Library/UI/Extensions/String/NSAttributedString+Bounds.swift new file mode 100644 index 000000000..b48d4fddb --- /dev/null +++ b/Nynja/Library/UI/Extensions/String/NSAttributedString+Bounds.swift @@ -0,0 +1,18 @@ +// +// NSAttributedString+Bounds.swift +// Nynja +// +// Created by Anton Poltoratskyi on 22.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +extension NSAttributedString { + + func size(constrainedBy maxWidth: CGFloat, options: NSStringDrawingOptions = .usesLineFragmentOrigin) -> CGSize { + let maxSize = CGSize(width: maxWidth, height: .greatestFiniteMagnitude) + let rect = self.boundingRect(with: maxSize, options: options, context: nil) + return CGSize(width: ceil(rect.width), height: ceil(rect.height)) + } +} diff --git a/Nynja/Library/UI/Extensions/StringExtensions.swift b/Nynja/Library/UI/Extensions/String/StringExtensions.swift similarity index 89% rename from Nynja/Library/UI/Extensions/StringExtensions.swift rename to Nynja/Library/UI/Extensions/String/StringExtensions.swift index 4d3649429..f936adddb 100644 --- a/Nynja/Library/UI/Extensions/StringExtensions.swift +++ b/Nynja/Library/UI/Extensions/String/StringExtensions.swift @@ -190,18 +190,19 @@ extension String { // MARK: - Size + func size(constrainedBy maxWidth: CGFloat, font: UIFont, options: NSStringDrawingOptions = .usesLineFragmentOrigin) -> CGSize { + let maxSize = CGSize(width: maxWidth, height: .greatestFiniteMagnitude) + let rect = self.boundingRect(with: maxSize, options: options, attributes: [.font: font], context: nil) + return CGSize(width: ceil(rect.width), height: ceil(rect.height)) + } + func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat { - let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude) - let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil) - - return ceil(boundingBox.height) + return size(constrainedBy: width, font: font).height } - func width(withConstraintedHeight height: CGFloat, font: UIFont) -> CGFloat { - let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height) - let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil) - - return ceil(boundingBox.width) + func width(for font: UIFont, options: NSStringDrawingOptions = .usesLineFragmentOrigin) -> CGFloat { + let maxSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) + return self.boundingRect(with: maxSize, options: options, attributes: [.font: font], context: nil).width } func width(for fontName: String, height: CGFloat) -> CGFloat { @@ -349,6 +350,12 @@ extension String { return self.trimmingCharacters(in: .whitespacesAndNewlines) } + var trimmedTranslation: String { + return self.trimmed() + .replacingOccurrences(of: "\n", with: "") + .replacingOccurrences(of: " ", with: "") + } + } extension String { @@ -363,3 +370,12 @@ extension String { } } +extension String { + func capitalizingFirstLetter() -> String { + return prefix(1).capitalized + dropFirst() + } + + mutating func capitalizeFirstLetter() { + self = self.capitalizingFirstLetter() + } +} diff --git a/Nynja/Library/UI/Extensions/UI/UIImageView/UIImageView+SetImage.swift b/Nynja/Library/UI/Extensions/UI/UIImageView/UIImageView+SetImage.swift index 05f2852dd..a0450c5e5 100644 --- a/Nynja/Library/UI/Extensions/UI/UIImageView/UIImageView+SetImage.swift +++ b/Nynja/Library/UI/Extensions/UI/UIImageView/UIImageView+SetImage.swift @@ -11,9 +11,34 @@ import SDWebImage extension UIImageView { - typealias ImageCompletion = (URL?, UIImage?, Error?) -> Void + typealias DownloadCompletion = (URL?, UIImage?, Error?) -> Void - func setImage(url: URL?, placeHolder: UIImage? = nil, accessibilityPrefix: String? = nil, completion: ImageCompletion? = nil) { + struct DownloadOptions: OptionSet { + let rawValue: Int + + init(rawValue: Int) { + self.rawValue = rawValue + } + + static let delayPlaceholder = DownloadOptions(rawValue: 1 << 0) + + fileprivate var sdWebImageOptions: SDWebImageOptions { + var options: SDWebImageOptions = [] + + if contains(.delayPlaceholder) { + options.formUnion(.delayPlaceholder) + } + + return options + } + } + + func setImage(url: URL?, + placeHolder: UIImage? = nil, + options: DownloadOptions = [], + accessibilityPrefix: String? = nil, + completion: DownloadCompletion? = nil) { + setInitialAccesibilityIdentifier(accessibilityPrefix: accessibilityPrefix) if let localURL = url, localURL.isLocalURL { @@ -23,7 +48,12 @@ extension UIImageView { completion?(url, image, nil) } else { - sd_setImage(with: url, placeholderImage: placeHolder, options: []) { [weak self] image, error, cache, url in + if options.contains(.delayPlaceholder), image == nil { + image = placeHolder + } + sd_setImage(with: url, placeholderImage: placeHolder, options: options.sdWebImageOptions) { + [weak self] image, error, cache, url in + if error == nil, image != nil { self?.setLoadedAccesibilityIdentifier(accessibilityPrefix: accessibilityPrefix) } diff --git a/Nynja/Library/UI/KeyboardLayoutGuide/KeyboardLayoutGuide.swift b/Nynja/Library/UI/KeyboardLayoutGuide/KeyboardLayoutGuide.swift index 87da1c305..b80c49b9e 100644 --- a/Nynja/Library/UI/KeyboardLayoutGuide/KeyboardLayoutGuide.swift +++ b/Nynja/Library/UI/KeyboardLayoutGuide/KeyboardLayoutGuide.swift @@ -90,12 +90,29 @@ final class KeyboardLayoutGuide: UILayoutGuide { height -= owningView.safeAreaInsets.bottom } if let constraint = heightConstraint, case let oldHeight = constraint.constant, oldHeight != height { - animate(notification) { constraint.constant = height } + animate { constraint.constant = height } } Keyboard.shared.currentHeight = height } - private func animate(_ notification: Notification, layout: () -> Void) { + +} + + +// MARK: - Helpers + +extension UILayoutGuide { + var heightConstraint: NSLayoutConstraint? { + guard let target = owningView else { return nil } + for c in target.constraints { + if let fi = c.firstItem as? UILayoutGuide, fi == self && c.firstAttribute == .height { + return c + } + } + return nil + } + + func animate(layout: () -> Void) { guard let owningView = owningView else { return } @@ -114,21 +131,6 @@ final class KeyboardLayoutGuide: UILayoutGuide { } } - -// MARK: - Helpers - -private extension UILayoutGuide { - var heightConstraint: NSLayoutConstraint? { - guard let target = owningView else { return nil } - for c in target.constraints { - if let fi = c.firstItem as? UILayoutGuide, fi == self && c.firstAttribute == .height { - return c - } - } - return nil - } -} - private extension Notification { var keyboardHeight: CGFloat? { guard let v = userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return nil } @@ -139,7 +141,7 @@ private extension Notification { } } -private extension UIView { +extension UIView { func isVisible() -> Bool { func isVisible(inView: UIView?) -> Bool { guard let inView = inView else { diff --git a/Nynja/Library/UI/SafeArea/UIViewController+SafeArea.swift b/Nynja/Library/UI/SafeArea/UIViewController+SafeArea.swift index bff0623ad..6bf8539ef 100644 --- a/Nynja/Library/UI/SafeArea/UIViewController+SafeArea.swift +++ b/Nynja/Library/UI/SafeArea/UIViewController+SafeArea.swift @@ -63,7 +63,7 @@ extension UIViewController { func updateToShow(view: UIView, offset: CGFloat) { view.snp.updateConstraints { make in - adjustVerticalInset(.bottom, make: make, offset: offset + safeAreaBottomInset()) + adjustVerticalInset(.bottom, make: make, offset: offset + safeAreaBottomInset) } } @@ -73,7 +73,7 @@ extension UIViewController { } } - func safeAreaBottomInset() -> CGFloat { + var safeAreaBottomInset: CGFloat { var safeAreaBottomInset: CGFloat = 0 if #available(iOS 11.0, *) { if let bottom = self.navigationController?.view.safeAreaInsets.bottom { diff --git a/Nynja/Library/UI/SnackBar/SnackBar.swift b/Nynja/Library/UI/SnackBar/SnackBar.swift new file mode 100644 index 000000000..441d5b68a --- /dev/null +++ b/Nynja/Library/UI/SnackBar/SnackBar.swift @@ -0,0 +1,224 @@ +// +// SnackBar.swift +// Nynja +// +// Created by Andrey Reznik on 22.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation +import MulticastDelegateSwift + +protocol SnackBarLayoutDelegate: class { + func snackBar(_ snackBar: SnackBar, + didChangeHeight height: CGFloat, + animationDuration: TimeInterval, + layout: (() -> Void)?) +} + +final class SnackBar: BaseView, InitializeConfigurable { + + let delegates = MulticastDelegate() + + override var activatedViews: [UIView] { + return [containerView] + } + + // MARK: - Views + + private lazy var containerView = makeContainerView() + private lazy var titleLabel = makeTitleLabel() + + private var currentHeight: CGFloat = 0 + + // MARK: - InitializeConfigurable + + struct Config { + let displayType: DisplayType + } + + convenience init() { + self.init(frame: .zero) + } + + convenience init(config: Config) { + self.init(frame: .zero) + titleLabel.text = config.displayType.text + } + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = UIColor.nynja.wheelBackHighlitedGray + alpha = 0.0 + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + // MARK: - + + func update(for displayState: DisplayState, + with config: Config? = nil, + animationDuration: TimeInterval = Animation.duration, + layout: (() -> Void)? = nil) { + + UIView.animate(withDuration: animationDuration) { + self.alpha = displayState.isHidden ? 0.0 : 1.0 + } + + switch displayState { + case .collapsed: + currentHeight = displayState.height + case .expanded: + guard let config = config else { + currentHeight = displayState.height + return + } + currentHeight = contentHeight(for: config.displayType.text) + titleLabel.text = config.displayType.text + containerView.snp.updateConstraints { maker in + maker.height.equalTo(currentHeight) + } + } + + delegates.invokeDelegates { + $0.snackBar(self, + didChangeHeight: currentHeight, + animationDuration: animationDuration, + layout: layout) + } + } + + func silentUpdate() { + delegates.invokeDelegates { + $0.snackBar(self, + didChangeHeight: currentHeight, + animationDuration: 0.0, + layout: nil) + } + } +} + + +// MARK: - Inner types + +extension SnackBar { + + enum DisplayType { + case text(String) + + var text: String { + switch self { + case .text(let text): + return text + } + } + } + + enum DisplayState { + case collapsed + case expanded + + var height: CGFloat { + switch self { + case .collapsed: + return CGFloat(0.0) + case .expanded: + return Constraints.containerView.height + } + } + + var isHidden: Bool { + switch self { + case .collapsed: + return true + case .expanded: + return false + } + } + } +} + + +// MARK: - Subviews maker + +private extension SnackBar { + + func makeContainerView() -> UIView { + let view = UIView() + view.backgroundColor = .clear + addSubview(view) + + view.snp.makeConstraints { maker in + maker.left.right.top.equalToSuperview() + maker.height.equalTo(Constraints.containerView.height) + } + return view + } + + func makeTitleLabel() -> UILabel { + let view = UILabel() + view.adjustsFontSizeToFitWidth = true + view.font = Font.titleFont + view.textColor = .white + view.numberOfLines = 0 + + containerView.addSubview(view) + + view.snp.makeConstraints { maker in + maker.left.equalTo(containerView.snp.left).offset(Constraints.titleLabel.horisontalInset) + maker.right.equalTo(containerView.snp.right).offset(-Constraints.titleLabel.horisontalInset) + maker.centerY.equalToSuperview() + } + + return view + } +} + + +// MARK: - Helper + +private extension SnackBar { + + func contentHeight(for text: String) -> CGFloat { + return text.height( + withConstrainedWidth: Constraints.contentWidth, + font: Font.titleFont) + 2 * Constraints.titleLabel.verticalInset + } +} + + +// MARK: - Constraints + +extension SnackBar { + enum Font { + static let titleFont = UIFont(name: FontFamily.NotoSans.medium.name, size: 16)! + } + + enum Display { + static let duration: TimeInterval = 4.0 + } + + enum Animation { + static let duration: TimeInterval = 0.5 + static let zero: TimeInterval = 0.0 + } + + enum Constraints { + + static let contentWidth = UIScreen.main.bounds.width - 2 * titleLabel.horisontalInset + + enum containerView { + static let height: CGFloat = CGFloat(45.0.adjustedByWidth) + } + + enum titleLabel { + static let verticalInset: CGFloat = CGFloat(8.0.adjustedByWidth) + static let horisontalInset: CGFloat = CGFloat(16.0.adjustedByWidth) + } + } +} + + diff --git a/Nynja/Library/UI/SnackBar/SnackBarLayoutGuide.swift b/Nynja/Library/UI/SnackBar/SnackBarLayoutGuide.swift new file mode 100644 index 000000000..9cedbf850 --- /dev/null +++ b/Nynja/Library/UI/SnackBar/SnackBarLayoutGuide.swift @@ -0,0 +1,72 @@ +// +// SnackBarLayoutGuide.swift +// Nynja +// +// Created by Andrey Reznik on 22.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import UIKit + +extension UIView { + + private struct AssociatedKeys { + static var snackBarLayoutGuide = "snackBarLayoutGuide" + } + + /// A layout guide representing the inset for the keyboard. + /// Use this layout guide’s top anchor to create constraints pinning to the top of the keyboard. + var snackBarLayoutGuide: SnackBarLayoutGuide { + if let obj = objc_getAssociatedObject(self, &AssociatedKeys.snackBarLayoutGuide) as? SnackBarLayoutGuide { + return obj + } + let guide = SnackBarLayoutGuide() + addLayoutGuide(guide) + guide.setup() + objc_setAssociatedObject(self, &AssociatedKeys.snackBarLayoutGuide, guide as Any, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return guide + } +} + +final class SnackBarLayoutGuide: UILayoutGuide { + + private(set) var height: CGFloat = 0 + + // MARK: - Setup + + fileprivate func setup() { + guard let view = owningView else { + return + } + NSLayoutConstraint.activate([ + heightAnchor.constraint(equalToConstant: height), + leftAnchor.constraint(equalTo: view.leftAnchor), + rightAnchor.constraint(equalTo: view.rightAnchor), + ] + ) + let viewBottomAnchor: NSLayoutYAxisAnchor = view.keyboardLayoutGuide.topAnchor + bottomAnchor.constraint(equalTo: viewBottomAnchor).isActive = true + } + + fileprivate func update(height: CGFloat, animationDuration: TimeInterval, layout: (() -> Void)?) { + self.height = height + if let constraint = heightConstraint, case let oldHeight = constraint.constant, oldHeight != height { + UIView.animate(withDuration: animationDuration) { + self.animate { + layout?() + constraint.constant = height + } + } + } + } +} + +extension SnackBarLayoutGuide: SnackBarLayoutDelegate { + + func snackBar(_ snackBar: SnackBar, + didChangeHeight height: CGFloat, + animationDuration: TimeInterval, + layout: (() -> Void)?) { + update(height: height, animationDuration: animationDuration, layout: layout) + } +} diff --git a/Nynja/Library/UI/StarDateConverter.swift b/Nynja/Library/UI/StarDateConverter.swift index 9654b03aa..bb6f08d58 100644 --- a/Nynja/Library/UI/StarDateConverter.swift +++ b/Nynja/Library/UI/StarDateConverter.swift @@ -28,7 +28,7 @@ class StarDateConverter { } static func toString(_ date: Date) -> String { - let currentLocale = Locale(identifier: Language.current.rawValue) + let currentLocale = Locale(identifier: AppLanguage.current.rawValue) updateLocale(currentLocale) let hoursAgo = Date().hours(from: date) diff --git a/Nynja/Library/UI/UITableViewCells/ChatSettingCell/AvatarCell.swift b/Nynja/Library/UI/UITableViewCells/ChatSettingCell/AvatarCell.swift index 9d1b57b17..a68742ac7 100644 --- a/Nynja/Library/UI/UITableViewCells/ChatSettingCell/AvatarCell.swift +++ b/Nynja/Library/UI/UITableViewCells/ChatSettingCell/AvatarCell.swift @@ -103,7 +103,8 @@ class AvatarCell: BaseSettingCell, ConfigurableCell { aliasBlockView.textLabel.text = cellViewModel.alias if let url = cellViewModel.url { - avatar.setImage(url: url, placeHolder: UIImage.nynja.Contacts.avaPlaceholder.image) + let placeholder = avatar.image ?? UIImage.nynja.Contacts.avaPlaceholder.image + avatar.setImage(url: url, placeHolder: placeholder, options: .delayPlaceholder) } else { avatar.image = UIImage.nynja.Contacts.avaPlaceholder.image } diff --git a/Nynja/Library/UI/View/CollapsedView/CollapsedView.swift b/Nynja/Library/UI/View/CollapsedView/CollapsedView.swift index 48167d76e..7f6573ad5 100644 --- a/Nynja/Library/UI/View/CollapsedView/CollapsedView.swift +++ b/Nynja/Library/UI/View/CollapsedView/CollapsedView.swift @@ -49,6 +49,15 @@ final class CollapsedView: UIView, Configurable { //MARK: - States + private var isLargeContent: Bool = false { + didSet { + guard oldValue != isLargeContent else { + return + } + updateMiddleHeight(isSilent: false) + } + } + private var height: CGFloat { get { return bounds.size.height @@ -181,15 +190,24 @@ extension CollapsedView { } struct ConstraintLayout { - let availableHeight: [CGFloat] + var availableHeight: [CGFloat] var collapsedHeight: CGFloat { return availableHeight.first ?? 0 } var middleHeight: CGFloat? { - return availableHeight.count > 2 ? availableHeight[1] : nil + set { + guard availableHeight.count > 2, let height = newValue else { + return + } + availableHeight[1] = height + } + get { + return availableHeight.count > 2 ? availableHeight[1] : nil + } } + var expandedHeight: CGFloat { return availableHeight.last ?? 0 } @@ -250,18 +268,35 @@ extension CollapsedView { func update(constraintLayout: ConstraintLayout) { self.constraintLayout = constraintLayout + updateMiddleHeight(isSilent: true) + switch viewState { case .expanded: self.height = self.constraintLayout.expandedHeight - UIView.animate(withDuration: TimeInterval(animationsDuration.expandedDuration), animations: { + UIView.animate(withDuration: TimeInterval(animationsDuration.expandedDuration)) { self.layoutIfNeeded() - }) + } case .middle: break default: break } } + + private func updateMiddleHeight(isSilent: Bool) { + let middleHeight = isLargeContent + ? FooterView.middleHeightLargeContent + : FooterView.middleHeight + constraintLayout.middleHeight = middleHeight + + if !isSilent && viewState == .middle { + height = middleHeight + UIView.animate(withDuration: TimeInterval(animationsDuration.expandedDuration)) { + self.layoutIfNeeded() + } + } + + } } //MARK: - Button action @@ -450,6 +485,20 @@ private extension CollapsedView { } } +//MARK: - Layout + +extension CollapsedView { + enum FooterView { + static let collapsedHeight: CGFloat = 30 + static let middleHeight: CGFloat = 90 + static let middleHeightLargeContent: CGFloat = 130 + static let expandedHeight: CGFloat = 300 + + static let arrowHeight = 30 + static let arrowWidth = 44 + } +} + //MARK: - UI configuration private extension CollapsedView { @@ -524,4 +573,13 @@ extension CollapsedView: TestableViewProtocol { } } - +extension CollapsedView: TranslationManualViewDelegate { + func didChange(displayingState: TranslationManualView.DisplayingState, + with contentSize: CGSize) { + guard case .translated = displayingState else { + isLargeContent = false + return + } + isLargeContent = (contentSize.height + 1.5 * FooterView.collapsedHeight) > FooterView.middleHeight + } +} diff --git a/Nynja/Library/UI/View/CollapsedView/Translation/TranslationAutoView.swift b/Nynja/Library/UI/View/CollapsedView/Translation/TranslationAutoView.swift index d218c273d..c2ad1e7ae 100644 --- a/Nynja/Library/UI/View/CollapsedView/Translation/TranslationAutoView.swift +++ b/Nynja/Library/UI/View/CollapsedView/Translation/TranslationAutoView.swift @@ -22,13 +22,13 @@ final class TranslationAutoView: UIView, Configurable { //MARK: - States - private var selectedLang: SelectedLang = .none { + private var selectedLang: Language! { didSet { currentLang = selectedLang } } - private var currentLang: SelectedLang = .none { + private var currentLang: Language! { didSet { setupLanguageButton(currentLang) } @@ -45,7 +45,7 @@ final class TranslationAutoView: UIView, Configurable { //MARK: - InnerTypes extension TranslationAutoView { - typealias LangHandler = (SelectedLang) -> Void + typealias LangHandler = (Language) -> Void } @@ -53,7 +53,7 @@ extension TranslationAutoView { extension TranslationAutoView { struct Config { - let selectedLang: SelectedLang + let selectedLang: Language let languageHandler: LangHandler } @@ -69,11 +69,11 @@ extension TranslationAutoView { } - func update(selectedLang: SelectedLang) { + func update(selectedLang: Language) { self.selectedLang = selectedLang } - func update(currentLang: SelectedLang) { + func update(currentLang: Language) { self.currentLang = currentLang } } @@ -81,7 +81,7 @@ extension TranslationAutoView { //MARK: - TranslationViewProtocol extension TranslationAutoView: TranslationViewProtocol { - func silentUpdate(with language: SelectedLang) { + func silentUpdate(with language: Language) { if language != selectedLang { update(selectedLang: language) } @@ -103,11 +103,8 @@ private extension TranslationAutoView { //MARK: - Helper private extension TranslationAutoView { - private func setupLanguageButton(_ selectedLang: SelectedLang) { - guard case let .lang(lang) = selectedLang else { - return - } - languageButton.setTitle(lang.language.uppercased(), for: .normal) + private func setupLanguageButton(_ selectedLang: Language) { + languageButton.setTitle(selectedLang.language.uppercased(), for: .normal) } } diff --git a/Nynja/Library/UI/View/CollapsedView/Translation/TranslationManualView.swift b/Nynja/Library/UI/View/CollapsedView/Translation/TranslationManualView.swift index a095e9245..9e4aa65ff 100644 --- a/Nynja/Library/UI/View/CollapsedView/Translation/TranslationManualView.swift +++ b/Nynja/Library/UI/View/CollapsedView/Translation/TranslationManualView.swift @@ -9,9 +9,15 @@ import UIKit import SnapKit +protocol TranslationManualViewDelegate: class { + func didChange(displayingState: TranslationManualView.DisplayingState, + with contentSize: CGSize) +} + final class TranslationManualView: UIView, Configurable { //MARK: - UI properties + private var translationLabel: UILabel! private var outdatedLabel: UILabel! @@ -21,17 +27,21 @@ final class TranslationManualView: UIView, Configurable { private var translationButton: UIButton! private var languageButton: UIButton! + //MARK: - Delegate + weak var delegate: TranslationManualViewDelegate? + + //MARK: - States - private var selectedLang: SelectedLang = .none { + private var selectedLang: Language! { didSet { currentLang = selectedLang } } - private var currentLang: SelectedLang = .none { + private var currentLang: Language! { didSet { setupLanguageButton(currentLang) } @@ -79,9 +89,9 @@ final class TranslationManualView: UIView, Configurable { //MARK: - InnerTypes extension TranslationManualView { - typealias LangHandler = (SelectedLang) -> Void + typealias LangHandler = (Language) -> Void - typealias TranslationHandler = (String, SelectedLang) -> Void + typealias TranslationHandler = (String, Language) -> Void typealias DisplayingHandler = (PandingState) -> Void enum ButtonType { @@ -148,7 +158,7 @@ extension TranslationManualView { case .translated(_): return .white default: - return UIColor.nynja.mainRed + return UIColor.nynja.manatee } } @@ -184,7 +194,7 @@ extension TranslationManualView { struct TranlationResult: Equatable { let originalText: String let translatedText: String - let translatedLanguage: SelectedLang + let translatedLanguage: Language let mentions: [Mention] static func == (lhs: TranslationManualView.TranlationResult, rhs: TranslationManualView.TranlationResult) -> Bool { @@ -199,7 +209,7 @@ extension TranslationManualView { struct Config { let initialState: DisplayingState - let selectedLang: SelectedLang + let selectedLang: Language let languageHandler: LangHandler let translationHandler: TranslationHandler @@ -245,11 +255,11 @@ extension TranslationManualView { pandingState = PandingState(state) } - func update(selectedLang: SelectedLang) { + func update(selectedLang: Language) { self.selectedLang = selectedLang } - func update(currentLang: SelectedLang) { + func update(currentLang: Language) { self.currentLang = currentLang } @@ -261,7 +271,7 @@ extension TranslationManualView { //MARK: - TranslationViewProtocol extension TranslationManualView: TranslationViewProtocol { - func silentUpdate(with language: SelectedLang) { + func silentUpdate(with language: Language) { if language != selectedLang { update(selectedLang: language) } @@ -299,6 +309,8 @@ private extension TranslationManualView { private func setupContent(_ state: DisplayingState) { setupTextView(state) setupTranslationButton(state) + + delegate?.didChange(displayingState: state, with: textView.contentSize) } private func setupTextView(_ state: DisplayingState) { @@ -312,11 +324,8 @@ private extension TranslationManualView { translationButton.setImage(ButtonType(state).image, for: .normal) } - private func setupLanguageButton(_ selectedLang: SelectedLang) { - guard case let .lang(lang) = selectedLang else { - return - } - languageButton.setTitle(lang.language.uppercased(), for: .normal) + private func setupLanguageButton(_ selectedLang: Language) { + languageButton.setTitle(selectedLang.language.uppercased(), for: .normal) } private func setupOutdatedLabel(_ pendingState: PandingState) { diff --git a/Nynja/Library/UI/View/CollapsedView/Translation/TranslationViewProtocol.swift b/Nynja/Library/UI/View/CollapsedView/Translation/TranslationViewProtocol.swift index 0c58f48ca..6e7fca981 100644 --- a/Nynja/Library/UI/View/CollapsedView/Translation/TranslationViewProtocol.swift +++ b/Nynja/Library/UI/View/CollapsedView/Translation/TranslationViewProtocol.swift @@ -14,7 +14,7 @@ protocol TranslationViewProtocol: class { func update(state: TranslationManualView.DisplayingState) - func silentUpdate(with language: SelectedLang) + func silentUpdate(with language: Language) func resetToDefault() } diff --git a/Nynja/Library/UI/WheelContainer/WheelContainer.swift b/Nynja/Library/UI/WheelContainer/WheelContainer.swift index e67bffa3d..8adae1fb5 100644 --- a/Nynja/Library/UI/WheelContainer/WheelContainer.swift +++ b/Nynja/Library/UI/WheelContainer/WheelContainer.swift @@ -337,12 +337,12 @@ class WheelContainer: UIView, UserSettingsRespondable { if let wheel = self.wheel(for: level) { wheel.reloadData() - restoreSelectionIfNeeded(for: level, wheel: wheel) + restoreScrollingPositionIfNeeded(for: level, wheel: wheel) } } } - private func restoreSelectionIfNeeded(for level: Int, wheel: Wheel) { + private func restoreScrollingPositionIfNeeded(for level: Int, wheel: Wheel) { if shouldRestoreState { scrollWheel(wheel, at: level, to: self.currentScrollingPath) } diff --git a/Nynja/LogService/LogService.swift b/Nynja/LogService/LogService.swift deleted file mode 100644 index fc96a8b5a..000000000 --- a/Nynja/LogService/LogService.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// LogService.swift -// Nynja -// -// Created by Anton M on 16.07.2018. -// Copyright © 2018 TecSynt Solutions. All rights reserved. -// - -import Foundation -import os.log - -class LogService { - - static var enable:[LogServiceTopic] = LogService.allValues - - enum LogServiceTopic: String { - case wallet = "Wallet" - case keychain = "Keychain" - case fileSystem = "File System" - case db = "Database" - case audioSystem = "Audo System" - case MQTT = "MQTT" - case amazon = "Amazon" - case callSystem = "Call System" - case locationSystem = "Location System" - case system = "Nynja System" - case network = "Network" - case galery = "Galery" - case videoConverter = "Video Converter" - case QRCode = "QRCode" - case passphrase = "Passphrase" - case push = "Push notification" - case userDefaults = "User Defaults" - case arc = "ARC" - } - - static let allValues: [LogServiceTopic] = [.userDefaults, .wallet, .keychain, .fileSystem, .db, - .audioSystem, .MQTT, .amazon, .callSystem, .locationSystem, - .system, .network, .galery, .videoConverter, .QRCode, - .passphrase, .arc] - - static var allValuesStrings: String { - return allValues.reduce("") { (result, topic) -> String in - return "\(result)\(topic.rawValue),\n" - } - } - - static func log(topic: LogServiceTopic, block: () -> String) { - #if !RELEASE - if !enable.contains(topic) { return } - LogService.executeLogs(topic: topic, text: block(), thread: Thread.current.debugDescription) - #endif - } - - private static func executeLogs(topic: LogServiceTopic, text: String, thread: String) { - LogWriter.shared?.writeLog(topic: topic.rawValue, description: text, thread: thread) - printToLog(topic: topic, text: text, thread: thread) - } - - private static func printToLog(topic: LogServiceTopic, text: String, thread: String) { - var i = 0 - let text = "\(thread), \(text)" - text.split(by: 30000).forEach { (part) in - let log = OSLog(subsystem: Bundle.main.bundleIdentifier, category: topic.rawValue) - if i != 0 { - os_log("%{public}@", log: log, type: .default, ">>>>> \(i):\n \(part)") - } else { - os_log("%{public}@", log: log, type: .default, "\(part)") - } - i += 1 - } - } -} diff --git a/Nynja/LogService/LogWriter.swift b/Nynja/LogService/LogWriter.swift deleted file mode 100644 index 1a1453996..000000000 --- a/Nynja/LogService/LogWriter.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// LogWriter.swift -// Nynja -// -// Created by Anton M on 15.08.2018. -// Copyright © 2018 TecSynt Solutions. All rights reserved. -// - -import Foundation - -class LogWriter { - - static let shared: LogWriter? = LogWriter() - - private var handle: FileHandle! - - private init?() { - let fileName = LogWriter.getCurrentDateAndTime() + ".log" - FileManagerService.sharedInstance.createDirectory(dirName: "Logs") - - guard let path = FileManagerService.sharedInstance.createFile(folder: "Logs", name: fileName), - FileManagerService.sharedInstance.fileManager.createFile(atPath: path, contents: nil, attributes: nil), - let url = URL(string: path) else { return nil } - - do { handle = try FileHandle(forUpdating: url) } - catch { - return nil - } - } - - private static func getCurrentDateAndTime() -> String { - let formatter = DateFormatter() - formatter.dateFormat = "yyyy_MM_dd_HH_mm_ss" - return formatter.string(from: Date()) - } - - func writeLog(topic: String, description: String, thread: String) { - guard let _log = Log(topic: topic, description: description, thread: thread), let data = _log.data else { return } - handle.seekToEndOfFile() - handle.write(data) - } - - func closeFile() { - handle.closeFile() - } - - private struct Log { - private var _bytes: [UInt8] - - init?(topic: String, description: String, thread: String) { - let title = BertAtom(fromString: "Log") - let _topic = topic.getBin() - let _description = description.getBin() - let timestamp = Int64(Date().timeIntervalSince1970.seconds) - let _timeStamp = BertNumber(fromInt64: timestamp) - let _thread = thread.getBin() - let bert = BertTuple(fromElements: [title, _topic, _description, _timeStamp, _thread]) - do { - let bytes = try Bert.encode(object: bert) - _bytes = [UInt8](repeating: 0, count: bytes.length) - bytes.getBytes(&_bytes, length: bytes.length) - } catch { - return nil - } - } - - var data: Data? { - get { - guard let bytesString = _bytes.toBase64() else { return nil } - return (bytesString + ";").data(using: String.Encoding.ascii) - } - } - } -} - - diff --git a/Nynja/MessageDAO.swift b/Nynja/MessageDAO.swift index 71678f186..7ef0c417e 100644 --- a/Nynja/MessageDAO.swift +++ b/Nynja/MessageDAO.swift @@ -217,6 +217,13 @@ final class MessageDAO: MessageDAOProtocol { } } + static func localStatus(message: Message) throws -> Message.LocalStatus? { + guard let serverId = message.id, + let message = try MessageDAO.rawMessage(serverId: serverId) else { + return nil + } + return message.localStatus + } // MARK: - Clear History diff --git a/Nynja/MessagesProcessingManager.swift b/Nynja/MessagesProcessingManager.swift index f26059f16..198a2e1f4 100644 --- a/Nynja/MessagesProcessingManager.swift +++ b/Nynja/MessagesProcessingManager.swift @@ -9,7 +9,7 @@ typealias MessageUploadCompletion = () -> Void protocol MessageProcessingDelegate : class { - func updateProgress(_ progress:ProgressModel) + func update(progress: ProgressModel) } protocol MessageProcessingManagerInterface: class { diff --git a/Nynja/MigrationManager.swift b/Nynja/MigrationManager.swift index a89c87bd7..940a922b4 100644 --- a/Nynja/MigrationManager.swift +++ b/Nynja/MigrationManager.swift @@ -11,6 +11,7 @@ import GRDBCipher enum Migration: Int, Describable { case addSeenByColumnToMessage = 0 case updateDBFeatureTargetTypes + case addAutoColumnToConvertMessage static var allTitles: [String] = { var i = 0 @@ -72,6 +73,12 @@ final class MigrationManager { try db.execute(sql, arguments: [DBFeature.TargetType.message.rawValue, "desc"]) try db.execute(sql, arguments: [DBFeature.TargetType.job.rawValue, "schedule"]) } + + migrator.registerMigration(.addAutoColumnToConvertMessage) { db in + try ConvertMessageTable.alter(in: db) { t in + t.add(column: ConvertMessageTable.Column.auto, .boolean) + } + } } } diff --git a/Nynja/Modules/AddParticipants/Presenter/AddParticipantsPresenter.swift b/Nynja/Modules/AddParticipants/Presenter/AddParticipantsPresenter.swift index 1b1aadbfd..c44e9ae03 100644 --- a/Nynja/Modules/AddParticipants/Presenter/AddParticipantsPresenter.swift +++ b/Nynja/Modules/AddParticipants/Presenter/AddParticipantsPresenter.swift @@ -11,15 +11,15 @@ class AddParticipantsPresenter: BasePresenter, AddParticipantsPresenterProtocol, var room: Room? var roomToCreate = Room() var contacts: [Contact]! - + private var shouldDiscartAllocatedConference: Bool = true - + deinit { if shouldDiscartAllocatedConference { interactor.discardAllocatedConference() } } - + override var itemsFactory: WCItemsFactory? { if participantsMode == .create { return CreateGroupItemsFactory() @@ -30,7 +30,7 @@ class AddParticipantsPresenter: BasePresenter, AddParticipantsPresenterProtocol, override func screenLoaded() { super.screenLoaded() - + switch self.participantsMode! { case .createGroupCall, .createConferenceCall: interactor.allocateConference() @@ -60,30 +60,36 @@ class AddParticipantsPresenter: BasePresenter, AddParticipantsPresenterProtocol, for i in contacts { members.append(Member(contact: i)) } - + let myContact = interactor.getMySelf() - + if let mc = myContact { - + let myName = "\(mc.names ?? "") \(mc.surnames ?? "")" roomToCreate.name = "\(myName) and \(members.count) other(s)" - + // add my self let myMember = Member(contact: mc) myMember.status = "admin" as AnyObject members.append(myMember) } - + roomToCreate.members = members - - (wireFrame as? AddParticipantsWireFrame)?.main?.view?.showSpinner() - if !interactor.createRoom(name: roomToCreate.name!, avatar: nil, members: roomToCreate.members!) { - (wireFrame as? AddParticipantsWireFrame)?.main?.view?.hideSpinner() + + if members.count > 1 { + (wireFrame as? AddParticipantsWireFrame)?.main?.view?.showSpinner() + if !interactor.createRoom(name: roomToCreate.name!, avatar: nil, members: roomToCreate.members!) { + (wireFrame as? AddParticipantsWireFrame)?.main?.view?.hideSpinner() + } + } else if let _ = myContact { + wireFrame.hide(with: .createConferenceCall(callId: interactor.allocateId, contacts: [], room:nil)) + } else { + wireFrame.hide(with: .cancel) } } - + // MARK: - AddParticipantsPresenterProtocol - + func filter(with text: String) { switch self.participantsMode! { case .create: @@ -98,7 +104,7 @@ class AddParticipantsPresenter: BasePresenter, AddParticipantsPresenterProtocol, return interactor.filterParticipantsByAlias(with: text) } } - + func hide(with contacts: [Contact]){ switch self.participantsMode! { case .create: @@ -128,20 +134,12 @@ class AddParticipantsPresenter: BasePresenter, AddParticipantsPresenterProtocol, } case .createGroupCall: shouldDiscartAllocatedConference = false - if contacts.isEmpty { - interactor.discardAllocatedConference() - wireFrame.hide(with: .cancel) - } else { - wireFrame.hide(with: .createGroupCall(callId: interactor.allocateId, contacts: contacts, room: room)) - } + wireFrame.hide(with: .createGroupCall(callId: interactor.allocateId, contacts: contacts, room: room)) + case .createConferenceCall: shouldDiscartAllocatedConference = false - if contacts.isEmpty { - interactor.discardAllocatedConference() - wireFrame.hide(with: .cancel) - } else { - wireFrame.hide(with: .createConferenceCall(callId: interactor.allocateId, contacts: contacts, room:roomToCreate)) - } + wireFrame.hide(with: .createConferenceCall(callId: interactor.allocateId, contacts: contacts, room:roomToCreate)) + case .updateGroupCall: shouldDiscartAllocatedConference = false if contacts.isEmpty { @@ -152,11 +150,11 @@ class AddParticipantsPresenter: BasePresenter, AddParticipantsPresenterProtocol, } } } - + // MARK: NavigationProtocol func back() { - let arr:[Contact] = [] - hide(with: arr) + interactor.discardAllocatedConference() + wireFrame.hide(with: .cancel) } // MARK: - AddParticipantsInteractorOutputProtocol @@ -168,7 +166,7 @@ class AddParticipantsPresenter: BasePresenter, AddParticipantsPresenterProtocol, func participantsFiltered(_ participants: [Participant]) { view.updateParticipantsList(grouped(participants)) } - + func created(room: Room) { (wireFrame as? AddParticipantsWireFrame)?.main?.view?.hideSpinner() self.roomToCreate = room @@ -184,7 +182,7 @@ class AddParticipantsPresenter: BasePresenter, AddParticipantsPresenterProtocol, hide(with: members.contacts) } - + private func grouped(_ participants: [Participant]) -> GroupedParticipants { switch self.participantsMode! { case .create: diff --git a/Nynja/Modules/AddParticipants/View/AddParticipantsViewController.swift b/Nynja/Modules/AddParticipants/View/AddParticipantsViewController.swift index 5b4aa9fa8..f4e940541 100644 --- a/Nynja/Modules/AddParticipants/View/AddParticipantsViewController.swift +++ b/Nynja/Modules/AddParticipants/View/AddParticipantsViewController.swift @@ -323,7 +323,7 @@ class AddParticipantsViewController: BaseVC, AddParticipantsViewProtocol, Keyboa private func updateDoneButtonState() { if presenter.participantsMode == .createGroupCall || presenter.participantsMode == .updateGroupCall || presenter.participantsMode == .createConferenceCall { - doneButton.isEnabled = (participantsDataSource.selectedParticipants.count > 0) + doneButton.isEnabled = true } } @@ -350,9 +350,7 @@ class AddParticipantsViewController: BaseVC, AddParticipantsViewProtocol, Keyboa @objc private func doneTapped(_ button: UIButton) { //TODO: IOS-87 self.view.endEditing(true) - if (presenter.participantsMode == .create || presenter.participantsMode == .createConferenceCall || presenter.participantsMode == .createGroupCall || - presenter.participantsMode == .updateGroupCall) - && participantsDataSource.selectedParticipants.isEmpty { + if presenter.participantsMode == .create && participantsDataSource.selectedParticipants.isEmpty { AlertManager.sharedInstance.showAlertOk(message: String.localizable.pleaseChooseAtLeastOneMember) return } diff --git a/Nynja/Modules/Auth/Interactor/AuthInteractor.swift b/Nynja/Modules/Auth/Interactor/AuthInteractor.swift deleted file mode 100644 index b9c4dc62e..000000000 --- a/Nynja/Modules/Auth/Interactor/AuthInteractor.swift +++ /dev/null @@ -1,166 +0,0 @@ -// -// AuthAuthInteractor.swift -// Nynja -// -// Created by Anton Makarov on 31/05/2017. -// Copyright © 2017 TecSynt Solutions. All rights reserved. -// - -class AuthInteractor: BaseInteractor, AuthInteractorInputProtocol, IoHandlerDelegate, MQTTServiceDelegate { - - override var subscribes: [SubscribeType]? { - return [.roster(nil)] - } - - weak var presenter: AuthInteractorOutputProtocol! - - private let mqtt = MQTTService.sharedInstance - - var phone = "" - - private var enteredCode: String? - - override init() { - super.init() - - IoHandler.delegate = self - let time = DispatchTime(uptimeNanoseconds: 200) - DispatchQueue.global.asyncAfter(deadline: time) { - MQTTService.sharedInstance.addSubscriber(self) - } - } - - func login(phone: String) { - self.phone = phone - mqtt.registration(number: phone) - } - - func checkPassword() { - guard let code = enteredCode else { return } - mqtt.checkSMS(code: code, phone: phone) - } - - func resendSMS(phone: String) { - mqtt.resendSMS(number: phone) - } - - func voiceCall() { - mqtt.voiceCall(phone: phone) - } - - func getSMSCode(result: ((String) -> ())?) { - #if !RELEASE - let path = "http://\(MQTTService.sharedInstance.host.url):8888/sessions?phone=\(phone)" - if let url = URL(string: path) { - var req = URLRequest(url: url) - - req.allHTTPHeaderFields = makeHTTPHeaderFields() - - let session = URLSession.shared - let task = session.dataTask(with: req, completionHandler: { (data, resp, err) in - if data == nil { - return - } - do { - if let todoJSON = try JSONSerialization.jsonObject(with: data!, options: []) as? [[String: Any]] { - for i in todoJSON { - let model = Modelka(input: i) - if model.smsCode != nil && model.devKey == MQTTService.sharedInstance.deviceId { - result?(model.smsCode!) - } - } - } - } catch { - - } - }) - task.resume() - } - #endif - } - - private func makeHTTPHeaderFields() -> [String: String]? { - let username = "nynja" - let password = "nynjaTS" - - guard let base64Encoded = "\(username):\(password)".base64Encoded() else { - return nil - } - - return [ - "Authorization": "Basic \(base64Encoded)" - ] - } - - // MARK: - IoHandlerDelegate - func sessionNotFound() { - mqtt.removeTokenAndLogin(number: phone) - } - - func smsAlreadySent() { - mqtt.resendSMS(number: phone) - } - - func invalidSMS() { - self.presenter.invalidCode() - } - - func smsSent() { - self.presenter.smsSent() - } - - func smsNotSent() { - AlertManager.sharedInstance.showAlertOk(message: "auth_something_went_wrong".localized) - } - - func notVerified() { - mqtt.resendSMS(number: phone) - } - - func wrongCode() { - AlertManager.sharedInstance.showAlertOk(message: "auth_sms_code_is_wrong".localized) - } - - func attempts_expired() { - AlertManager.sharedInstance.showAlertOk(message: "auth_attempts_expired".localized) - } - - func mismatchUserData() { - mqtt.removeTokenAndLogin(number: phone) - } - - func numberNotAllowed() { - self.presenter.numberNotAllowed() - } - - // MARK: - MQTTServiceDelegate - func mqttServiceDidReceiveWrongServerVersion() { - DispatchQueue.main.async { - AlertManager.sharedInstance.showAlertOk(message: "wrongVersion".localized) - } - } - - // MARK: - StorageSubscriber - override func update(with changes: [StorageChange], type: SubscribeType) { - guard case .roster = type, - let info = changes.first, - let dbRoster = info.entity as? DBRoster else { - return - } - - let roster = Roster(roster: dbRoster) - - if info.kind == .insert { - if (roster.names ?? "").isEmpty, let contact = roster.myContact { - presenter.registered(contact: contact) - } else { - presenter.logined() - } - } - } - - func saveEnteredPassword(code: String) { - self.enteredCode = code - } - -} diff --git a/Nynja/Modules/Call/CallInProgressProtocols.swift b/Nynja/Modules/Call/CallInProgressProtocols.swift index 383438e34..79dddaa95 100644 --- a/Nynja/Modules/Call/CallInProgressProtocols.swift +++ b/Nynja/Modules/Call/CallInProgressProtocols.swift @@ -41,6 +41,8 @@ protocol CallInProgressViewProtocol: class { func callConnecting() func callRinging() func callConnected() + func callFailed() + func callDisconnected() } protocol CallInProgressPresenterProtocol: class { @@ -83,7 +85,9 @@ protocol CallInProgressPresenterProtocol: class { func updateCallStatus() func fetchCallParticipants() func activeSpeakerText()->String + func groupCallSubject()->String func isCallConnected()->Bool + func joinLink()->String? } protocol CallInProgressInteractorOutputProtocol: class { @@ -96,6 +100,8 @@ protocol CallInProgressInteractorOutputProtocol: class { func callConnecting() func callRinging() func callConnected(withVideo: Bool) + func callFailed() + func callDisconnected() func setRingingWithoutVideo() func setRingingStatus() func remoteVideoStreamStopped() @@ -143,7 +149,9 @@ protocol CallInProgressInteractorInputProtocol: class { func updateCallStatus() func fetchCallParticipants() func activeSpeakerText()->String + func groupCallSubject()->String func isCallConnected()->Bool + func joinLink()->String? } protocol ManageCallInProgressParticipantsProtocol: class { diff --git a/Nynja/Modules/Call/CallScreens/CallInProgress/Interactor/CallInProgressInteractor.swift b/Nynja/Modules/Call/CallScreens/CallInProgress/Interactor/CallInProgressInteractor.swift index 5db244c99..a7091de2d 100644 --- a/Nynja/Modules/Call/CallScreens/CallInProgress/Interactor/CallInProgressInteractor.swift +++ b/Nynja/Modules/Call/CallScreens/CallInProgress/Interactor/CallInProgressInteractor.swift @@ -142,10 +142,22 @@ class CallInProgressInteractor: CallInProgressInteractorInputProtocol, NynjaCall return final } + func groupCallSubject()->String { + var final = "" + if let nc = self.nynCall { + final = nc.subject + } + return final + } + func isCallConnected() -> Bool { return self.nynCall?.isRunning ?? false } + func joinLink()->String? { + return self.nynCall?.joinLink + } + func endCall() { if let nc = self.nynCall { nc.end() @@ -288,32 +300,7 @@ class CallInProgressInteractor: CallInProgressInteractorInputProtocol, NynjaCall func updateCallStatus() { if let c = self.nynCall { - - switch c.callState - { - case NYNCallState.new: - break - case NYNCallState.readyToStart: - break; - case NYNCallState.ringing: - self.presenter.callRinging() - break - case NYNCallState.connecting: - self.presenter.callConnecting() - break - case NYNCallState.connected: - self.presenter.callConnected(withVideo: false) - self.startTimer() - break - case NYNCallState.failed: - break - case NYNCallState.disconnected: - break - case NYNCallState.closed: - break - case NYNCallState.count: - break - } + self.stateDidChange(call: c, state: c.callState) } } @@ -361,8 +348,12 @@ class CallInProgressInteractor: CallInProgressInteractorInputProtocol, NynjaCall self.startTimer() break case NYNCallState.failed: + self.stopTimer() + self.presenter.callFailed() break case NYNCallState.disconnected: + self.stopTimer() + self.presenter.callDisconnected() break case NYNCallState.closed: break diff --git a/Nynja/Modules/Call/CallScreens/CallInProgress/Presenter/CallInProgressPresenter.swift b/Nynja/Modules/Call/CallScreens/CallInProgress/Presenter/CallInProgressPresenter.swift index bc912a53b..ef596d7a3 100644 --- a/Nynja/Modules/Call/CallScreens/CallInProgress/Presenter/CallInProgressPresenter.swift +++ b/Nynja/Modules/Call/CallScreens/CallInProgress/Presenter/CallInProgressPresenter.swift @@ -52,10 +52,18 @@ class CallInProgressPresenter: CallInProgressPresenterProtocol, CallInProgressIn return interactor.activeSpeakerText() } + func groupCallSubject()->String { + return interactor.groupCallSubject() + } + func isCallConnected()->Bool { return interactor.isCallConnected() } + func joinLink()->String? { + return interactor.joinLink() + } + func speakerAction() { interactor.speakerAction() } @@ -107,7 +115,15 @@ class CallInProgressPresenter: CallInProgressPresenterProtocol, CallInProgressIn func callConnected(withVideo: Bool) { self.view.callConnected() } - + + func callFailed() { + self.view.callFailed() + } + + func callDisconnected() { + self.view.callDisconnected() + } + func askEndOrLeave() { self.view.askEndOrLeave() } diff --git a/Nynja/Modules/Call/CallScreens/CallInProgress/View/CallInProgressViewController.swift b/Nynja/Modules/Call/CallScreens/CallInProgress/View/CallInProgressViewController.swift index 9aabbcf0f..2ed92f550 100644 --- a/Nynja/Modules/Call/CallScreens/CallInProgress/View/CallInProgressViewController.swift +++ b/Nynja/Modules/Call/CallScreens/CallInProgress/View/CallInProgressViewController.swift @@ -170,13 +170,7 @@ class CallInProgressViewController: BaseVC, CallInProgressViewProtocol, AudioSes if let ctct = self.contact { viewTitle = "\(ctct.names ?? "") \(ctct.surnames ?? "")" } else if self.callInProgressMode == .groupAudio { - if self.presenter != nil { - if let room = RoomDAO.findRoom(by: self.roomId) { - if let rn = room.name { - viewTitle = rn - } - } - } + viewTitle = self.presenter.groupCallSubject() } callInProgressView.nameLabel.text = viewTitle @@ -355,11 +349,47 @@ class CallInProgressViewController: BaseVC, CallInProgressViewProtocol, AudioSes LogService.log(topic: .callSystem) { return "onCameraButtonPressed"} self.presenter.cameraAction() } + + func onMoreButtonPressed() { + + if .groupAudio == self.callInProgressMode { + let inviteAction = UIAlertAction(title: String.localizable.callInvite, style: .default) { [weak self] _ in + + var arrActions = [UIAlertAction]() + + if (self?.moderator)! { + let groupPartAction = UIAlertAction(title: String.localizable.callInviteGroupParticipants, style: .default) { [weak self] _ in + self?.presenter.updateCallParticipants() + } + + arrActions.append(groupPartAction) + } + + let copyLinkAction = UIAlertAction(title: String.localizable.callInviteCopyInvitationLink, style: .default) { [weak self] _ in + if let joinLink = self?.presenter.joinLink() { + UIPasteboard.general.string = joinLink + BannerView.shared().showBannerWith(text: String.localizable.callInviteCopyBannerText) + } + } + + arrActions.append(copyLinkAction) + + let cancelAction = UIAlertAction(title: String.localizable.cancel, style: .cancel) + arrActions.append(cancelAction) + + AlertManager.sharedInstance.showActionSheet(title: nil, message: nil, actions: arrActions) + } + + let cancelAction = UIAlertAction(title: String.localizable.cancel, style: .cancel) + + AlertManager.sharedInstance.showActionSheet(title: nil, message: nil, actions: [inviteAction, cancelAction]) + } + } func onPortOutButtonPressed() { AlertManager.sharedInstance.showAlertOk(message: String.localizable.voiceCallTheFeatureCurrentlyUnavailable) } - + func updateTime(text: String) { if .oneToOneVideo == self.callInProgressMode { callInProgressView.statusLabel.text = "video_call_status".localized + " - \(text)" @@ -456,12 +486,12 @@ class CallInProgressViewController: BaseVC, CallInProgressViewProtocol, AudioSes } func callConnecting() { - callInProgressView.statusLabel.text = "call_connecting".localized + callInProgressView.statusLabel.text = String.localizable.callConnecting disableButtons() } func callRinging() { - callInProgressView.statusLabel.text = "call_ringing".localized + callInProgressView.statusLabel.text = String.localizable.callRinging disableButtons() } @@ -470,10 +500,20 @@ class CallInProgressViewController: BaseVC, CallInProgressViewProtocol, AudioSes enableButtons() } + func callFailed() { + callInProgressView.statusLabel.text = String.localizable.callFailed + disableButtons() + } + + func callDisconnected() { + callInProgressView.statusLabel.text = String.localizable.callDisconnected + disableButtons() + } + private func enableButtons() { callInProgressView.messageButton.isEnabled = (nil != self.contact) || (self.roomId.count > 0) callInProgressView.cameraButton.isEnabled = (.oneToOneVideo == self.callInProgressMode) - callInProgressView.moreButton.isEnabled = true + callInProgressView.moreButton.isEnabled = (.groupAudio == self.callInProgressMode) callInProgressView.speakerButton.isEnabled = true callInProgressView.microphoneButton.isEnabled = true } diff --git a/Nynja/Modules/Call/View/GroupCollectionViewCell.swift b/Nynja/Modules/Call/View/GroupCollectionViewCell.swift index 2e6af0c04..c12b301fc 100644 --- a/Nynja/Modules/Call/View/GroupCollectionViewCell.swift +++ b/Nynja/Modules/Call/View/GroupCollectionViewCell.swift @@ -10,6 +10,8 @@ import UIKit class MyImageView: UIImageView { + var owner:Bool = false + override func layoutSubviews() { super.layoutSubviews() @@ -17,6 +19,9 @@ class MyImageView: UIImageView { let radius = self.frame.size.height/2 self.layer.cornerRadius = radius self.backgroundColor = .gray + + self.layer.borderWidth = owner ? 1.0 : 0.0 + self.layer.borderColor = owner ? UIColor.nynja.callGreen.cgColor : UIColor.nynja.clear.cgColor } } @@ -29,7 +34,8 @@ class GroupCollectionViewCell: UICollectionViewCell { weak var avatarImageView: MyImageView! weak var labelName: UILabel! - + var owner:Bool = false + private var longPressRecognizer: UILongPressGestureRecognizer? weak var delegate:GroupCollectionViewCellDelegate? @@ -106,13 +112,11 @@ class GroupCollectionViewCell: UICollectionViewCell { labelName.text = cp.name longPressRecognizer?.isEnabled = (!cp.isMe && canRemove) + avatarImageView.owner = owner if cp.isActive { - self.avatarImageView.setImage(url: URL.init(string: cp.avatarUrl), placeHolder: UIImage.nynja.Contacts.avaPlaceholder.image) - } else { - self.avatarImageView.image = nil } } diff --git a/Nynja/Modules/Call/View/MultiPageCollectionView.swift b/Nynja/Modules/Call/View/MultiPageCollectionView.swift index 566bb0e5c..cfee1e992 100644 --- a/Nynja/Modules/Call/View/MultiPageCollectionView.swift +++ b/Nynja/Modules/Call/View/MultiPageCollectionView.swift @@ -242,14 +242,12 @@ class MultiPageCollectionView: UIView, UIScrollViewDelegate, GroupAddParticipant let callPart:NYNCallParticipant? = collViewDataSource[indexPath.row] as? NYNCallParticipant if let cp = callPart { - cellPart.callPart = cp cellPart.canRemove = self.moderator cellPart.delegate = self + cellPart.owner = cp.isOwner cellPart.updateCell() - } else { - print("Illegal cell") } diff --git a/Nynja/Modules/Contacts/View/ViewController/ContactsViewController.swift b/Nynja/Modules/Contacts/View/ViewController/ContactsViewController.swift index 1936b49f0..c3422ff12 100644 --- a/Nynja/Modules/Contacts/View/ViewController/ContactsViewController.swift +++ b/Nynja/Modules/Contacts/View/ViewController/ContactsViewController.swift @@ -170,17 +170,17 @@ class ContactsViewController: BaseVC, ContactsViewProtocol, ContactCellDelegate, guard let this = self else { return } switch contactsViewMode { - case .myContacts: + case .myContacts, .allContacts: make.top.equalTo(this.inviteFriendsView.snp.bottom) case .transferContacts: make.top.equalTo(this.transferCoinsView.snp.bottom) - case .allContacts, .shareContact: + case .shareContact: make.top.equalTo(this.navigationView.snp.bottom) } } switch contactsViewMode { - case .myContacts: + case .myContacts, .allContacts: shouldShowSeparator = false transferCoinsView.isHidden = true inviteFriendsView.isHidden = false @@ -188,7 +188,7 @@ class ContactsViewController: BaseVC, ContactsViewProtocol, ContactCellDelegate, transferCoinsView.isHidden = false shouldShowSeparator = true inviteFriendsView.isHidden = true - case .allContacts, .shareContact: + case .shareContact: shouldShowSeparator = true transferCoinsView.isHidden = true inviteFriendsView.isHidden = true diff --git a/Nynja/Modules/Flows/CameraFlow/Camera/Interactor/CameraInteractor.swift b/Nynja/Modules/Flows/CameraFlow/Camera/Interactor/CameraInteractor.swift index 72bdbad6b..ad1819d5a 100644 --- a/Nynja/Modules/Flows/CameraFlow/Camera/Interactor/CameraInteractor.swift +++ b/Nynja/Modules/Flows/CameraFlow/Camera/Interactor/CameraInteractor.swift @@ -106,7 +106,6 @@ extension CameraInteractor { func stopRecording() { timer?.invalidate() seconds = -1 - sendTyping(for: .done) } func isGridShouldBeDrawing() -> Bool { diff --git a/Nynja/Modules/Flows/CameraSettingsFlow/CameraQualitySettings/View/CameraQualitySettingsViewController.swift b/Nynja/Modules/Flows/CameraSettingsFlow/CameraQualitySettings/View/CameraQualitySettingsViewController.swift index 52a8dc83c..d06d1c1e6 100644 --- a/Nynja/Modules/Flows/CameraSettingsFlow/CameraQualitySettings/View/CameraQualitySettingsViewController.swift +++ b/Nynja/Modules/Flows/CameraSettingsFlow/CameraQualitySettings/View/CameraQualitySettingsViewController.swift @@ -18,7 +18,11 @@ final class CameraQualitySettingsViewController: UIViewController, UITableViewDe override func viewDidLoad() { super.viewDidLoad() + + view.backgroundColor = UIColor.nynja.darkLight + statusBarBackgroundView = UIView.makeStatusBarBackgroundView(on: view) + headerView = UIView.makeHeaderView( on: view, top: statusBarBackgroundView, @@ -113,8 +117,8 @@ private extension CameraQualitySettingsViewController { tableView.dataSource = self view.addSubview(tableView) - - tableView.backgroundColor = UIColor.nynja.darkLight + + tableView.backgroundColor = .clear tableView.separatorStyle = .singleLine tableView.separatorColor = UIColor.nynja.backgroundGray diff --git a/Nynja/Modules/Flows/CameraSettingsFlow/CameraSettings/View/CameraSettingsViewController.swift b/Nynja/Modules/Flows/CameraSettingsFlow/CameraSettings/View/CameraSettingsViewController.swift index c25a20a5b..1f7cbb7db 100644 --- a/Nynja/Modules/Flows/CameraSettingsFlow/CameraSettings/View/CameraSettingsViewController.swift +++ b/Nynja/Modules/Flows/CameraSettingsFlow/CameraSettings/View/CameraSettingsViewController.swift @@ -19,7 +19,10 @@ final class CameraSettingsViewController: UIViewController, UITableViewDelegate, override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = UIColor.nynja.darkLight + statusBarbackgroundView = UIView.makeStatusBarBackgroundView(on: view) + headerView = UIView.makeHeaderView( on: view, top: statusBarbackgroundView, @@ -118,7 +121,7 @@ private extension CameraSettingsViewController { view.addSubview(tableView) - tableView.backgroundColor = UIColor.nynja.darkLight + tableView.backgroundColor = .clear tableView.separatorStyle = .singleLine tableView.separatorColor = UIColor.nynja.backgroundGray diff --git a/Nynja/Modules/ForwardSelector/ForwardSelectorProtocols+ShareExt.swift b/Nynja/Modules/ForwardSelector/ForwardSelectorProtocols+ShareExt.swift index f7f238757..bb740d0b9 100644 --- a/Nynja/Modules/ForwardSelector/ForwardSelectorProtocols+ShareExt.swift +++ b/Nynja/Modules/ForwardSelector/ForwardSelectorProtocols+ShareExt.swift @@ -7,8 +7,6 @@ // protocol ForwardSelectorViewSetupProtocol: class { - - func setupUI(with mode: ForwardSelectorDisplayMode) func setupForwardTargets(_ targets: [ForwardTarget], selected: [ForwardTarget]) func updateSelection(selectedTargets: [ForwardTarget]) } diff --git a/Nynja/Modules/ForwardSelector/ForwardSelectorProtocols.swift b/Nynja/Modules/ForwardSelector/ForwardSelectorProtocols.swift index dff1c82b5..7a1390aad 100644 --- a/Nynja/Modules/ForwardSelector/ForwardSelectorProtocols.swift +++ b/Nynja/Modules/ForwardSelector/ForwardSelectorProtocols.swift @@ -37,6 +37,8 @@ protocol ForwardSelectorViewProtocol: ForwardSelectorViewSetupProtocol { /** * Add here your methods for communication PRESENTER -> VIEW */ + + func setupUI(with mode: ForwardSelectorDisplayMode) } protocol ForwardSelectorPresenterProtocol: BasePresenterProtocol { diff --git a/Nynja/Modules/GroupStorage/Interactor/GroupStorageInteractor.swift b/Nynja/Modules/GroupStorage/Interactor/GroupStorageInteractor.swift index 3e071e051..3b98b2d10 100644 --- a/Nynja/Modules/GroupStorage/Interactor/GroupStorageInteractor.swift +++ b/Nynja/Modules/GroupStorage/Interactor/GroupStorageInteractor.swift @@ -83,7 +83,7 @@ class GroupStorageInteractor: BaseInteractor, GroupStorageInteractorInputProtoco } } - func updateProgress(_ progress: ProgressModel) { + func update(progress: ProgressModel) { DispatchQueue.main.async { [weak presenter] in presenter?.updateProgress(progressModel: progress) } diff --git a/Nynja/Modules/GroupStorage/View/Files/GroupFilesCell.swift b/Nynja/Modules/GroupStorage/View/Files/GroupFilesCell.swift index bd8395239..77718c382 100644 --- a/Nynja/Modules/GroupStorage/View/Files/GroupFilesCell.swift +++ b/Nynja/Modules/GroupStorage/View/Files/GroupFilesCell.swift @@ -165,7 +165,7 @@ class GroupFilesCell : UITableViewCell, GroupStorageCell { if item.creationDate != nil { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "MMM dd, YYYY 'at' hh:mm a".localizedDateFormat - dateFormatter.locale = Locale(identifier: Language.current.rawValue) + dateFormatter.locale = Locale(identifier: AppLanguage.current.rawValue) dateFormatter.amSymbol = String.localizable.am dateFormatter.pmSymbol = String.localizable.pm dateString = dateFormatter.string(from: item.creationDate!) diff --git a/Nynja/Modules/History/HistoryProtocols.swift b/Nynja/Modules/History/HistoryProtocols.swift index 64060e0e6..2c864a578 100644 --- a/Nynja/Modules/History/HistoryProtocols.swift +++ b/Nynja/Modules/History/HistoryProtocols.swift @@ -16,6 +16,7 @@ protocol HistoryWireFrameProtocol: class { * Add here your methods for communication PRESENTER -> WIREFRAME */ func showChat(contact: Contact) + func showInviteFriends() } protocol HistoryViewProtocol: class { @@ -42,6 +43,7 @@ protocol HistoryPresenterProtocol: BasePresenterProtocol { func accept(with phoneId: String) func showChat(contact: Contact) + func showInviteFriends() } protocol HistoryInteractorOutputProtocol: class { diff --git a/Nynja/Modules/History/Presenter/HistoryPresenter.swift b/Nynja/Modules/History/Presenter/HistoryPresenter.swift index ec1ed1bde..9e56816a1 100644 --- a/Nynja/Modules/History/Presenter/HistoryPresenter.swift +++ b/Nynja/Modules/History/Presenter/HistoryPresenter.swift @@ -53,4 +53,8 @@ class HistoryPresenter: BasePresenter, HistoryPresenterProtocol, HistoryInteract func filtered(contacts: [Contact]) { view.showHistory(contacts: contacts) } + + func showInviteFriends() { + self.wireFrame.showInviteFriends() + } } diff --git a/Nynja/Modules/History/View/HistoryViewController.swift b/Nynja/Modules/History/View/HistoryViewController.swift index a8b027e35..2e322d9f8 100644 --- a/Nynja/Modules/History/View/HistoryViewController.swift +++ b/Nynja/Modules/History/View/HistoryViewController.swift @@ -26,6 +26,22 @@ class HistoryViewController: BaseVC, HistoryViewProtocol, HistoryTableDSDelegate // MARK: - Subviews + lazy var inviteFriendsView: UIView = { + let inviteFriendHeader = InviteFriendHeaderView() + let inviteFriendTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(inviteFriendTapped)) + inviteFriendHeader.isUserInteractionEnabled = true + inviteFriendHeader.addGestureRecognizer(inviteFriendTapRecognizer) + + self.view.addSubview(inviteFriendHeader) + inviteFriendHeader.snp.makeConstraints({ (make) in + make.left.right.equalToSuperview() + make.height.equalTo(InviteFriendHeaderView.height.adjustedByWidth) + make.top.equalTo(navigationView.snp.bottom) + }) + return inviteFriendHeader + }() + + lazy var tableView: UITableView = { let tableView = UITableView.default tableView.delegate = self @@ -44,7 +60,7 @@ class HistoryViewController: BaseVC, HistoryViewProtocol, HistoryTableDSDelegate tableView.snp.makeConstraints({ (make) in make.left.right.equalToSuperview() - make.top.equalTo(navigationView.snp.bottom) + make.top.equalTo(self.inviteFriendsView.snp.bottom) }) return tableView }() @@ -80,7 +96,7 @@ class HistoryViewController: BaseVC, HistoryViewProtocol, HistoryTableDSDelegate super.initialize() screenTitle = Constants.LocalizableKeys.history.localized.uppercased() - navigationView.isSeparatorVisible = true + navigationView.isSeparatorVisible = false tableView.isHidden = false controlContainerView.isHidden = false @@ -120,6 +136,11 @@ class HistoryViewController: BaseVC, HistoryViewProtocol, HistoryTableDSDelegate self.view.endEditing(true) self.presenter.showChat(contact: contact) } + + @objc func inviteFriendTapped() { + self.presenter.showInviteFriends() + } + } extension HistoryViewController: UITableViewDelegate { diff --git a/Nynja/Modules/History/WireFrame/HistoryWireframe.swift b/Nynja/Modules/History/WireFrame/HistoryWireframe.swift index f27684de1..e61768b69 100644 --- a/Nynja/Modules/History/WireFrame/HistoryWireframe.swift +++ b/Nynja/Modules/History/WireFrame/HistoryWireframe.swift @@ -37,4 +37,7 @@ class HistoryWireFrame: HistoryWireFrameProtocol { mainWF?.showChat(contact) } + func showInviteFriends() { + mainWF?.showInviteFriends() + } } diff --git a/Nynja/Modules/Interpretation/InterpretationModel.swift b/Nynja/Modules/Interpretation/InterpretationModel.swift index 7554b732a..069347844 100644 --- a/Nynja/Modules/Interpretation/InterpretationModel.swift +++ b/Nynja/Modules/Interpretation/InterpretationModel.swift @@ -10,8 +10,8 @@ import Foundation class InterpretationModel { var type: InterpretationType = .general - var fromLang: Language = Language.empty_default - var toLang: Language = Language.current + var fromLang: AppLanguage = AppLanguage.empty_default + var toLang: AppLanguage = AppLanguage.current var time: Int = 30 var price: NYNMoney { let result = Double(time) * NSDecimalNumber(decimal: type.price.amount).doubleValue diff --git a/Nynja/Modules/Interpretation/View/InterpretationViewController.swift b/Nynja/Modules/Interpretation/View/InterpretationViewController.swift index d0e92c0bc..597c757fa 100644 --- a/Nynja/Modules/Interpretation/View/InterpretationViewController.swift +++ b/Nynja/Modules/Interpretation/View/InterpretationViewController.swift @@ -345,7 +345,7 @@ final class InterpretationViewController: BaseVC, InterpretationViewProtocol { picker.delegate = delegate } - private func selectRow(with language: Language, in picker: UIPickerView, animated: Bool) { + private func selectRow(with language: AppLanguage, in picker: UIPickerView, animated: Bool) { var row: Int? = fromLangDelegate.positionIn(value: language) if picker == self.toLangPickerView { row = toLangDelegate.positionIn(value: language) @@ -407,7 +407,7 @@ extension InterpretationViewController: NavigationProtocol { } extension InterpretationViewController: LanguagePickerDelegateCallback { - func didSelectLanguage(_ pickerView: UIPickerView, language: Language) { + func didSelectLanguage(_ pickerView: UIPickerView, language: AppLanguage) { if pickerView == self.fromLangPickerView { self.model.fromLang = language } else if pickerView == self.toLangPickerView { diff --git a/Nynja/Modules/Interpretation/View/LanguagePickerDelegate.swift b/Nynja/Modules/Interpretation/View/LanguagePickerDelegate.swift index 4e90682de..5675a064e 100644 --- a/Nynja/Modules/Interpretation/View/LanguagePickerDelegate.swift +++ b/Nynja/Modules/Interpretation/View/LanguagePickerDelegate.swift @@ -9,7 +9,7 @@ import Foundation protocol LanguagePickerDelegateCallback : class { - func didSelectLanguage(_ pickerView: UIPickerView, language : Language) + func didSelectLanguage(_ pickerView: UIPickerView, language : AppLanguage) } class LanguagePickerDelegate : NSObject, UIPickerViewDataSource, UIPickerViewDelegate { @@ -18,11 +18,11 @@ class LanguagePickerDelegate : NSObject, UIPickerViewDataSource, UIPickerViewDel weak var callback : LanguagePickerDelegateCallback? - private var data: [Language]! + private var data: [AppLanguage]! //MARK: - Init required init(defaultEmptyLanguage: Bool) { - self.data = defaultEmptyLanguage ? Language.allValuesWithDefault : Language.allValues + self.data = defaultEmptyLanguage ? AppLanguage.allValuesWithDefault : AppLanguage.allValues } //MARK: - UIPickerViewDataSource @@ -48,7 +48,7 @@ class LanguagePickerDelegate : NSObject, UIPickerViewDataSource, UIPickerViewDel callback?.didSelectLanguage(pickerView, language: valueForRow(row)) } - func positionIn(value : Language) -> Int? { + func positionIn(value : AppLanguage) -> Int? { if let i = data.index(of: value) { return pickerViewMiddle + i } @@ -66,7 +66,7 @@ class LanguagePickerDelegate : NSObject, UIPickerViewDataSource, UIPickerViewDel return label } - private func valueForRow(_ row: Int) -> Language { + private func valueForRow(_ row: Int) -> AppLanguage { return self.data[row % data.count] } diff --git a/Nynja/Modules/InviteFriends/Interactor/InviteFriendsInteractor.swift b/Nynja/Modules/InviteFriends/Interactor/InviteFriendsInteractor.swift index c2e141fa1..0c3cf1677 100644 --- a/Nynja/Modules/InviteFriends/Interactor/InviteFriendsInteractor.swift +++ b/Nynja/Modules/InviteFriends/Interactor/InviteFriendsInteractor.swift @@ -128,7 +128,9 @@ class InviteFriendsInteractor: BaseInteractor, InviteFriendsInteractorInputProto private func prepareGroupedContacts(contacts: [PhoneContact]) { var letters = Set() - contacts.forEach { + var _contacts = contacts + _contacts.sort() { $0.name ?? "" < $1.name ?? "" } + _contacts.forEach { if let first = $0.firstLetter { letters.insert(first) if groupedContacts[first] == nil { diff --git a/Nynja/Modules/LanguageSettings/LanguageSelector/Interactor/LanguageSelectroInteractor.swift b/Nynja/Modules/LanguageSettings/LanguageSelector/Interactor/LanguageSelectroInteractor.swift index 610754656..919c7f77c 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSelector/Interactor/LanguageSelectroInteractor.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSelector/Interactor/LanguageSelectroInteractor.swift @@ -12,7 +12,7 @@ class LanguageSelectorInteractor: BaseInteractor, LanguageSelectorInteractorInpu var translationService = TranslationService(input: TranslationService.Input(apiKey: ThirdPartyServicesFactory.google.serviceConfig.apiKey)) - var languages: [LangExtended] = [] { + var languages: [Language] = [] { didSet { presenter.languagesFetched(languages) } diff --git a/Nynja/Modules/LanguageSettings/LanguageSelector/LanguageSelectorProtocols.swift b/Nynja/Modules/LanguageSettings/LanguageSelector/LanguageSelectorProtocols.swift index a42324e6a..3efced59e 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSelector/LanguageSelectorProtocols.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSelector/LanguageSelectorProtocols.swift @@ -10,11 +10,9 @@ enum LanguageSelector { struct Input { let selectorType: SelectorType - let selectedLang: SelectedLang + let selectedLang: Language? let selectorLang: SelectorLang? let saveLangHandler: SaveLangHandler? - - let isNoneAvailable: Bool } } @@ -55,8 +53,8 @@ protocol LanguageSelectorInteractorOutputProtocol: class { * Add here your methods for communication INTERACTOR -> PRESENTER */ - func languagesFetched(_ languages: [LangExtended]) - func languagesFiltered(_ languages: [LangExtended]) + func languagesFetched(_ languages: [Language]) + func languagesFiltered(_ languages: [Language]) } protocol LanguageSelectorInteractorInputProtocol: BaseInteractorProtocol { diff --git a/Nynja/Modules/LanguageSettings/LanguageSelector/Presenter/LanguageSelectorPresenter.swift b/Nynja/Modules/LanguageSettings/LanguageSelector/Presenter/LanguageSelectorPresenter.swift index 50e26a802..566490d61 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSelector/Presenter/LanguageSelectorPresenter.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSelector/Presenter/LanguageSelectorPresenter.swift @@ -11,11 +11,10 @@ class LanguageSelectorPresenter: BasePresenter, LanguageSelectorPresenterProtoco private var input: LanguageSelector.Input var selectorType: SelectorType - var selectedLang: SelectedLang + var selectedLang: Language? var selectorLang: SelectorLang? var saveLangHandler: SaveLangHandler? - let isNoneAvailable: Bool - + //MARK: - BasePresenter override var itemsFactory: WCItemsFactory? { @@ -45,7 +44,6 @@ class LanguageSelectorPresenter: BasePresenter, LanguageSelectorPresenterProtoco selectedLang = self.input.selectedLang selectorLang = self.input.selectorLang saveLangHandler = self.input.saveLangHandler - isNoneAvailable = self.input.isNoneAvailable super.init() } @@ -56,7 +54,7 @@ class LanguageSelectorPresenter: BasePresenter, LanguageSelectorPresenterProtoco func back() { wireFrame.dismiss() { [weak self] in - self?.selectorLang?(.none) + self?.selectorLang?(nil) } } @@ -66,25 +64,28 @@ class LanguageSelectorPresenter: BasePresenter, LanguageSelectorPresenterProtoco guard let lang = lang.lang else { return } if let saveLangHandler = saveLangHandler { - AlertManager.sharedInstance.showAlertWithTwoActions(title: String.localizable.lsMyLanguage, message: String.localizable.lsAlertMessage, firstActionTitle: String.localizable.lsAlways, secondActionTitle: String.localizable.lsOnce, + AlertManager.sharedInstance.showAlertWithTwoActions( + title: selectorType.title.lowercased().capitalizingFirstLetter(), + message: String.localizable.lsAlertMessage, + firstActionTitle: String.localizable.lsAlways, + secondActionTitle: String.localizable.lsOnce, firstAction: { [weak self] in self?.wireFrame.dismiss() { - self?.selectorLang?(.lang(lang)) - saveLangHandler(true, .lang(lang)) + self?.selectorLang?(lang) + saveLangHandler(true, lang) } - }, secondAction: { [weak self] in + }, + secondAction: { [weak self] in self?.wireFrame.dismiss() { - self?.selectorLang?(.lang(lang)) - saveLangHandler(false, .lang(lang)) + self?.selectorLang?(lang) + saveLangHandler(false, lang) } }) - } else { wireFrame.dismiss() { [weak self] in - self?.selectorLang?(.lang(lang)) + self?.selectorLang?(lang) } } - } func filter(with text: String) { @@ -94,12 +95,12 @@ class LanguageSelectorPresenter: BasePresenter, LanguageSelectorPresenterProtoco //MARK: - LanguageSelectorInteractorOutputProtocol - func languagesFetched(_ languages: [LangExtended]) { + func languagesFetched(_ languages: [Language]) { let groupedLanguages = prepareLanguagesToShow(languages) view.setupLanguages(groupedLanguages) } - func languagesFiltered(_ languages: [LangExtended]) { + func languagesFiltered(_ languages: [Language]) { let groupedLanguages = prepareLanguagesToShow(languages) view.setupLanguages(groupedLanguages) } @@ -107,23 +108,15 @@ class LanguageSelectorPresenter: BasePresenter, LanguageSelectorPresenterProtoco //MARK: - Private - private func prepareLanguagesToShow(_ languages: [LangExtended]) -> GroupOfLangCellViewModels { - var groupedLanguages = languages.map { LangCellViewModel(lang: $0, isSelected: $0.language == currentLang) }.groupedByName() - if isNoneAvailable { - var isSelectedNone = selectedLang == nil ? true : false - if case .lang(let language) = selectedLang { - isSelectedNone = language.language == LanguageSettingConstants.languageNone ? true : isSelectedNone - } - groupedLanguages["#"] = [LangCellViewModel(lang: LangExtended.none, isSelected: isSelectedNone)] - } - return groupedLanguages + private func prepareLanguagesToShow(_ languages: [Language]) -> GroupOfLangCellViewModels { + return languages + .map { + LangCellViewModel(lang: $0, + isSelected: $0.language == currentLang) } + .groupedByName() } private var currentLang: String? { - var currentLang: String? = nil - if case let .lang(lang) = selectedLang { - currentLang = lang.language - } - return currentLang + return selectedLang?.language } } diff --git a/Nynja/Modules/LanguageSettings/LanguageSelector/View/TableView/Cell/LangCellViewModel.swift b/Nynja/Modules/LanguageSettings/LanguageSelector/View/TableView/Cell/LangCellViewModel.swift index 490a2bed2..386c44c74 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSelector/View/TableView/Cell/LangCellViewModel.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSelector/View/TableView/Cell/LangCellViewModel.swift @@ -7,10 +7,10 @@ // class LangCellViewModel : CellModel { - var lang: LangExtended? + var lang: Language? var isSelected: Bool = false - init(lang: LangExtended, isSelected: Bool = false) { + init(lang: Language, isSelected: Bool = false) { self.lang = lang self.isSelected = isSelected } diff --git a/Nynja/Modules/LanguageSettings/LanguageSettingProtocol.swift b/Nynja/Modules/LanguageSettings/LanguageSettingProtocol.swift index eec2f7cc7..c3758f683 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettingProtocol.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettingProtocol.swift @@ -6,48 +6,78 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // -typealias LangSignature = (name: String, lang: String) - -struct LanguageSettingConstants { +enum LanguageSettingConstants { static let languageSettingsGroup = "LANGUAGE_SETTING" - static let languageNone = "none" } enum LanguageSettingKey: String { - case chatLanguage = "CHAT_LANGUAGE" - case autoLanguageChat = "AUTO_LANGUAGE_IN_CHAT" - case translateLanguage = "TRANSLATE_LANGUAGE" - case autoTranslateLanguage = "AUTO_TRANSLATE_LANGUAGE" + case defaultLanguage = "DEFAULT_LANGUAGE" + + case autoTranslate = "AUTO_TRANSLATE" + + case outgoingTranslateLanguage = "OUTGOING_TRANSLATE_LANGUAGE" + case autoOutgoingTranslate = "AUTO_OUTGOING_TRANSLATE" + + case transcribeLanguage = "TRANCRIBE_LANGUAGE" + case autoTranscribe = "AUTO_TRANSCRIBE" + case autoTranslateTranscribe = "AUTO_TRANSLATE_TRANSCRIBE" +} + +enum AutoOutgoingTranslate: String { + case off + case auto + case manual } protocol LanguageSettingProtocol: Customizable { - var chatLanguage: LangSignature { get } - var autoLanguageChat: Bool { get } + var defaultLanguage: Language { get } - var translateLanguage: LangSignature? { get } - var autoTranslateLanguage: Bool { get } + var autoTranslate: Bool { get } + + var outgoingTranslateLanguage: Language { get } + var autoOutgoingTranslate: AutoOutgoingTranslate { get } + + var transcribeLanguage: Language { get } + var autoTranscribe: Bool { get } + var autoTranslateTranscribe: Bool { get } func feature(by key: LanguageSettingKey) -> Feature func featureOrNil(by key: LanguageSettingKey) -> Feature? } +extension Contact: LanguageSettingProtocol {} + +extension Member: LanguageSettingProtocol {} + extension LanguageSettingProtocol { - var chatLanguage: LangSignature { - return getChatLanguage() + var defaultLanguage: Language { + return getDefaultLanguage() } - var autoLanguageChat: Bool { - return getAutoLanguageChat() + var autoTranslate: Bool { + return getAutoTranslate() } - var translateLanguage: LangSignature? { - return getTranslateLanguage() + var outgoingTranslateLanguage: Language { + return getOutgoingTranslateLanguage() } - var autoTranslateLanguage: Bool { - return getAutoTranslateLanguage() + var autoOutgoingTranslate: AutoOutgoingTranslate { + return getAutoOutgoingTranslate() + } + + var transcribeLanguage: Language { + return getTranscribeLanguage() + } + + var autoTranscribe: Bool { + return getAutoTranscribe() + } + + var autoTranslateTranscribe: Bool { + return getAutoTranslateTranscribe() } func feature(by key: LanguageSettingKey) -> Feature { @@ -67,31 +97,47 @@ extension LanguageSettingProtocol { private extension LanguageSettingProtocol { - func getChatLanguage() -> LangSignature { - guard let feature = featureOrNil(by: .chatLanguage) else { - return (name: Language.current.longValue, lang: Language.current.rawValue) - } - return splitLanguageSignature(feature.value) + func getDefaultLanguage() -> Language { + return featureOrNil(by: .defaultLanguage) + .flatMap { $0.languageValue } + ?? .init(language: AppLanguage.english) + } + + func getAutoTranslate() -> Bool { + return featureOrNil(by: .autoTranslate) + .flatMap { $0.boolValue } + ?? false + } + + func getOutgoingTranslateLanguage() -> Language { + return featureOrNil(by: .outgoingTranslateLanguage) + .flatMap { $0.languageValue } + ?? .init(language: AppLanguage.english) } - func getAutoLanguageChat() -> Bool { - guard let feature = featureOrNil(by: .autoLanguageChat) else { return false } - return feature.value == true.description ? true : false + func getAutoOutgoingTranslate() -> AutoOutgoingTranslate { + return featureOrNil(by: .autoOutgoingTranslate) + .flatMap { $0.value } + .flatMap { AutoOutgoingTranslate(rawValue: $0) } + ?? .off } - func getTranslateLanguage() -> LangSignature? { - guard let feature = featureOrNil(by: .translateLanguage) else { return nil } - return splitLanguageSignature(feature.value) + func getTranscribeLanguage() -> Language { + return featureOrNil(by: .transcribeLanguage) + .flatMap { $0.languageValue } + ?? getDefaultLanguage() } - func getAutoTranslateLanguage() -> Bool { - guard let feature = featureOrNil(by: .autoTranslateLanguage) else { return false } - return feature.value == true.description ? true : false + func getAutoTranscribe() -> Bool { + return featureOrNil(by: .autoTranscribe) + .flatMap { $0.boolValue } + ?? true } - func splitLanguageSignature(_ value: String?) -> LangSignature { - guard let signature = value?.split(separator: ":"), let name = signature.first, let lang = signature.last else { return (name: "", lang: "") } - return (name: String(name), lang: String(lang)) + func getAutoTranslateTranscribe() -> Bool { + return featureOrNil(by: .autoTranslateTranscribe) + .flatMap { $0.boolValue } + ?? false } } diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings+Helper.swift b/Nynja/Modules/LanguageSettings/LanguageSettings+Helper.swift index 60d966610..f6e84902c 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettings+Helper.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettings+Helper.swift @@ -7,42 +7,21 @@ // enum SelectorType: String { - case my - case sending - case transcription + case translateIncoming + case translateOutgoing + case transcribe var title: String { switch self { - case .my: - return String.localizable.lsMyLanguage.uppercased() - case .sending: - return String.localizable.lsLanguageForTranslation.uppercased() - case .transcription: - return String.localizable.lsTranscriptionLanguage.uppercased() + case .translateIncoming: + return String.localizable.lsLanguageSelectorTitleOnIncomingTranslate.uppercased() + case .translateOutgoing: + return String.localizable.lsLanguageSelectorTitleOnOutgoingTranslate.uppercased() + case .transcribe: + return String.localizable.lsLanguageSelectorTitleOnTranscribe.uppercased() } } } -enum SelectedLang: Equatable { - case none - case lang(LangExtended) - - init(_ signature: LangSignature?) { - guard let signature = signature else { - self = .none - return - - } - self = .lang(LangExtended(signature: signature)) - } - - func title() -> String { - if case let .lang(lang) = self { - return lang.name.capitalized - } - return String.localizable.lsNone - } -} - -typealias SelectorLang = (SelectedLang)->Void -typealias SaveLangHandler = (Bool, SelectedLang)->Void +typealias SelectorLang = (Language?)->Void +typealias SaveLangHandler = (Bool, Language)->Void diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/ChatLanguageSettingsProtocols.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/ChatLanguageSettingsProtocols.swift index dd95d5dd0..30623cff2 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettings/ChatLanguageSettingsProtocols.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/ChatLanguageSettingsProtocols.swift @@ -48,21 +48,24 @@ protocol ChatLanguageSettingsInteractorOutputProtocol: class { * Add here your methods for communication INTERACTOR -> PRESENTER */ - func update(myLang: LangSignature, myLangIsOn: Bool, sendingLang: LangSignature?, sendingLangIsOn: Bool) + func update(with setting: ConversationLanguageSettingProtocol, to user: Contact?) } protocol ChatLanguageSettingsInteractorInputProtocol: BaseInteractorProtocol { var presenter: ChatLanguageSettingsInteractorOutputProtocol? { get set } + var chatType: ChatLanguageSettingType { get } /** * Add here your methods for communication PRESENTER -> INTERACTOR */ - func updateMyLanguage(_ lang: LangExtended) - func updateAutotranslateReceipt(_ isOn: Bool) - func updateSendingLanguage(_ lang: LangExtended) - func updateAutotranslateSending(_ isOn: Bool) + func update(autoTranslate: Bool) + func update(outgoingTranslateLanguage: Language) + func update(autoOutgoingTranslate: AutoOutgoingTranslate) + func update(transcribeLanguage: Language) + func update(autoTranscribe: Bool) + func update(autoTranslateTranscribe: Bool) } //MARK: - View diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/Interactor/ChatLanguageSettingsInteractor.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/Interactor/ChatLanguageSettingsInteractor.swift index 6544fadfa..625d5ffaf 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettings/Interactor/ChatLanguageSettingsInteractor.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/Interactor/ChatLanguageSettingsInteractor.swift @@ -28,48 +28,62 @@ class ChatLanguageSettingsInteractor: BaseInteractor, ChatLanguageSettingsIntera override func loadData() { super.loadData() - var settingOwner: Customizable + let settingStorage: LanguageSettingProtocol if case let .contact(id) = chatType, let contact = ContactDAO.findContactBy(phoneId: id) { - settingOwner = contact + settingStorage = contact } else if case let .room(id) = chatType, let phoneId = StorageService.sharedInstance.phoneId, let member = MemberDAO.findMemberBy(roomId: id, phoneId: phoneId) { - settingOwner = member + settingStorage = member } else { //TODO: Error return } - conversationLanguageSettingService = ConversationLanguageSettingService(dependencies: .init(settingOwner: settingOwner)) + conversationLanguageSettingService = ConversationLanguageSettingService( + dependencies: .init(settingStorage: settingStorage)) - presenter?.update(myLang: conversationLanguageSettingService.chatLanguage, myLangIsOn: conversationLanguageSettingService.autoLanguageChat, sendingLang: conversationLanguageSettingService.translateLanguage, sendingLangIsOn: conversationLanguageSettingService.autoTranslateLanguage) + let setting = conversationLanguageSettingService.getConversationLanguageSetting() + presenter?.update(with: setting, to: settingStorage as? Contact) } //MARK: - ChatLanguageSettingsInteractorInputProtocol - func updateMyLanguage(_ lang: LangExtended) { - let feature = conversationLanguageSettingService.feature(by: .chatLanguage) - feature.value = lang.toString + func update(autoTranslate: Bool) { + let feature = conversationLanguageSettingService.feature(by: .autoTranslate) + feature.value = autoTranslate.description update(with: feature) } - func updateAutotranslateReceipt(_ isOn: Bool) { - let feature = conversationLanguageSettingService.feature(by: .autoLanguageChat) - feature.value = isOn.description + func update(outgoingTranslateLanguage: Language) { + let feature = conversationLanguageSettingService.feature(by: .outgoingTranslateLanguage) + feature.value = outgoingTranslateLanguage.stringValue update(with: feature) } - func updateSendingLanguage(_ lang: LangExtended) { - let feature = conversationLanguageSettingService.feature(by: .translateLanguage) - feature.value = lang.toString + func update(autoOutgoingTranslate: AutoOutgoingTranslate) { + let feature = conversationLanguageSettingService.feature(by: .autoOutgoingTranslate) + feature.value = autoOutgoingTranslate.rawValue update(with: feature) } - func updateAutotranslateSending(_ isOn: Bool) { - let feature = conversationLanguageSettingService.feature(by: .autoTranslateLanguage) - feature.value = isOn.description + func update(transcribeLanguage: Language) { + let feature = conversationLanguageSettingService.feature(by: .transcribeLanguage) + feature.value = transcribeLanguage.stringValue update(with: feature) } + func update(autoTranscribe: Bool) { + let feature = conversationLanguageSettingService.feature(by: .autoTranscribe) + feature.value = autoTranscribe.description + update(with: feature) + } + + func update(autoTranslateTranscribe: Bool) { + let feature = conversationLanguageSettingService.feature(by: .autoTranslateTranscribe) + feature.value = autoTranslateTranscribe.description + update(with: feature) + } + //MARK: - StorageSubscriber diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/Presenter/ChatLanguageSettingsPresenter.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/Presenter/ChatLanguageSettingsPresenter.swift index 7f741b687..dc21d34cc 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettings/Presenter/ChatLanguageSettingsPresenter.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/Presenter/ChatLanguageSettingsPresenter.swift @@ -9,21 +9,22 @@ class ChatLanguageSettingsPresenter: BasePresenter, ChatLanguageSettingsPresenterProtocol, ChatLanguageSettingsInteractorOutputProtocol { - private struct Constants { - static let myLanguageHeaderId = "my_language" - static let translationHeaderId = "translation" + private enum Constants { + static let translationInHeaderId = "translation_incoming" + static let translationOutHeaderId = "translation_outgoing" + static let transcribe = "transcribe" } - var myLang: SelectedLang = .lang(LangExtended(language: "en", name: "English")) - var myLangIsOn: Bool = true - var sendingLang: SelectedLang = .none - var sendingLangIsOn: Bool = false + var contact: Contact? - //MARK: - BasePresenter + var autoTranslate: Bool = true - override var itemsFactory: WCItemsFactory? { - return GroupOptionsItemsFactory() // TODO: on create or update process? - } + var outgoingTranslateLanguage: Language = Language(language: .english) + var autoOutgoingTranslate: AutoOutgoingTranslate = .off + + var transcribeLanguage: Language = Language(language: .english) + var autoTranscribe: Bool = true + var autoTranslateTranscribe: Bool = true //MARK: - ChatLanguageSettingsPresenterProtocol @@ -45,11 +46,15 @@ class ChatLanguageSettingsPresenter: BasePresenter, ChatLanguageSettingsPresente //MARK: - ChatLanguageSettingsInteractorOutputProtocol - func update(myLang: LangSignature, myLangIsOn: Bool, sendingLang: LangSignature?, sendingLangIsOn: Bool) { - self.myLang = SelectedLang(myLang) - self.myLangIsOn = myLangIsOn - self.sendingLang = SelectedLang(sendingLang) - self.sendingLangIsOn = sendingLangIsOn + func update(with setting: ConversationLanguageSettingProtocol, to user: Contact?) { + contact = user + + autoTranslate = setting.autoTranslate + outgoingTranslateLanguage = setting.outgoingTranslateLanguage + autoOutgoingTranslate = setting.autoOutgoingTranslate + transcribeLanguage = setting.transcribeLanguage + autoTranscribe = setting.autoTranscribe + autoTranslateTranscribe = setting.autoTranslateTranscribe view?.setupSettings(sections: configureTableSections(), items: configureTableItems()) } @@ -58,157 +63,173 @@ class ChatLanguageSettingsPresenter: BasePresenter, ChatLanguageSettingsPresente //MARK: - Private private func configureTableSections() -> [LabeledHeaderViewModel] { - return [.init(title: String.localizable.lsMyLanguage, headerId: Constants.myLanguageHeaderId), - .init(title: String.localizable.lsLanguageForTranslation, headerId: Constants.translationHeaderId)] + var sections: [LabeledHeaderViewModel] = [.init(title: String.localizable.lsTranslateIncoming, + headerId: Constants.translationInHeaderId), + .init(title: String.localizable.lsTranslateOutcoming, + headerId: Constants.translationOutHeaderId)] + guard contact != nil else { + return sections + } + sections.append(.init(title: String.localizable.lsTranscribe, + headerId: Constants.transcribe)) + return sections } private func configureTableItems() -> [[CellModel]] { - let myLang = makeMyLangModel() - let switchMyLang = makeSwitchMyLangModel() - - let sendingSectionCoordinator = LanguageSectionCoordinator() - let sendingLang = makeSendingLangModel(sectionCoordinator: sendingSectionCoordinator) - let switchSendingLang = makeSwitchSendingLangModel(sectionCoordinator: sendingSectionCoordinator) + var items: [[CellModel]] = [[makeTranslationIncomingModel(), + ActionCellViewModel(title: String.localizable.lsTranslateIncDescription)], + [makeTranslationOutgoingLanguageModel(), + ActionCellViewModel(title: String.localizable.lsTranslateOutDescription1), + makeTranslationOutgoingModel(), + ActionCellViewModel(title: String.localizable.lsTranslateOutDescription2)]] + guard contact != nil else { + return items + } - return [[myLang, switchMyLang], [sendingLang, switchSendingLang]] + items.append([makeTranscribeLanguageModel(), + ActionCellViewModel(title: String.localizable.lsTrancribeDescription1(contact?.fullName ?? "")), + makeTranscribeModel(), + ActionCellViewModel(title: String.localizable.lsTrancribeDescription2), + makeTranslateTranscribeModel(), + ActionCellViewModel(title: String.localizable.lsTrancribeDescription3)]) + return items } - private func makeMyLangModel() -> CellModel { - let myLang = DirectableActionCellViewModel(title: self.myLang.title()) - let myLangAction: ()->Void = { [weak self, weak myLang] in - guard let currentLang = self?.myLang else { return } - - let input = LanguageSelector.Input(selectorType: .my, - selectedLang: currentLang, - selectorLang: { [weak self] selected in - guard case let .lang(lang) = selected else { return } - myLang?.title = lang.name - self?.view.reloadData() - self?.myLang = selected - - self?.interactor.updateMyLanguage(lang) - }, - saveLangHandler: nil, - isNoneAvailable: false) - self?.wireFrame.showLanguageSelector(input: input) - } - myLang.action = myLangAction - return myLang - } - private func makeSwitchMyLangModel() -> CellModel { - let switchMyLang = SwitchableActionCellViewModel(isOn: myLangIsOn, title: String.localizable.lsAutoTOnReceipt) - let switchMyLangAction: ()->Void = { [weak self, weak switchMyLang] in - guard var isOn = self?.myLangIsOn else { return } - - isOn = !isOn + private func makeTranslationIncomingModel() -> CellModel { + let model = SwitchableActionCellViewModel(isOn: autoTranslate, + title: String.localizable.lsAutoTranslateInc) + + let action: () -> Void = { [weak self, weak model] in + guard let `self` = self else { return } - switchMyLang?.isOn = isOn - self?.myLangIsOn = isOn + let newValue = !self.autoTranslate - self?.interactor.updateAutotranslateReceipt(isOn) + self.autoTranslate = newValue + model?.isOn = newValue + self.interactor.update(autoTranslate: newValue) } - switchMyLang.action = switchMyLangAction - return switchMyLang + model.action = action + return model } - private func makeSendingLangModel(sectionCoordinator: LanguageSectionCoordinator) -> CellModel { - let sendingLang = DirectableActionCellViewModel(title: self.sendingLang.title()) - - sectionCoordinator.languagePicker = sendingLang - - let sendingLangAction: ()->Void = { [weak self, weak sendingLang] in - guard let currentLang = self?.sendingLang else { return } + private func makeTranslationOutgoingLanguageModel() -> CellModel { + let model = DirectableActionCellViewModel(title: outgoingTranslateLanguage.name.capitalized) + let action: () -> Void = { [weak self, weak model] in + guard let `self` = self else { return } - let input = LanguageSelector.Input(selectorType: .sending, - selectedLang: currentLang, - selectorLang: { [weak self] selectedLanguage in - guard let `self` = self, case .lang(let language) = selectedLanguage else { - return - } + let input = LanguageSelector.Input(selectorType: .translateOutgoing, + selectedLang: self.outgoingTranslateLanguage, + selectorLang: { [weak self] language in + guard let language = language else { return } - sendingLang?.title = language.name - if language.isNone { - sectionCoordinator.languageSwitch?.isOn = false - } + model?.title = language.name - self.view.reloadData() + self?.view.reloadData() - self.sendingLang = selectedLanguage + self?.outgoingTranslateLanguage = language - if language.isNone { - self.sendingLangIsOn = false - self.interactor.updateAutotranslateSending(false) - } - - self.interactor.updateSendingLanguage(language) + self?.interactor.update(outgoingTranslateLanguage: language) }, - saveLangHandler: nil, - isNoneAvailable: true) - - self?.wireFrame.showLanguageSelector(input: input) + saveLangHandler: nil) + self.wireFrame.showLanguageSelector(input: input) } - sendingLang.action = sendingLangAction - return sendingLang + model.action = action + return model } - private func makeSwitchSendingLangModel(sectionCoordinator: LanguageSectionCoordinator) -> CellModel { - let switchSendingLang = SwitchableActionCellViewModel(isOn: sendingLangIsOn, title: - String.localizable.lsAutoTOnSending) - - sectionCoordinator.languageSwitch = switchSendingLang - - let forceSendingLangAction: ()->Void = { [weak self, weak switchSendingLang] in - guard let currentLang = self?.sendingLang else { return } + private func makeTranslationOutgoingModel() -> CellModel { + let model = OptionallyActionCellViewModel(option: autoOutgoingTranslate, + title: String.localizable.lsAutoTranslateOut) + let action: () -> Void = { [weak self, weak model] in + self?.showTranslateOutgoingModes(relates: model) + } + model.action = action + return model + } + + private func makeTranscribeLanguageModel() -> CellModel { + let model = DirectableActionCellViewModel(title: transcribeLanguage.name.capitalized) + let action: () -> Void = { [weak self, weak model] in + guard let `self` = self else { return } - let input = LanguageSelector.Input(selectorType: .sending, - selectedLang: currentLang, - selectorLang: { [weak self] selected in - guard case .lang(let language) = selected, - !language.isNone else { - switchSendingLang?.isOn = false - self?.view.reloadData() - self?.sendingLangIsOn = false - return - } - - let isOn = true + let input = LanguageSelector.Input(selectorType: .transcribe, + selectedLang: self.transcribeLanguage, + selectorLang: { [weak self] language in + guard let language = language else { return } - sectionCoordinator.languagePicker?.title = language.name - switchSendingLang?.isOn = isOn + model?.title = language.name self?.view.reloadData() - self?.sendingLang = selected - self?.sendingLangIsOn = isOn + self?.transcribeLanguage = language - self?.interactor.updateSendingLanguage(language) - self?.interactor.updateAutotranslateSending(isOn) + self?.interactor.update(transcribeLanguage: language) }, - saveLangHandler: nil, - isNoneAvailable: true) + saveLangHandler: nil) + self.wireFrame.showLanguageSelector(input: input) + } + model.action = action + return model + } + + private func makeTranscribeModel() -> CellModel { + let model = SwitchableActionCellViewModel(isOn: autoTranscribe, + title: String.localizable.lsAutoTranscribeVoice) + let action: () -> Void = { [weak self, weak model] in + guard let `self` = self else { return } - self?.wireFrame.showLanguageSelector(input: input) + let newValue = !self.autoTranscribe + + self.autoTranslate = newValue + model?.isOn = newValue + self.interactor.update(autoTranscribe: newValue) } + model.action = action + return model + } + + private func makeTranslateTranscribeModel() -> CellModel { + let model = SwitchableActionCellViewModel(isOn: autoTranslateTranscribe, + title: String.localizable.lsAutoTranslateVoice) + let action: () -> Void = { [weak self, weak model] in + guard let `self` = self else { return } + + let newValue = !self.autoTranslateTranscribe + + self.autoTranslate = newValue + model?.isOn = newValue + self.interactor.update(autoTranslateTranscribe: newValue) + } + model.action = action + return model + } + + private func showTranslateOutgoingModes(relates to: OptionallyActionCellViewModel?) { - let switchSendingAction: ()->Void = { [weak self, weak switchSendingLang] in - guard let currentLang = self?.sendingLang, - case .lang(let language) = currentLang, - !language.isNone else { - forceSendingLangAction() + let updateHandler: (AutoOutgoingTranslate) -> Void = { [weak self] mode in + guard self?.autoOutgoingTranslate != mode else { return } + to?.option = mode + self?.view.reloadData() - guard var isOn = self?.sendingLangIsOn else { return } - - isOn = !isOn - - switchSendingLang?.isOn = isOn - self?.sendingLangIsOn = isOn + self?.autoOutgoingTranslate = mode - self?.interactor.updateAutotranslateSending(isOn) + self?.interactor.update(autoOutgoingTranslate: mode) } - switchSendingLang.action = switchSendingAction - return switchSendingLang + + let offAction = UIAlertAction(title: String.localizable.lsModeOff, style: .default) { _ in + updateHandler(.off) + } + let manualAction = UIAlertAction(title: String.localizable.lsModeManual, style: .default) { _ in + updateHandler(.manual) + } + let autoAction = UIAlertAction(title: String.localizable.lsModeAuto, style: .default) { _ in + updateHandler(.auto) + } + let cancelAction = UIAlertAction(title: String.localizable.cancel, style: .cancel) + + AlertManager.sharedInstance.showActionSheet(title: String.localizable.lsModeSheetTitle, message: nil, actions: [autoAction, manualAction, offAction, cancelAction]) } } diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/Presenter/LanguageSectionCoordinator.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/Presenter/LanguageSectionCoordinator.swift deleted file mode 100644 index 66eccb15a..000000000 --- a/Nynja/Modules/LanguageSettings/LanguageSettings/Presenter/LanguageSectionCoordinator.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// LanguageSectionCoordinator.swift -// Nynja -// -// Created by Andrey Reznik on 29.07.2018. -// Copyright © 2018 TecSynt Solutions. All rights reserved. -// - -import Foundation - -class LanguageSectionCoordinator { - var languagePicker: DirectableActionCellViewModel? - var languageSwitch: SwitchableActionCellViewModel? -} diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/View/ChatLanguageSettingsViewController.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/View/ChatLanguageSettingsViewController.swift index 5824774d2..d2e7736e4 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettings/View/ChatLanguageSettingsViewController.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/View/ChatLanguageSettingsViewController.swift @@ -23,9 +23,12 @@ class ChatLanguageSettingsViewController: BaseVC, ChatLanguageSettingsViewProtoc table.delegate = self table.register(DirectableActionCell.self, forCellReuseIdentifier: DirectableActionCell.cellId) table.register(SwitchableActionCell.self, forCellReuseIdentifier: SwitchableActionCell.cellId) + table.register(OptionallyActionCell.self, forCellReuseIdentifier: OptionallyActionCell.cellId) + table.register(DescriptionCell.self, forCellReuseIdentifier: DescriptionCell.cellId) table.backgroundColor = UIColor.nynja.clear table.separatorStyle = .none - table.bounces = false + table.estimatedRowHeight = 100 + table.rowHeight = UITableViewAutomaticDimension self.view.addSubview(table) table.snp.makeConstraints({ (make) in make.top.equalTo(navigationView.snp.bottom) @@ -78,11 +81,8 @@ class ChatLanguageSettingsViewController: BaseVC, ChatLanguageSettingsViewProtoc } } - //MARK: - UITableViewDelegate - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return Constraints.Table.cellHeight - } + //MARK: - UITableViewDelegate func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { guard let dataSource = tableViewDataSource else { diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/ActionCell.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/ActionCell.swift index 2ba3705f1..08a07e720 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/ActionCell.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/ActionCell.swift @@ -58,25 +58,28 @@ class ActionCell: BaseCell { extension ActionCell { - struct Constraints { + enum Constraints { static let height: CGFloat = 44.0 - struct titleLabel { + enum titleLabel { static let width: CGFloat = 340.0 - static let height: CGFloat = 22.0 + static let height: CGFloat = 28.0 + + static let horizontalInset = 8.0 static let leftInset = 16.0 + static let rightInset = 8.0 static let heightProportion = height / width } - struct containerView { + enum containerView { static let width = 24.0 static let rightInset = 16.0 } - struct separatorView { + enum separatorView { static let height = 1.0 static let horizontalInset = 16.0 @@ -89,20 +92,22 @@ extension ActionCell { private extension ActionCell { func makeTitleLabel() -> UILabel { - let width = Constraints.titleLabel.width.adjustedByWidth - let height = width * Constraints.titleLabel.heightProportion - - let label = UILabel(size: CGSize(width: width, height: height), color: UIColor.nynja.white, fontName: FontFamily.NotoSans.regular.name) - + let label = UILabel() + label.textColor = UIColor.nynja.white + label.font = UIFont(font: FontFamily.NotoSans.medium, size: 16.0) + label.backgroundColor = .clear + label.numberOfLines = 0 contentView.addSubview(label) let gestureTap = UITapGestureRecognizer(target: self, action:#selector(self.cellTapped(_:))) self.addGestureRecognizer(gestureTap) label.snp.makeConstraints({ (make) in - make.width.equalTo(width) + make.top.equalToSuperview().offset(Constraints.titleLabel.horizontalInset.adjustedByWidth) + make.bottom.equalToSuperview().offset(-Constraints.titleLabel.horizontalInset.adjustedByWidth) make.centerY.equalToSuperview() make.left.equalTo(Constraints.titleLabel.leftInset.adjustedByWidth) + make.right.equalTo(containerView.snp.left).offset(-Constraints.titleLabel.rightInset.adjustedByWidth) }) return label @@ -115,8 +120,7 @@ private extension ActionCell { contentView.addSubview(view) view.snp.makeConstraints({ (make) in make.centerY.equalToSuperview() -// make.width.height.equalTo(Constraints.containerView.width.adjustedByWidth) - make.right.equalTo(-Constraints.containerView.rightInset.adjustedByWidth) + make.right.equalToSuperview().offset(-Constraints.containerView.rightInset.adjustedByWidth) }) return view diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/ActionCellViewModel.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/ActionCellViewModel.swift index ea9a9809f..8f2ce910c 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/ActionCellViewModel.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/ActionCellViewModel.swift @@ -7,7 +7,7 @@ // class ActionCellViewModel: CellModel { - var title: String? + var title: String var action: (()->Void)? init(title: String, action: (()->Void)? = nil) { diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/DescriptionCell.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/DescriptionCell.swift new file mode 100644 index 000000000..2c2beb41f --- /dev/null +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/DescriptionCell.swift @@ -0,0 +1,22 @@ +// +// DescriptionCell.swift +// Nynja +// +// Created by Andrey Reznik on 09.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +class DescriptionCell: ActionCell, ConfigurableCell { + + static var cellId: String = "DescriptionCell" + static var accessibilityPrefix: String = "description_cell" + + //MARK: - Setup + + override func baseSetup() { + super.baseSetup() + titleLabel.font = UIFont(font: FontFamily.NotoSans.regular, size: 14) + titleLabel.textColor = UIColor.nynja.manatee + separatorView.isHidden = true + } +} diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/OptionallyActionCell.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/OptionallyActionCell.swift new file mode 100644 index 000000000..967beefb9 --- /dev/null +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/OptionallyActionCell.swift @@ -0,0 +1,93 @@ +// +// OptionallyActionCell.swift +// Nynja +// +// Created by Andrey Reznik on 09.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +class OptionallyActionCell: ActionCell, ConfigurableCell { + + static var cellId: String = "OptionallyActionCell" + static var accessibilityPrefix: String = "optionally_action_cell" + + + //MARK: - Subviews + + lazy var directImageView: UIImageView = makeDirectImageView() + lazy var optionLabel: UILabel = makeOptionLabel() + + //MARK: - Setup + + override func baseSetup() { + super.baseSetup() + optionLabel.isHidden = false + } + + override func setup(with model: CellModel) { + super.setup(with: model) + guard let viewModel = viewModel as? OptionallyActionCellViewModel else { return } + optionLabel.text = viewModel.option.rawValue.capitalized + } + + + //MARK: - Actions + + @objc override func cellTapped(_ recognizer: UITapGestureRecognizer) { + super.cellTapped(recognizer) + } +} + +extension OptionallyActionCell { + + enum Constraints { + + enum optionLabel { + static let width = 65.0 + static let rightInset = 0.0 + } + + enum directImageView { + static let width = 24.0 + static let rightInset = 16.0 + } + } +} + +private extension OptionallyActionCell { + + func makeOptionLabel() -> UILabel { + let label = UILabel() + label.textColor = UIColor.nynja.manatee + label.font = UIFont(font: FontFamily.NotoSans.regular, size: 16.0) + label.backgroundColor = .clear + label.textAlignment = .right + containerView.addSubview(label) + + label.snp.makeConstraints({ (make) in + make.centerY.equalToSuperview() + make.width.equalTo(Constraints.optionLabel.width.adjustedByWidth) + make.left.equalToSuperview() + make.right.equalTo(directImageView.snp.left).offset(Constraints.optionLabel.rightInset.adjustedByWidth) + }) + + return label + } + + func makeDirectImageView() -> UIImageView { + let imageView = UIImageView() + imageView.image = UIImage.nynja.icArrowRight.image + imageView.tintColor = UIColor.nynja.manatee + + containerView.addSubview(imageView) + imageView.snp.makeConstraints({ (make) in + make.width.height.equalTo(Constraints.directImageView.width.adjustedByWidth) + make.centerY.equalToSuperview() + make.right.equalToSuperview() + }) + + return imageView + } +} diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/OptionallyActionCellViewModel.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/OptionallyActionCellViewModel.swift new file mode 100644 index 000000000..dfeec8232 --- /dev/null +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/OptionallyActionCellViewModel.swift @@ -0,0 +1,18 @@ +// +// OptionallyActionCellViewModel.swift +// Nynja +// +// Created by Andrey Reznik on 09.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +class OptionallyActionCellViewModel: ActionCellViewModel { + var option: AutoOutgoingTranslate + + init(option: AutoOutgoingTranslate = .off, title: String, action: (()->Void)? = nil) { + self.option = option + super.init(title: title, action: action) + } +} diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/SwitchableActionCell.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/SwitchableActionCell.swift index 5d88d4778..dffcafc9f 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/SwitchableActionCell.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/Cell/SwitchableActionCell.swift @@ -22,6 +22,7 @@ class SwitchableActionCell: ActionCell, ConfigurableCell { override func baseSetup() { super.baseSetup() toggle.isHidden = false + titleLabel.numberOfLines = 2 } override func setup(with model: CellModel) { @@ -42,13 +43,20 @@ class SwitchableActionCell: ActionCell, ConfigurableCell { //MARK: - Actions @objc override func cellTapped(_ recognizer: UITapGestureRecognizer) { -// toggle.setOn(!toggle.isOn, animated: true) -// super.cellTapped(recognizer) guard let viewModel = viewModel as? SwitchableActionCellViewModel else { return } viewModel.wrapAction?() } } +extension SwitchableActionCell { + + enum Constraints { + + enum `switch` { + static let width = 51.0 + } + } +} private extension SwitchableActionCell { @@ -59,6 +67,7 @@ private extension SwitchableActionCell { containerView.addSubview(toggle) toggle.snp.makeConstraints({ (make) in + make.width.equalTo(Constraints.switch.width) make.edges.equalToSuperview() }) return toggle diff --git a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/ChatLanguageSettingsTableDataSource.swift b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/ChatLanguageSettingsTableDataSource.swift index 648665a3f..d61e5010b 100644 --- a/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/ChatLanguageSettingsTableDataSource.swift +++ b/Nynja/Modules/LanguageSettings/LanguageSettings/View/TableView/ChatLanguageSettingsTableDataSource.swift @@ -45,6 +45,18 @@ class ChatLanguageSettingsTableDataSource: NSObject, UITableViewDataSource { cell.setupAccessibility(at: indexPath, sectionLabel: section.headerId) return cell } + + if model is OptionallyActionCellViewModel, let cell = tableView.dequeueReusableCell(withIdentifier: OptionallyActionCell.cellId) as? OptionallyActionCell { + cell.setup(with: model) + cell.setupAccessibility(at: indexPath, sectionLabel: section.headerId) + return cell + } + if model is ActionCellViewModel, let cell = tableView.dequeueReusableCell(withIdentifier: DescriptionCell.cellId) as? DescriptionCell { + cell.setup(with: model) + cell.setupAccessibility(at: indexPath, sectionLabel: section.headerId) + return cell + } + return UITableViewCell() } } diff --git a/Nynja/Modules/Main/MainProtocols.swift b/Nynja/Modules/Main/MainProtocols.swift index acb80b2db..98d9743d6 100644 --- a/Nynja/Modules/Main/MainProtocols.swift +++ b/Nynja/Modules/Main/MainProtocols.swift @@ -96,6 +96,7 @@ protocol MainWireFrameProtocol: class { func showAddParticipants() func showGroupsList() func showGroupsOptions() + func showChatOptions() // Channel func showNewChannel() @@ -104,6 +105,8 @@ protocol MainWireFrameProtocol: class { // Conference calling func getRoom() -> Room? + + func notifyAvailability(snackBar: SnackBar, isAvailable: Bool) } protocol MainViewProtocol: WheelOutProtocol { @@ -128,6 +131,8 @@ protocol MainViewProtocol: WheelOutProtocol { func showUILocker() func hideUILocker() + + func notifyAvailability(snackBar: SnackBar, isAvailable: Bool) } protocol MainPresenterProtocol: class { @@ -190,6 +195,7 @@ protocol MainPresenterProtocol: class { func showAddParticipants() func showGroupsList() func showGroupsOptions() + func showChatOptions() func showMarketplace() func showMySelfChat() diff --git a/Nynja/Modules/Main/Presenter/MainPresenter.swift b/Nynja/Modules/Main/Presenter/MainPresenter.swift index aeb015141..5292588ec 100644 --- a/Nynja/Modules/Main/Presenter/MainPresenter.swift +++ b/Nynja/Modules/Main/Presenter/MainPresenter.swift @@ -279,6 +279,10 @@ class MainPresenter: MainPresenterProtocol, MainInteractorOutputProtocol, Schedu func showGroupsOptions() { self.wireFrame.showGroupsOptions() } + + func showChatOptions() { + self.wireFrame.showChatOptions() + } func showUILocker() { view?.showUILocker() diff --git a/Nynja/Modules/Main/View/MainNavigationItem.swift b/Nynja/Modules/Main/View/MainNavigationItem.swift index 4f29d1c0a..7fd4c3064 100644 --- a/Nynja/Modules/Main/View/MainNavigationItem.swift +++ b/Nynja/Modules/Main/View/MainNavigationItem.swift @@ -63,6 +63,7 @@ enum MainNavigationItem: String { case groups = "wheel_item_groups" case newGroup = "wheel_item_newGroup" case groupOptions = "wheel_item_groupOptions" + case chatOptions = "wheel_item_chatOptions" // New Group section case notifications = "wheel_item_notifications" diff --git a/Nynja/Modules/Main/View/MainViewController+Gallery.swift b/Nynja/Modules/Main/View/MainViewController+Gallery.swift index dc7784d45..353b7bbcd 100644 --- a/Nynja/Modules/Main/View/MainViewController+Gallery.swift +++ b/Nynja/Modules/Main/View/MainViewController+Gallery.swift @@ -111,8 +111,8 @@ extension MainViewController { self.handleImage(asset: asset) } - self.closeTopLevelItem(at: indexPath) - self.closeWheel(indexPath: indexPath) + let currentIndexPath = indexPath?.appending(params.index) + self.closeWheel(indexPath: currentIndexPath) } } } diff --git a/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift b/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift index 3007c5e50..23e4a8d39 100644 --- a/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift +++ b/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift @@ -96,6 +96,11 @@ extension MainViewController: NavigateProtocol { closeWheel(indexPath: indexPath) } + func showChatOptions(indexPath: IndexPath?) { + presenter.showChatOptions() + closeWheel(indexPath: indexPath) + } + func showMessages(indexPath: IndexPath?) { closeWheel(indexPath: indexPath) } @@ -160,9 +165,7 @@ extension MainViewController: NavigateProtocol { } func showMedia(indexPath: IndexPath?) { - expand(by: indexPath, indexOfItem: 1) { [weak self] indexPath in - self?.showRecentsMedia(indexPath: indexPath) - } + self.showGallery(indexPath: indexPath) } diff --git a/Nynja/Modules/Main/View/MainViewController.swift b/Nynja/Modules/Main/View/MainViewController.swift index 3a1cc6589..36d8fc95f 100644 --- a/Nynja/Modules/Main/View/MainViewController.swift +++ b/Nynja/Modules/Main/View/MainViewController.swift @@ -255,12 +255,10 @@ class MainViewController: BaseVC, MainViewProtocol, HitTestDelegate, UINavigatio self.presenter.viewShowed() viewShowed = true - self.view.bringSubview(toFront: gradientView) - self.view.bringSubview(toFront: container) - self.view.bringSubview(toFront: nextButton) - self.view.bringSubview(toFront: safeAreaView) - - + view.bringSubview(toFront: gradientView) + view.bringSubview(toFront: container) + view.bringSubview(toFront: nextButton) + if view.subviews.contains(uiLocker) { self.view.bringSubview(toFront: uiLocker) } @@ -329,7 +327,7 @@ class MainViewController: BaseVC, MainViewProtocol, HitTestDelegate, UINavigatio make.centerX.equalTo(self.view.snp.leading).offset(centerX) } - make.centerY.equalTo(self.view.keyboardLayoutGuide.snp.top).offset(-centerX) + make.centerY.equalTo(self.view.snackBarLayoutGuide.snp.top).offset(-centerX) } } @@ -703,4 +701,13 @@ class MainViewController: BaseVC, MainViewProtocol, HitTestDelegate, UINavigatio setupSafeAreaView(with: newSettings) } + + func notifyAvailability(snackBar: SnackBar, isAvailable: Bool) { + if !isAvailable && snackBar.delegates.containsDelegate(view.snackBarLayoutGuide) { + snackBar.delegates.removeDelegate(view.snackBarLayoutGuide) + } else { + snackBar.delegates.addDelegate(view.snackBarLayoutGuide) + } + + } } diff --git a/Nynja/Modules/Main/View/MainViewControllerLayout.swift b/Nynja/Modules/Main/View/MainViewControllerLayout.swift index c2da53636..50a870d2d 100644 --- a/Nynja/Modules/Main/View/MainViewControllerLayout.swift +++ b/Nynja/Modules/Main/View/MainViewControllerLayout.swift @@ -9,20 +9,20 @@ extension MainViewController { // MARK: For iPhone 6+ - struct Constraints { + enum Constraints { - struct gradientView { + enum gradientView { static let height = 100.0.adjustedByWidth } - struct nextButton { + enum nextButton { static let collapsedWidth: CGFloat = 68.0 static let expandedWidth: CGFloat = 136.0 static let centerX: CGFloat = 50.0 } - struct partnerView { + enum partnerView { static let width = 106.0 static let height = 186.0 @@ -30,7 +30,7 @@ extension MainViewController { static let trailingInset = 7.0 } - struct container { + enum container { static let width = 910.0 struct firstWheel { @@ -44,29 +44,37 @@ extension MainViewController { } } - struct inputView { + enum inputView { static let height = 43.0 static let horizontalInset = 16.0 } - struct inputBar { + enum inputBar { static let height: CGFloat = 100.0 - struct leftButton { + enum leftButton { static let width = 25.0 } - struct rightButton { + enum rightButton { static let width = 100.0 static let height = 42.0 } } - struct returnButton { + enum returnButton { static let height = 100.0 } + + enum snackBar { + enum height { + static let collapsed: CGFloat = 0 + static let expanded: CGFloat = CGFloat(50.0).adjustedByWidth + } + + } } } diff --git a/Nynja/Modules/Main/View/NavigateProtocol.swift b/Nynja/Modules/Main/View/NavigateProtocol.swift index b00a29af4..8b2368203 100644 --- a/Nynja/Modules/Main/View/NavigateProtocol.swift +++ b/Nynja/Modules/Main/View/NavigateProtocol.swift @@ -32,7 +32,7 @@ protocol SecondLevelNavigateProtocol: class { func showRecentGroupChats(indexPath: IndexPath?) func showCreateGroup(indexPath: IndexPath?) func showGroupOptions(indexPath: IndexPath?) - + func showChatOptions(indexPath: IndexPath?) func showMessages(indexPath: IndexPath?) // MARK: - Chat Actions diff --git a/Nynja/Modules/Main/WireFrame/MainWireframe.swift b/Nynja/Modules/Main/WireFrame/MainWireframe.swift index 4ea1cf427..cbb1e674e 100644 --- a/Nynja/Modules/Main/WireFrame/MainWireframe.swift +++ b/Nynja/Modules/Main/WireFrame/MainWireframe.swift @@ -550,6 +550,10 @@ class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate { func showGroupsOptions() { SettingsGroupWireFrame().present(navigation: contentNavigation, main: self) } + + func showChatOptions() { + OtherUserWireFrame().present(navigation: contentNavigation, main: self) + } // MARK: Chats p2p @@ -622,4 +626,7 @@ class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate { animated: animated) } + func notifyAvailability(snackBar: SnackBar, isAvailable: Bool) { + view?.notifyAvailability(snackBar: snackBar, isAvailable: isAvailable) + } } diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor+AutoConversion.swift b/Nynja/Modules/Message/Interactor/MessageInteractor+AutoConversion.swift new file mode 100644 index 000000000..d4c241508 --- /dev/null +++ b/Nynja/Modules/Message/Interactor/MessageInteractor+AutoConversion.swift @@ -0,0 +1,140 @@ +// +// MessageInteractor+AutoConversion.swift +// Nynja +// +// Created by Andrey Reznik on 10.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + + +protocol AutoConversion { + + var conversionCheckpoint: Int64? { get } + + func performAutoConversion(new message: Message) -> Bool + func performAutoConversion(exist message: Message) + + func performAutoConversion(for messages: [Message]) +} + +extension MessageInteractor: AutoConversion { + + var conversionCheckpoint: Int64? { + return initialSelfReader + } + + @discardableResult + func performAutoConversion(new message: Message) -> Bool { + guard let checkpoint = conversionCheckpoint, + message.isСonvertible(relatively: checkpoint) else { + return false + } + + if message.isTranslatable { + if conversationLanguageSettingService.autoTranslate { + performTranslation(for: message) + } + } else if chat is Contact && message.isTranscribable { + var localStatus: Message.LocalStatus = [] + if conversationLanguageSettingService.autoTranslateTranscribe { + if !messageParser.parse(message, to: .transcription).isEmpty { + performTranslation(for: message) + } else { + localStatus = localStatus.union(.translate) + } + } + if conversationLanguageSettingService.autoTranscribe { + ///Check if message exsist in local storage + if message.mainUrl?.mediaURL != nil { + performTranscription(for: message) + } else { + localStatus = localStatus.union(.transcribe) + } + } + update(message: message, with: localStatus) + } + return true + } + + func performAutoConversion(exist message: Message) { + guard message.isСonvertible else { + return + } + if message.isTranslatable { + if conversationLanguageSettingService.autoTranslate { + performTranslation(for: message, translateIfNotExist: false) + } + } else if chat is Contact && message.isTranscribable { + guard let status = message.localStatus else { + return + } + if status.contains(.transcribe) { + if message.mainUrl?.mediaURL != nil { + performTranscription(for: message) + } + } + if status.contains(.translate) { + if !messageParser.parse(message, to: .transcription).isEmpty { + performTranslation(for: message) + } + } + } + } + + func performAutoConversion(for messages: [Message]) { + messages.forEach { + if !performAutoConversion(new: $0) { + performAutoConversion(exist: $0) + } + } + } +} + +extension AutoConversion { + func update(message: Message, with localStatus: Message.LocalStatus) { + var status: Message.LocalStatus = [] + if let currentStatus = message.localStatus { + status = currentStatus + } + status = status.union(localStatus) + message.localStatus = status + MessageDAO.updateColumns([.localStatus], message: message) + } + + func remove(localStatus: Message.LocalStatus, from message: Message) { + guard var status = message.localStatus, + status.contains(localStatus) else { + return + } + status.remove(localStatus) + message.localStatus = status + MessageDAO.updateColumns([.localStatus], message: message) + } +} + +private extension MessageInteractor { + func performTranslation(for message: Message, translateIfNotExist: Bool = true) { + let translation = messageParser.parse(message, to: .translation(.convenientToMe)).first?.payload + + if translation == nil && !translateIfNotExist { + return + } + + let text = messageParser.parse(message, to: .transcription).first?.payload ?? message.mainFile?.payload + + if translation != text { + translate(message: message) + } + } + + func performTranscription(for message: Message) { + guard let localId = message.msg_id, + messageParser.parse(message, to: .transcription).isEmpty else { + return + } + + transcribeMessage(localId: localId, isAuto: true) + } +} diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift b/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift index 222a5863d..ace2b8b33 100644 --- a/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift +++ b/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift @@ -211,9 +211,10 @@ extension MessageInteractor { configuration.messages[index] = message if oldMessage.isDelivered { - if message.isEdited { - autoTranslateReceiptMessageIfNeeded(message) + if message.isEdited || message.files?.count != oldMessage.files?.count { + performAutoConversion(exist: message) } + removeProgressIfNeeded(for: message) let config = createMessageConfiguration(message) presenter?.updateMessage(with: config) @@ -222,6 +223,7 @@ extension MessageInteractor { presenter?.scrollToBottomIfNeeded() } } else if message.files?.count != oldMessage.files?.count { + removeProgressIfNeeded(for: message) let config = createMessageConfiguration(message) presenter?.updateMessage(with: config) } else { @@ -250,7 +252,7 @@ extension MessageInteractor { chat.unread = 0 initialUnreadCount = 0 } else { - autoTranslateReceiptMessageIfNeeded(message) + performAutoConversion(new: message) config = createMessageConfiguration(message) @@ -284,7 +286,7 @@ extension MessageInteractor { } var progressModel: ProgressModel? - if let url = message.mainUrl { + if message.isDownloadable, let url = message.mainUrl { let model = processingManager.progressFor(url) configuration.progressModels[model.url] = model progressModel = model diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor+Transcription.swift b/Nynja/Modules/Message/Interactor/MessageInteractor+Transcription.swift index 7370ba72d..47b63fdc1 100644 --- a/Nynja/Modules/Message/Interactor/MessageInteractor+Transcription.swift +++ b/Nynja/Modules/Message/Interactor/MessageInteractor+Transcription.swift @@ -15,10 +15,9 @@ protocol Transcription { var transcriptionService: TranscribeServiceProtocol { get } var conversationLanguageSettingService: ConversationLanguageSettingServiceProtocol { get } - func transcribeMessage(localId: String, lang: SelectedLang?) + func transcribeMessage(localId: String, language: Language?, isAuto: Bool) func cancelTransсribing(localId: String) func untranscribeMessage(localId: String) - func chooseTranscriptionLanguageForMessage(with localId: String, lang: SelectedLang) func subscribeToTranscribeProcessing() func unsubscribeFromTranscribeProcessing() @@ -26,10 +25,27 @@ protocol Transcription { func fetchConversionProgress(with id: String) -> ConvertionProgressModel? } +extension Transcription { + + func transcribeMessage(localId: String, language: Language? = nil, isAuto: Bool = false) { + transcribeMessage(localId: localId, language: language, isAuto: isAuto) + } +} + +enum TranscriptionState: ConversionState { + case failed + + var text: String { + switch self { + case .failed: + return String.localizable.tpTranscribingFailed + } + } +} extension MessageInteractor: Transcription { - func transcribeMessage(localId: String, lang: SelectedLang?) { + func transcribeMessage(localId: String, language: Language?, isAuto: Bool) { guard let message = messageBy(localId: localId), message.sendType == .audio, let url = message.mainUrl, @@ -38,40 +54,38 @@ extension MessageInteractor: Transcription { return } - var language = message.mainFile?.language ?? conversationLanguageSettingService.chatLanguage.lang - if case let .lang(extendedLang)? = lang { - language = extendedLang.language - } - + let language = language?.language ?? (message.isOwn ? conversationLanguageSettingService.myDefaultLanguage.language : conversationLanguageSettingService.transcribeLanguage.language) + transcriptionService.transcribe(.init(message: message, localUrl: fileUrl, - language: language)) - } - - func cancelTransсribing(localId: String) { - cancelTransсribing(localId: localId, isCancelOperation: true) + language: language, + isAuto: isAuto)) } func untranscribeMessage(localId: String) { - guard let message = messageBy(localId: localId), + guard let message = messageBy(localId: localId)?.clone, let transcribeId = messageParser.parse(message, to: .transcription).first?.id else { - assertionFailure("Something went wrong") return } + remove(localStatus: .transcribe, from: message) + let translateId = messageParser.parse(message, to: .translation(.convenientToMe)).first?.id - let untranscribedMessage = messageFactory.makeUntranscribedMessage(message: message, - transcriptionId: transcribeId, - translationId: translateId) + + let untranscribedMessage = messageFactory.makeUntranscribedMessage( + message: message, + transcriptionId: transcribeId, + translationId: translateId) + processingManager.uploadMessage(untranscribedMessage) } - func chooseTranscriptionLanguageForMessage(with localId: String, lang: SelectedLang) { - transcribeMessage(localId: localId, lang: lang) + func cancelTransсribing(localId: String) { + cancelTransсribing(localId: localId, isCancelOperation: true) } func subscribeToTranscribeProcessing() { - transcriptionService.observeState(self) { [weak self] localId, state in + transcriptionService.observeState(self) { [weak self] localId, state, isAuto in guard let `self` = self else { return } @@ -84,19 +98,18 @@ extension MessageInteractor: Transcription { status: .atProgress) self.configuration.transcribingModels[localId] = convertionModel - self.updateConvertionProgress(convertionModel) + self.presenter?.updateConversion(with: convertionModel) case .success: self.irreversibleTransсribing(localId: localId) case .failure(let error): switch error { case .emptyResponse(let language): - self.stopTranscribing(localId: localId) - self.presenter?.showChooseAnotherLanguageAlert(localId: localId, language: language) + self.finishTranscription(for: localId, with: language, isAuto: isAuto) default: - break + self.presenter?.updateConversion(with: TranscriptionState.failed) } case .unknown: - self.stopTranscribing(localId: localId) + self.cancelTransсribing(localId: localId, isCancelOperation: false) case .cancel: break } @@ -121,11 +134,23 @@ private extension MessageInteractor { } convertionModel.status = .irreversible - updateConvertionProgress(convertionModel) + presenter?.updateConversion(with: convertionModel) } - func stopTranscribing(localId: String) { + func finishTranscription(for localId: String, with language: String, isAuto: Bool) { cancelTransсribing(localId: localId, isCancelOperation: false) + + if isAuto { + presenter?.updateConversion(with: TranscriptionState.failed) + } else { + presenter?.showChooseAnotherLanguageAlert(localId: localId, + language: .init(language: language)) + } + + guard let message = messageBy(localId: localId) else { + return + } + remove(localStatus: .transcribe, from: message) } func cancelTransсribing(localId: String, isCancelOperation: Bool = true) { @@ -138,11 +163,8 @@ private extension MessageInteractor { } convertionModel.status = .initial - updateConvertionProgress(convertionModel) + presenter?.updateConversion(with: convertionModel) + configuration.translatingModels.removeValue(forKey: localId) } - - func updateConvertionProgress(_ progress: ConvertionProgressModel) { - self.presenter?.updateTranslationProgress(progress) - } } diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor+Translation.swift b/Nynja/Modules/Message/Interactor/MessageInteractor+Translation.swift index 70c4d3e25..0551903b9 100644 --- a/Nynja/Modules/Message/Interactor/MessageInteractor+Translation.swift +++ b/Nynja/Modules/Message/Interactor/MessageInteractor+Translation.swift @@ -14,30 +14,36 @@ protocol Translation { func translate(input: TranslationInput, handler: @escaping TranslationHandler) -> CancelAction? - func translateMessage(localId: String, lang: SelectedLang?, completion: TranslationComletion?) - func cancelTranslating(localId: String) + func translateMessage(localId: String, language: Language?, handler: TranslationHandler?) func untranslateMessage(localId: String) - func chooseTranslationLanguageForMessage(with localId: String, lang: SelectedLang) - func setupMyLanguage(lang: SelectedLang) - func setupSendingLanguage(lang: SelectedLang) + var myLanguage: Language { get } + var sendingLanguage: Language { get set } +} + +extension Translation { - func getMySelectedLang() -> SelectedLang + func translateMessage(localId: String, language: Language? = nil, handler: TranslationHandler? = nil) { + translateMessage(localId: localId, language: language, handler: handler) + } } typealias CancelAction = ()->Void -typealias TranslationHandler = (TranslationMentionedOutput) -> Void -typealias TranslationComletion = (TranslationOutput) -> Void + +struct TranslationPreviewConfig { + let language: Language + let mode: AutoOutgoingTranslate +} struct TranslationInput { let text: String - let language: SelectedLang + let language: Language let mentions: [Mention] } struct TranslationMentionedOutput { let text: String - let language: SelectedLang + let language: Language let mentions: [Mention] } @@ -47,26 +53,62 @@ struct TranslationOutput { let translatedLang: String } +enum TranslationState: ConversionState { + case failed + case equalLanguages + + var text: String { + switch self { + case .failed: + return String.localizable.tpTranslationFailed + case .equalLanguages: + return String.localizable.tpTranslationLanguageIsEqual + } + } +} + +enum TranslationResult { + case output(TranslationOutput) + case success(TranslationMentionedOutput) + case error(TranslationState) +} + +typealias TranslationHandler = (TranslationResult) -> Void + + extension MessageInteractor { - //MARK: - Helper + // MARK: - Helper var isAutotranslateOnReceipt: Bool { - return conversationLanguageSettingService.autoLanguageChat + return conversationLanguageSettingService.autoTranslate } var isAutotranslateOnSending: Bool { - return conversationLanguageSettingService.autoTranslateLanguage + return conversationLanguageSettingService.autoOutgoingTranslate == .auto } + var myLanguage: Language { + return conversationLanguageSettingService.myDefaultLanguage + } - //MARK: - Translation Protocol - func translate(input: TranslationInput, handler: @escaping TranslationHandler) -> CancelAction? { - guard case let .lang(language) = input.language else { - assertionFailure("Something went wrong with translation on sending") - return nil + var sendingLanguage: Language { + + get { + return conversationLanguageSettingService.feature(by: .outgoingTranslateLanguage).languageValue } + set { + let feature = conversationLanguageSettingService.feature(by: .outgoingTranslateLanguage) + feature.value = newValue.stringValue + update(with: feature) + } + } + + + // MARK: - Translation + + func translate(input: TranslationInput, handler: @escaping TranslationHandler) -> CancelAction? { let mentions = input.mentions.map { MentionInfo(mention: $0) } var attribute: [String: MentionInfo] = [:] @@ -74,210 +116,94 @@ extension MessageInteractor { return "\(MentionInfo.translationAlias)_\(mentionInfo.accountId)_\(mentionInfo.range.lowerBound.encodedOffset)" } - let result = MessagePayloadRenderer.processPlainTextPayload(into: &attribute, - with: mentions, - in: input.text, - replacement: replacement, - transform: { (range, mentionInfo) -> (String, MentionInfo) in - - return (replacement(mentionInfo), mentionInfo) + let result = MessagePayloadRenderer.processPlainTextPayload( + into: &attribute, + with: mentions, + in: input.text, + replacement: replacement, + transform: { (range, mentionInfo) -> (String, MentionInfo) in + + return (replacement(mentionInfo), mentionInfo) }, - updateResult: { (result, element) in - result[element.0] = element.1 + updateResult: { (result, element) in + result[element.0] = element.1 }) - let translationTask = translationService.translate(text: result.text, from: nil, to: language.language, completion: { (translatedText, error) in - guard error == nil else { - return - } - guard var translatedText = translatedText else { - assertionFailure("Something went wrong with translation on sending") - return - } - - var shiftedMentions: [Mention] = [] - attribute.keys.forEach { + let translationTask = translationService.translate( + text: result.text, + from: nil, + to: input.language.language, + timeout: TranslationService.Constants.translationTimeout) { (translatedText, error) in - if let mentionInfo = attribute[$0], let keyRange = translatedText.range(of: $0) { - translatedText.removeSubrange(keyRange) - - let text = "@\(mentionInfo.alias)" - translatedText.insert(contentsOf: text, at: keyRange.lowerBound) - - let attrRange = Range(keyRange.utf16Encoded.lowerBound.. SelectedLang { - let langSignature = conversationLanguageSettingService.chatLanguage - return SelectedLang(langSignature) - } - - - //MARK: - Autotranslate on receipt - - private func messagesForUpdateAutotranslateOnReceipt() -> [Message] { - guard let count = translationCount else { - return [] - } - let messages = configuration.messages - - let languageToTranslation = conversationLanguageSettingService.chatLanguage.lang - let index = messages.count - Int(count) - 1 - - guard messages.indices.contains(index) else { - return [] - } - - return messages[...index] - .filter { - guard $0.canBeTranslated, - let translationPayload = messageParser.parse($0, to: .translation(.language(languageToTranslation))).first?.payload, - translationPayload != $0.mainFile?.payload else { - return false - } - return true - } - } - - private func messagesForAutotranslateOnReceipt() -> [Message] { - guard let count = translationCount, count != 0 else { - return [] - } - let messages = configuration.messages - - let languageToTranslation = conversationLanguageSettingService.chatLanguage.lang - let index = messages.count - Int(count) - - guard messages.indices.contains(index) else { - return [] - } - - return messages[index...] - .filter { - $0.canBeTranslated && - messageParser.parse($0, to: .translation(.language(languageToTranslation))).isEmpty - } - } - - func autoTranslateReceiptMessageIfNeeded(_ message: Message) { - let languageToTranslation = conversationLanguageSettingService.chatLanguage.lang - - guard isAutotranslateOnReceipt, - message.canBeTranslated else { - return - } + remove(localStatus: .translate, from: message) - let translationPayload = messageParser.parse(message, to: .translation(.language(languageToTranslation))).first?.payload + let untranslatedMessage = messageFactory.makeUntranslatedMessage(message: message, + translationId: translationId) - if translationPayload == nil || translationPayload != message.mainFile?.payload { - translate(message) - } - } - - func autoTranslateReceiptMessagesIfNeeded() { - if isAutotranslateOnReceipt { - translate(messagesForUpdateAutotranslateOnReceipt()) - translate(messagesForAutotranslateOnReceipt()) - } - translationCount = chat.unreadCount + processingManager.uploadMessage(untranslatedMessage) } } -//MARK: - Private extension MessageInteractor { - //MARK: - Processing - - private func textForTranslation(from message: Message) -> String? { - guard let attach = message.mainFile, let mime = attach.mime, let type = SendMessageType(rawValue: mime) else { - return nil - } - - switch type { - case .transfer: - return attach.data?.first?.value - default: - return (messageParser.parse(message, to: .transcription).first?.payload ?? attach.payload) - } - } - - func translate(_ message: Message, lang: SelectedLang? = nil, completion: TranslationComletion? = nil) { + func translate(message: Message, language: Language? = nil, handler: TranslationHandler? = nil) { guard let id = message.msg_id, let text = textForTranslation(from: message) else { - return - } - - var traslationLanguage = conversationLanguageSettingService.chatLanguage.lang - if case let .lang(extendedLang)? = lang { - traslationLanguage = extendedLang.language + return } + + let translationLanguage = language?.language ?? conversationLanguageSettingService.myDefaultLanguage.language let mentions = payloadParser.parse(message) var attribute: [String: String] = [:] @@ -285,43 +211,54 @@ extension MessageInteractor { return "\(MentionInfo.translationAlias)_\(mentionInfo.accountId)" } - let result = MessagePayloadRenderer.processPlainTextPayload(into: &attribute, - with: mentions, - in: text, - replacement: replacement, - transform: { (range, mentionInfo) -> (String, String) in - let mention = Mention(indices: range, - memberId: mentionInfo.memberId, - accountId: mentionInfo.accountId, - alias: mentionInfo.alias) - let tag = MessagePayloadBuilder.buildTag(for: mention) - return (replacement(mentionInfo), tag) + let result = MessagePayloadRenderer.processPlainTextPayload( + into: &attribute, + with: mentions, + in: text, + replacement: replacement, + transform: { (range, mentionInfo) -> (String, String) in + let mention = Mention(indices: range, + memberId: mentionInfo.memberId, + accountId: mentionInfo.accountId, + alias: mentionInfo.alias) + let tag = MessagePayloadBuilder.buildTag(for: mention) + return (replacement(mentionInfo), tag) }, - updateResult: { (result, element) in - result[element.0] = element.1 + updateResult: { (result, element) in + result[element.0] = element.1 }) - let task = translationService.translate(text: result.text, from: nil, to: traslationLanguage, completion: { [weak self] (translatedText, error) in - guard var translatedText = translatedText else { - self?.cancelTranslating(localId: id) - return - } - self?.irreversibleTranslating(localId: id) - - attribute.keys.forEach { - translatedText = translatedText.replacingOccurrences(of: $0, with: attribute[$0]!) - } - - if let completion = completion { - let output = TranslationOutput(inputText: text, - translatedText: translatedText, - translatedLang: traslationLanguage) - completion(output) - } else { - guard let translatedMessage = self?.messageFactory.makeTranslatedMessage(message: message, text: text, translatedText: translatedText, lang: traslationLanguage) else { return } - self?.processingManager.uploadMessage(translatedMessage) - } - }) + let task = translationService.translate( + text: result.text, + from: nil, + to: translationLanguage, + timeout: TranslationService.Constants.translationTimeout) { [weak self] (translatedText, error) in + guard var translatedText = translatedText, !translatedText.isEmpty else { + handler?(.error(.failed)) + self?.finishTranslation(for: id, with: .failed) + return + } + + attribute.keys.forEach { + translatedText = translatedText.replacingOccurrences(of: $0, with: attribute[$0]!) + } + + guard result.text.trimmedTranslation != translatedText.trimmedTranslation else { + handler?(.error(.equalLanguages)) + self?.finishTranslation(for: id, with: .equalLanguages) + return + } + + if let handler = handler { + let output = TranslationOutput(inputText: text, + translatedText: translatedText, + translatedLang: translationLanguage) + handler(.output(output)) + } else { + guard let translatedMessage = self?.messageFactory.makeTranslatedMessage(message: message, text: text, translatedText: translatedText, lang: translationLanguage) else { return } + self?.processingManager.uploadMessage(translatedMessage) + } + } let translationModel = ConvertionProgressModel(id: id, type: .translate, @@ -330,34 +267,10 @@ extension MessageInteractor { configuration.translatingModels[id] = translationModel - updateTranslationProgress(translationModel) - } - - func translate(_ messages: [Message]) { - messages.forEach { translate($0) } + presenter?.updateConversion(with: translationModel) } - - //MARK: - Progress - - private func irreversibleTranslating(localId: String) { - guard let translationModel = configuration.translatingModels[localId] else { - return - } - translationModel.status = .irreversible - - updateTranslationProgress(translationModel) - } - - private func updateTranslationProgress(_ progress: ConvertionProgressModel) { - self.presenter?.updateTranslationProgress(progress) - } -} - -//MARK: - Setup -extension MessageInteractor { - - var settingOwner: Customizable? { + var settingStorage: LanguageSettingProtocol? { switch chat { case is Room: if let id = room?.id, @@ -369,32 +282,60 @@ extension MessageInteractor { return contact default: break - } + } return nil } func updateTranslationPreview() { - let sendingLanguage = conversationLanguageSettingService.translateLanguage - presenter?.updateTranslationPreview(selectedLang: SelectedLang(sendingLanguage), isAuto: isAutotranslateOnSending) - } - - func setupChatLanguageIfNeeded() { - guard conversationLanguageSettingService.featureOrNil(by: .chatLanguage) == nil else { - return - } - - let feature = Feature.languageFeature(LanguageSettingKey.chatLanguage.rawValue) - feature.value = Language.current.featureValue - update(with: feature) + let sendingLanguage = conversationLanguageSettingService.outgoingTranslateLanguage + let sendingMode = conversationLanguageSettingService.autoOutgoingTranslate + presenter?.updateTranslationPreview(with: .init(language: sendingLanguage, + mode: sendingMode)) } func update(with feature: Feature) { - if let contact = conversationLanguageSettingService.settingOwner as? Contact, let id = contact.phone_id { + if let contact = conversationLanguageSettingService.settingStorage as? Contact, let id = contact.phone_id { MQTTService.sharedInstance.updateSetting(feature, to: id) - } else if let member = conversationLanguageSettingService.settingOwner as? Member { + } else if let member = conversationLanguageSettingService.settingStorage as? Member { MQTTService.sharedInstance.updateSetting(feature, to: member) } else { assertionFailure("Something went wrong") } } } + +private extension MessageInteractor { + + func textForTranslation(from message: Message) -> String? { + guard let attach = message.mainFile, let mime = attach.mime, let type = SendMessageType(rawValue: mime) else { + return nil + } + + switch type { + case .transfer: + return attach.data?.first?.value + default: + return (messageParser.parse(message, to: .transcription).first?.payload ?? attach.payload) + } + } + + func finishTranslation(for localId: String, with state: TranslationState) { + cancelTranslation(for: localId) + + presenter?.updateConversion(with: state) + + guard let message = messageBy(localId: localId) else { + return + } + remove(localStatus: .translate, from: message) + } + + func cancelTranslation(for localId: String) { + guard let translationModel = configuration.translatingModels[localId] else { return } + translationModel.status = .initial + + presenter?.updateConversion(with: translationModel) + + configuration.translatingModels.removeValue(forKey: localId) + } +} diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor+Utils.swift b/Nynja/Modules/Message/Interactor/MessageInteractor+Utils.swift index 13a5cb855..dbf6cdfa3 100644 --- a/Nynja/Modules/Message/Interactor/MessageInteractor+Utils.swift +++ b/Nynja/Modules/Message/Interactor/MessageInteractor+Utils.swift @@ -47,4 +47,20 @@ extension MessageInteractor { func indexOfMessage(with serverId: MessageServerId) -> Int? { return configuration.messages.index(where: { $0.id == serverId }) } + + + // MARK: - Progress + + func removeProgressIfNeeded(for message: Message) { + guard let localId = message.msg_id else { + return + } + if !messageParser.parse(message, to: .transcription).isEmpty { + configuration.transcribingModels.removeValue(forKey: localId) + } + + if !messageParser.parse(message, to: .translation(.convenientToMe)).isEmpty { + configuration.translatingModels.removeValue(forKey: localId) + } + } } diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor.swift b/Nynja/Modules/Message/Interactor/MessageInteractor.swift index dcd8bbb7e..d455ff61b 100644 --- a/Nynja/Modules/Message/Interactor/MessageInteractor.swift +++ b/Nynja/Modules/Message/Interactor/MessageInteractor.swift @@ -67,7 +67,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H return storageService.phoneId } - var translationCount: Int64? + var conversionCount: Int64? var initialUnreadCount = 0 var initialSelfReader: Int64? @@ -114,11 +114,11 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H }() private(set) lazy var conversationLanguageSettingService: ConversationLanguageSettingServiceProtocol = { - guard let settingOwner = settingOwner else { + guard let settingStorage = settingStorage else { fatalError("settingOwner = nil") /////TODO: We need to know how this chat was added to our profile. } let dependencies = ConversationLanguageSettingService.Dependencies( - settingOwner: settingOwner + settingStorage: settingStorage ) return ConversationLanguageSettingService(dependencies: dependencies) }() @@ -227,9 +227,8 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H isAfterConnectionAppeared = false prepareInitialValues() - translationCount = chat.unreadCount + conversionCount = chat.unreadCount - setupChatLanguageIfNeeded() updateTranslationPreview() if ReachabilityService.sharedInstance.isReachable { @@ -491,7 +490,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H func sendAudio(withUrl url: URL) { let message = messageFactory.makeAudioMessage(withUrl: url, - language: conversationLanguageSettingService.chatLanguage.lang, + language: conversationLanguageSettingService.defaultLanguage.language, contact: contact, room: room) sendMessage(message) @@ -672,7 +671,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H UIPasteboard.general.string = text } - func detectMessageLanguage(with localId: String, convertionType: ConvertionMessageModel.`Type`, success: (SelectedLang)->Void) { + func detectMessageLanguage(with localId: String, convertionType: ConvertionMessageModel.`Type`, success: (Language)->Void) { guard let message = messageBy(localId: localId) else { assertionFailure("Something went wrong") @@ -695,9 +694,9 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H } if let language = language { - success(SelectedLang.lang(LangExtended.init(language: language, name: ""))) + success(Language(language: language)) } else { - success(getMySelectedLang()) + success(myLanguage) } } @@ -895,15 +894,25 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H } // MARK: - Validation + func canDeleteMessageForAll(localId: String) -> Bool { guard let message = messageBy(localId: localId), let date = message.createdDate else { return false } return Date().hours(from: date) < 24 } // MARK: - MessageProcessingDelegate - func updateProgress(_ progress: ProgressModel) { - self.configuration.progressModels[progress.url] = progress - self.presenter?.updateProgress(progress) + + func update(progress: ProgressModel) { + + configuration.progressModels[progress.url] = progress + presenter?.update(progress: progress) + + if progress.status == .done { + let messagesToConversion = configuration.messages.filter { $0.mainUrl == progress.url && $0.sendType == .audio } + DispatchQueue.main.async { [weak self] in + messagesToConversion.forEach { self?.performAutoConversion(exist: $0) } + } + } } // MARK: - HistoryHandlerDelegate @@ -929,7 +938,8 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H self.isNew = true self.fetchFromStorage() } - self.autoTranslateReceiptMessagesIfNeeded() + + self.performAutoConversion(for: self.configuration.messages) } } @@ -979,8 +989,8 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H notifyAboutRead(withOld: oldChat.otherReader, new: newChat.otherReader) - if let settingOwner = settingOwner { - conversationLanguageSettingService.updateSettingOwner(settingOwner) + if let settingStorage = settingStorage { + conversationLanguageSettingService.update(settingStorage: settingStorage) updateTranslationPreview() } @@ -1056,26 +1066,29 @@ extension MessageInteractor { } if isAutotranslateOnSending, let mime = message.mainFile?.mime, let type = SendMessageType(rawValue: mime), type == .text { - guard let sendingLang = conversationLanguageSettingService.translateLanguage else { - return - } + let sendingLang = conversationLanguageSettingService.outgoingTranslateLanguage try? storageService.perform(action: .save, with: message) - translate(message, lang: SelectedLang(sendingLang)) { [weak self] output in + translate(message: message, language: sendingLang) { [weak self] result in guard let `self` = self else { return } - let translationDesc = self.messageFactory.autotranslationDesc(text: output.inputText, translatedText: output.translatedText, lang: output.translatedLang) - var files = message.files?.filter { $0.type != .autotranslate } ?? [] - files.append(translationDesc) - message.files = files - + + switch result { + case .output(let output): + let translationDesc = self.messageFactory.autotranslationDesc(text: output.inputText, translatedText: output.translatedText, lang: output.translatedLang) + var files = message.files?.filter { $0.type != .autotranslate } ?? [] + files.append(translationDesc) + message.files = files + default: + break + } + if let repliedMessage = self.repliedMessage { self.messageSendingService.sendRepliedMessage(repliedMessage, message: message) self.declineReply() } else { self.messageSendingService.sendMessage(message) } - } } else { if let repliedMessage = repliedMessage { diff --git a/Nynja/Modules/Message/Presenter/MessagePresenter.swift b/Nynja/Modules/Message/Presenter/MessagePresenter.swift index 4e558edb5..48b28d443 100644 --- a/Nynja/Modules/Message/Presenter/MessagePresenter.swift +++ b/Nynja/Modules/Message/Presenter/MessagePresenter.swift @@ -57,7 +57,9 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract } else { return P2pChatItemsFactory(isActionsEnabled: isActionsEnabled, isPaymentEnabled: false) { [weak self] in let isBan = self?.interactor.contact?.isBan ?? false - + if let id = self?.interactor.contact?.id { + UserDefaults.standard.set(id, forKey: String(SharedParameters.contactId.rawValue)) + } if isBan { self?.blockMessageSending() } @@ -224,7 +226,6 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract func didCancelTapped(_ id: String) { interactor.cancelLoading(id) - interactor.cancelTranslating(localId: id) interactor.cancelTransсribing(localId: id) } @@ -294,7 +295,7 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract } func translateMessage(localId: String) { - interactor.translateMessage(localId: localId, lang: nil, completion: nil) + interactor.translateMessage(localId: localId) } func untranslateMessage(localId: String) { @@ -307,31 +308,35 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract switch convertionModel.type { case .translate: - input = LanguageSelector.Input(selectorType: .my, - selectedLang: language, - selectorLang: { [weak self] selectedLang in - guard let `self` = self, case .lang = selectedLang else { - return - } - self.interactor.chooseTranslationLanguageForMessage(with: localId, - lang: selectedLang) - }, - saveLangHandler: { [weak self] isNeedToSave, selectedLang in - guard isNeedToSave, let `self` = self else { return } - self.interactor.setupMyLanguage(lang: selectedLang) + let selectorType: SelectorType + if let message = interactor.messageBy(localId: localId) { + selectorType = message.isOwn ? .translateOutgoing : .translateIncoming + } else { + selectorType = .translateOutgoing + } + input = LanguageSelector.Input( + selectorType: selectorType, + selectedLang: language, + selectorLang: { [weak self] selectedLang in + guard let `self` = self, let selectedLang = selectedLang else { + return + } + self.interactor.translateMessage(localId: localId, + language: selectedLang) }, - isNoneAvailable: false) + saveLangHandler: nil) case .transcribe: - input = LanguageSelector.Input(selectorType: .transcription, - selectedLang: language, - selectorLang: { [weak self] selectedLang in - guard let `self` = self, case .lang = selectedLang else { - return - } - self.interactor.chooseTranscriptionLanguageForMessage(with: localId, lang: selectedLang) + input = LanguageSelector.Input( + selectorType: .transcribe, + selectedLang: language, + selectorLang: { [weak self] selectedLang in + guard let `self` = self, let selectedLang = selectedLang else { + return + } + self.interactor.transcribeMessage(localId: localId, + language: selectedLang) }, - saveLangHandler: nil, - isNoneAvailable: false) + saveLangHandler: nil) } wireFrame.showLanguageSelector(input: input) @@ -343,49 +348,62 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract return interactor.translate(input: input, handler: translationHandler) } - func chooseLanguageForTranslation(sendingLang: SelectedLang, selector: SelectorLang?, saveLangHandler: SaveLangHandler?) { - - let input = LanguageSelector.Input(selectorType: .sending, - selectedLang: sendingLang, - selectorLang: selector, - saveLangHandler: { [weak self] isNeedToSave, selectedLang in - saveLangHandler?(isNeedToSave, selectedLang) - - guard isNeedToSave, let `self` = self else { return } - self.interactor.setupSendingLanguage(lang: selectedLang) - }, - isNoneAvailable: false) + func chooseLanguageForTranslation(sendingLang: Language, selector: SelectorLang?, saveLangHandler: SaveLangHandler?) { + + let input = LanguageSelector.Input( + selectorType: .translateOutgoing, + selectedLang: sendingLang, + selectorLang: selector, + saveLangHandler: { [weak self] isNeedToSave, selectedLang in + saveLangHandler?(isNeedToSave, selectedLang) + + guard isNeedToSave, let `self` = self else { return } + self.interactor.sendingLanguage = selectedLang + }) wireFrame.showLanguageSelector(input: input) } - func showChooseAnotherLanguageAlert(localId: String, language: String) { - showChooseAnotherAlert(localId: localId, - language: SelectedLang.lang(.init(language: language, name: ""))) + func showChooseAnotherLanguageAlert(localId: String, language: Language) { + DispatchQueue.main.async { + AlertManager.sharedInstance.showAlertWithTwoActions( + title: String.localizable.transcribeErrorTitle, + message: String.localizable.transcribeErrorMessage, + firstActionTitle: String.localizable.transcribeErrorAction, + secondActionTitle: String.localizable.ok, + firstAction: { [weak self] in + guard let `self` = self else { + return + } + self.showLanguageSelector(for: localId, with: language) + } , + secondAction: nil) + } } - func showChooseAnotherAlert(localId: String, language: SelectedLang) { - DispatchQueue.main.async { - AlertManager.sharedInstance.showAlertWithTwoActions(title: String.localizable.transcribeErrorTitle, message: String.localizable.transcribeErrorMessage, firstActionTitle: String.localizable.transcribeErrorAction, secondActionTitle: String.localizable.ok, firstAction: { [weak self] in - guard let `self` = self else { + func showLanguageSelector(for localId: String, with language: Language? = nil) { + let input = LanguageSelector.Input( + selectorType: .transcribe, + selectedLang: language, + selectorLang: { [weak self] selectedLang in + guard let `self` = self, let selectedLang = selectedLang else { return } - let input = LanguageSelector.Input(selectorType: .transcription, selectedLang: language, selectorLang: { [weak self] selectedLang in - guard let `self` = self, case .lang = selectedLang else { - return - } - - self.interactor.chooseTranscriptionLanguageForMessage(with: localId, - lang: selectedLang) - }, saveLangHandler: nil, isNoneAvailable: false) - self.wireFrame.showLanguageSelector(input: input) - } , secondAction: nil) - } + self.interactor.transcribeMessage(localId: localId, + language: selectedLang) + }, + saveLangHandler: nil) + + self.wireFrame.showLanguageSelector(input: input) } func transcribeMessage(localId: String) { - interactor.transcribeMessage(localId: localId, lang: nil) + if interactor.chat is Contact { + interactor.transcribeMessage(localId: localId) + } else { + showLanguageSelector(for: localId) + } } func untranscribeMessage(localId: String) { @@ -419,6 +437,9 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract self.wireFrame?.openURL(url: url) } + func notifyAvailability(snackBar: SnackBar, isAvailable: Bool) { + wireFrame.notifyAvailability(snackBar: snackBar, isAvailable: isAvailable) + } // MARK: MessageInteractorOutputProtocol @@ -435,8 +456,8 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract view.updateHeader(with: avatarModel) } - func updateTranslationPreview(selectedLang: SelectedLang, isAuto: Bool) { - view.showTranslationPreview(selectedLang, isAuto: isAuto) + func updateTranslationPreview(with config: TranslationPreviewConfig) { + view.showTranslationPreview(with: config) } func blockWheelActions(_ isBlock: Bool) { @@ -886,15 +907,6 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract self.view.newMessage(model) } } - - func updateTranslationProgress(_ progress: ConvertionProgressModel) { - self.view.updateTranslationProgress(progressModel: progress) - } - - func updateProgress(_ model: ProgressModel) { - // NOTE: find crash which appears when user close and open 'Chat' scren to fast. Need to think. - self.view?.updateProgress(progressModel: model) - } func updateMessage(with configuration: MessageConfiguration) { let message = configuration.message @@ -925,6 +937,19 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract view.removeMessage(localId, isForAllUsers: isForAllUsers) } + func updateConversion(with progress: ConvertionProgressModel) { + view?.updateConversion(with: progress) + } + + func updateConversion(with state: ConversionState) { + view?.updateConversion(with: state) + } + + func update(progress: ProgressModel) { + // NOTE: find crash which appears when user close and open 'Chat' scren to fast. Need to think. + view?.update(progress: progress) + } + func internetStatusChanged(_ status: InternetStatus) { internetStatus = status switch status { diff --git a/Nynja/Modules/Message/Protocols/ConversionState.swift b/Nynja/Modules/Message/Protocols/ConversionState.swift new file mode 100644 index 000000000..dbadb79e6 --- /dev/null +++ b/Nynja/Modules/Message/Protocols/ConversionState.swift @@ -0,0 +1,14 @@ +// +// ConversionState.swift +// Nynja +// +// Created by Andrey Reznik on 29.10.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol ConversionState { + + var text: String { get } +} diff --git a/Nynja/Modules/Message/Protocols/MessageProtocols.swift b/Nynja/Modules/Message/Protocols/MessageProtocols.swift index 5258417d6..58b3050bd 100644 --- a/Nynja/Modules/Message/Protocols/MessageProtocols.swift +++ b/Nynja/Modules/Message/Protocols/MessageProtocols.swift @@ -43,6 +43,8 @@ protocol MessageWireframeProtocol: DocumentInteractionInput { func show(alert: UIAlertController) func openMarketplaceScreen() func openTransfersHistoty(for profile: Profile) + + func notifyAvailability(snackBar: SnackBar, isAvailable: Bool) } //MARK: Presenter - @@ -104,7 +106,7 @@ protocol MessagePresenterProtocol: BasePresenterProtocol, func chooseLanguageForMessage(localId: String, convertionModel: ConvertionMessageModel) func translate(input: TranslationInput, translationHandler: @escaping TranslationHandler) -> CancelAction? - func chooseLanguageForTranslation(sendingLang: SelectedLang, selector: SelectorLang?, saveLangHandler: SaveLangHandler?) + func chooseLanguageForTranslation(sendingLang: Language, selector: SelectorLang?, saveLangHandler: SaveLangHandler?) func transcribeMessage(localId: String) func untranscribeMessage(localId: String) @@ -132,6 +134,8 @@ protocol MessagePresenterProtocol: BasePresenterProtocol, func openMarketplaceScreen() func currentMembersCount() -> UInt func openTransfersHistoty() + + func notifyAvailability(snackBar: SnackBar, isAvailable: Bool) } //MARK: Interactor - @@ -141,13 +145,15 @@ protocol MessageInteractorOutputProtocol: class, MentionFetchOutputProtocol, Mes func setup(with configuration: ChatConfiguration) func setupNew(with configuration: ChatConfiguration) func newMessage(with configuration: MessageConfiguration) - func updateTranslationProgress(_ progress: ConvertionProgressModel) - func updateProgress(_ model: ProgressModel) func updateMessage(with configuration: MessageConfiguration) func removeMessage(_ message: Message, isForAllUsers: Bool) + + func updateConversion(with progress: ConvertionProgressModel) + func updateConversion(with state: ConversionState) + func update(progress: ProgressModel) func chatUpdated(_ chat: ChatModel) - func updateTranslationPreview(selectedLang: SelectedLang, isAuto: Bool) + func updateTranslationPreview(with config: TranslationPreviewConfig) func internetStatusChanged(_ status: InternetStatus) func presenceStatusChanged(_ status: PresenceStatus) @@ -178,7 +184,7 @@ protocol MessageInteractorOutputProtocol: class, MentionFetchOutputProtocol, Mes func didChangeCallInvitationState(_ call: NYNCall) - func showChooseAnotherLanguageAlert(localId: MessageLocalId, language: String) + func showChooseAnotherLanguageAlert(localId: MessageLocalId, language: Language) } @@ -230,7 +236,7 @@ protocol MessageInteractorInputProtocol: BaseInteractorProtocol, MentionFetchInp func copyMessage(localId: String) func copyConvertion(with model: ConvertionMessageModel) - func detectMessageLanguage(with localId: String, convertionType: ConvertionMessageModel.`Type`, success: (SelectedLang)->Void) + func detectMessageLanguage(with localId: String, convertionType: ConvertionMessageModel.`Type`, success: (Language)->Void) func starMessage(localId: MessageLocalId) func unstarMessage(localId: MessageLocalId) @@ -259,6 +265,8 @@ protocol MessageInteractorInputProtocol: BaseInteractorProtocol, MentionFetchInp func didSelectForwardTargets(_ targets: ForwardTargets) func currentMembersCount() -> UInt + + func messageBy(localId: MessageLocalId) -> Message? } //MARK: View - @@ -273,9 +281,13 @@ protocol MessageViewProtocol: class { func updateData(with configuration: DisplayChatConfiguration) func updateNewData(_ cells: [BaseChatCellModel]) func newMessage(_ cell: BaseChatCellModel) - func updateTranslationProgress(progressModel: ConvertionProgressModel?) - func updateProgress(progressModel: ProgressModel?) func updateMessage(_ model: BaseChatCellModel) + + func updateConversion(with progress: ConvertionProgressModel?) + func updateConversion(with state: ConversionState) + + func update(progress: ProgressModel?) + func updateStar(starID: String?, messageId: String) func updateUnreadTitle(_ selfRead: Int64?, unreadCount: Int) @@ -306,7 +318,7 @@ protocol MessageViewProtocol: class { func showReplyPreview(_ model: RepliedMessageModel, mode: InputBar.DisplayMode) func hideReplyPreview() - func showTranslationPreview(_ selectedLang: SelectedLang, isAuto: Bool) + func showTranslationPreview(with config: TranslationPreviewConfig) func removeEditMessageAppearance() func prepareEditMessageUI(with model: RepliedMessageModel) diff --git a/Nynja/Modules/Message/View/MessageVC.swift b/Nynja/Modules/Message/View/MessageVC.swift index 515bdd20e..79de3f991 100644 --- a/Nynja/Modules/Message/View/MessageVC.swift +++ b/Nynja/Modules/Message/View/MessageVC.swift @@ -191,12 +191,26 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw view.addSubview(inputBar) inputBar.snp.makeConstraints { maker in maker.left.right.equalToSuperview() - maker.bottom.equalTo(view.keyboardLayoutGuide.snp.top) + maker.bottom.equalTo(view.snackBarLayoutGuide.snp.top) } return inputBar }() - + + private var snackBarHideWorkItem: DispatchWorkItem? + private lazy var snackBar: SnackBar = { + let bar = SnackBar() + bar.delegates.addDelegate(view.snackBarLayoutGuide) + view.addSubview(bar) + + bar.snp.makeConstraints { maker in + maker.left.right.equalToSuperview() + maker.top.equalTo(view.snackBarLayoutGuide.snp.top) + maker.bottom.equalTo(view.keyboardLayoutGuide.snp.top).offset(additionalSnackBarHeight) + } + return bar + }() + private weak var translationPreview: TranslationViewProtocol! private weak var footerView: CollapsedView! @@ -205,6 +219,7 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw make.left.right.equalToSuperview() } } + lazy var staticFooterView: UIView = { let view = UIView() view.backgroundColor = UIColor.nynja.clear @@ -424,6 +439,7 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw setupStickerSearchResultViewOutput() setupStickerStateHandlers() + view.bringSubview(toFront: inputBar) view.bringSubview(toFront: replyPreview) view.bringSubview(toFront: gradientView) @@ -439,12 +455,28 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw override func prepareForDissappear() { super.prepareForDissappear() endInputBarInteraction() + + snackBarHideWorkItem?.cancel() + hideSnackBar(animationDuration: SnackBar.Animation.zero) + + presenter?.notifyAvailability(snackBar: snackBar, isAvailable: false) } deinit { UserDefaults.standard.set("", forKey: String(SharedParameters.roomId.rawValue)) + UserDefaults.standard.set("", forKey: String(SharedParameters.contactId.rawValue)) } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + presenter?.notifyAvailability(snackBar: snackBar, isAvailable: true) + + if snackBarHideWorkItem?.isCancelled ?? false { + snackBar.silentUpdate() + } + } + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) viewVisible = true @@ -454,7 +486,7 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw center.addObserver(self, selector: #selector(didEnterBackground), name: .UIApplicationDidEnterBackground, object: nil) center.addObserver(self, selector: #selector(willResignActive), name: .UIApplicationWillResignActive, object: nil) - + center.addObserver(self, selector: #selector(didBecomeActive), name: .UIApplicationDidBecomeActive, object: nil) collectionView.layoutIfNeeded() presenter.viewDidAppear() @@ -463,7 +495,11 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw @objc private func willResignActive() { goAway() } - + + @objc private func didBecomeActive() { + self.presenter.loadData() + } + override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) if viewVisible { @@ -473,7 +509,7 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw NotificationCenter.default.removeObserver(self, name: .UIApplicationDidEnterBackground, object: nil) NotificationCenter.default.removeObserver(self, name: .UIApplicationWillResignActive, object: nil) - + NotificationCenter.default.removeObserver(self, name: .UIApplicationDidBecomeActive, object: nil) goAway() @@ -489,10 +525,10 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw updateScrollIndicatorInsets() - let expandedHeight = collectionView.bounds.height + CGFloat(FooterViewLayout.collapsedHeight) + let expandedHeight = collectionView.bounds.height + CGFloat(CollapsedView.FooterView.collapsedHeight) - footerView?.update(constraintLayout: CollapsedView.ConstraintLayout(availableHeight: [CGFloat(FooterViewLayout.collapsedHeight), - CGFloat(FooterViewLayout.middleHeight), + footerView?.update(constraintLayout: CollapsedView.ConstraintLayout(availableHeight: [CGFloat(CollapsedView.FooterView.collapsedHeight), + CGFloat(CollapsedView.FooterView.middleHeight), expandedHeight])) } @@ -512,10 +548,15 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw } isAllowedEndEditing = true + + snackBarHideWorkItem?.perform() } @objc private func wheelDidOpen() { endInputBarInteraction() + + snackBarHideWorkItem?.cancel() + hideSnackBar(animationDuration: SnackBar.Animation.zero) } @@ -950,26 +991,6 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw } } - func updateProgress(progressModel: ProgressModel?) { - DispatchQueue.main.async { [weak self] in - guard let messageDS = self?.messageDS, let progressModel = progressModel else { - return - } - messageDS.update(progress: progressModel) - } - } - - func updateTranslationProgress(progressModel: ConvertionProgressModel?) { - DispatchQueue.main.async { - let id = progressModel?.id - self.messageDS.forEach { - guard $0.id == id else { return } - $0.convertProgressModel = progressModel - } - self.collectionView.reloadData() - } - } - func updateMessage(_ model: BaseChatCellModel) { guard let messageId = model.id, let index = messageDS.index(where: { $0.id == messageId }) else { return @@ -988,6 +1009,34 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw collectionView.reloadData() } + func updateConversion(with progress: ConvertionProgressModel?) { + guard let progress = progress else { + return + } + + DispatchQueue.main.async { + self.messageDS.forEach { + guard $0.id == progress.id else { return } + $0.convertProgressModel = progress + } + self.collectionView.reloadData() + } + } + + func updateConversion(with state: ConversionState) { + self.showSnackBar(for: state) + } + + func update(progress: ProgressModel?) { + DispatchQueue.main.async { [weak self] in + guard let messageDS = self?.messageDS, + let progress = progress else { + return + } + messageDS.update(progress: progress) + } + } + private func updateReplies(of messageId: MessageLocalId, update: (RepliedMessageModel) -> Void) { for idx in messageDS.indices { let cellModel = messageDS.cellModel(at: idx) @@ -1618,8 +1667,8 @@ extension MessageVC { // MARK: - Translation extension MessageVC { - func showTranslationPreview(_ selectedLang: SelectedLang, isAuto: Bool) { - guard case .lang(let language) = selectedLang, !language.isNone else { + func showTranslationPreview(with config: TranslationPreviewConfig) { + guard config.mode != .off else { if footerView != nil { footerView.removeFromSuperview() footerView = nil @@ -1643,22 +1692,27 @@ extension MessageVC { } collectionView.snp.remakeConstraints { make in makeDefaultTableConstraint(make) - make.bottom.equalTo(staticFooterView.snp.top).offset(-FooterViewLayout.collapsedHeight) + make.bottom.equalTo(staticFooterView.snp.top).offset(-CollapsedView.FooterView.collapsedHeight) } } - if isAuto { + switch config.mode { + case .auto: if let preview = translationPreview as? TranslationAutoView { - preview.silentUpdate(with: selectedLang) + preview.silentUpdate(with: config.language) } else { - footerView.contentView = makeAutoTranslationView(selectedLang: selectedLang) + footerView.contentView = makeAutoTranslationView(selectedLang: config.language) } - } else { + case .manual: if let preview = translationPreview as? TranslationManualView { - preview.silentUpdate(with: selectedLang) + preview.silentUpdate(with: config.language) } else { - footerView.contentView = makeManualTranslationView(selectedLang: selectedLang) + let contentView = makeManualTranslationView(selectedLang: config.language) + contentView.delegate = footerView + footerView.contentView = contentView } + case .off: + break } } } @@ -1673,9 +1727,9 @@ private extension MessageVC { style: .bordered, initialViewState: .collapsed, initialArrowState: .toUp, - heightConstraintLayout: CollapsedView.ConstraintLayout(availableHeight: [CGFloat(FooterViewLayout.collapsedHeight), - CGFloat(FooterViewLayout.middleHeight), - CGFloat(FooterViewLayout.expandedHeight)]), + heightConstraintLayout: CollapsedView.ConstraintLayout(availableHeight: [CGFloat(CollapsedView.FooterView.collapsedHeight), + CGFloat(CollapsedView.FooterView.middleHeight), + CGFloat(CollapsedView.FooterView.expandedHeight)]), animationsDuration: CollapsedView.AnimationsDuration( arrowToUpDuration: 0.1, arrowToLineDuration: 0.1, @@ -1683,7 +1737,7 @@ private extension MessageVC { collapsedDuration: 0.1, expandedDuration: 0.1), swipeIndicatorLayout: CollapsedView.SwipeIndicatorLayout( - size: CGSize(width: FooterViewLayout.arrowWidth, height: FooterViewLayout.arrowHeight), + size: CGSize(width: CollapsedView.FooterView.arrowWidth, height: CollapsedView.FooterView.arrowHeight), topOffset: 0)) footerView.configure(config: config) @@ -1697,7 +1751,7 @@ private extension MessageVC { return footerView } - func makeManualTranslationView(selectedLang: SelectedLang, initialState: TranslationManualView.DisplayingState = .empty) -> TranslationManualView { + func makeManualTranslationView(selectedLang: Language, initialState: TranslationManualView.DisplayingState = .empty) -> TranslationManualView { let view = TranslationManualView() view.backgroundColor = UIColor.nynja.clear view.clipsToBounds = true @@ -1720,13 +1774,25 @@ private extension MessageVC { language: selectedLang, mentions: self?.mentionController.mentions ?? []) - let cancelAction = self?.presenter.translate(input: translationInput, - translationHandler: { translationMentionedOutput in - let result = TranslationManualView.TranlationResult(originalText: text, - translatedText: translationMentionedOutput.text, - translatedLanguage: translationMentionedOutput.language, - mentions: translationMentionedOutput.mentions) - view.update(state: .translated(result)) + let cancelAction = self?.presenter.translate( + input: translationInput, + translationHandler: { result in + switch result { + case .success(let output): + let result = TranslationManualView.TranlationResult( + originalText: text, + translatedText: output.text, + translatedLanguage: output.language, + mentions: output.mentions) + + view.update(state: .translated(result)) + case .error(let state): + view.update(state: .readyForTranslate(text)) + self?.showSnackBar(for: state) + default: + break + } + }) view.attach(cancelAction: cancelAction) @@ -1747,7 +1813,7 @@ private extension MessageVC { return view } - func makeAutoTranslationView(selectedLang: SelectedLang) -> TranslationAutoView { + func makeAutoTranslationView(selectedLang: Language) -> TranslationAutoView { let view = TranslationAutoView() view.backgroundColor = UIColor.nynja.clear view.clipsToBounds = true @@ -1771,15 +1837,44 @@ private extension MessageVC { } } -//MARK: - Layout +//MARK: - Snack Bar extension MessageVC { - enum FooterViewLayout { - static let collapsedHeight = 30 - static let middleHeight = 100.adjustedByHeight - static let expandedHeight = 300.adjustedByHeight - - static let arrowHeight = 30 - static let arrowWidth = 44 + + func showSnackBar(for state: ConversionState) { + DispatchQueue.main.async { [weak self] in + self?.showSnackBar(with: .init(displayType: .text(state.text))) + } + } + + func showSnackBar(with config: SnackBar.Config, animationDuration: TimeInterval = SnackBar.Animation.duration) { + guard viewVisible else { + return + } + + snackBarHideWorkItem?.cancel() + + snackBar.update(for: .expanded, + with: config, + animationDuration: animationDuration) + + snackBarHideWorkItem = DispatchWorkItem { [weak self] in + self?.hideSnackBar() + } + DispatchQueue.main.asyncAfter( + deadline: .now() + SnackBar.Display.duration, + execute: snackBarHideWorkItem!) + } + + func hideSnackBar(animationDuration: TimeInterval = SnackBar.Animation.duration) { + snackBar.update(for: .collapsed, animationDuration: animationDuration) + } + + var additionalSnackBarHeight: CGFloat { + var offset: CGFloat = 0 + if #available(iOS 11.0, *) { + offset = safeAreaBottomInset + } + return offset } } diff --git a/Nynja/Modules/Message/View/MessageVCLayout.swift b/Nynja/Modules/Message/View/MessageVCLayout.swift index 0f7309484..b39e1d1f8 100644 --- a/Nynja/Modules/Message/View/MessageVCLayout.swift +++ b/Nynja/Modules/Message/View/MessageVCLayout.swift @@ -43,5 +43,12 @@ extension MessageVC { enum stickerSearchResultView { static let height = CGFloat(100.0).adjustedByWidth } + + enum snackBar { + enum height { + static let collapsed: CGFloat = 0 + static let expanded: CGFloat = CGFloat(50.0).adjustedByWidth + } + } } } diff --git a/Nynja/Modules/Message/View/Views/AvatarView/AvatarView.swift b/Nynja/Modules/Message/View/Views/AvatarView/AvatarView.swift index e8f8185ea..c8c2d2ce6 100644 --- a/Nynja/Modules/Message/View/Views/AvatarView/AvatarView.swift +++ b/Nynja/Modules/Message/View/Views/AvatarView/AvatarView.swift @@ -121,7 +121,8 @@ class AvatarView: BaseView { func setup(with viewModel: AvatarViewModel) { titleLabel.text = viewModel.title titleLabel.accessibilityValue = viewModel.title - imageView.setImage(url: viewModel.avatarUrl, placeHolder: UIImage.nynja.Contacts.avaPlaceholder.image) + let placeholder = imageView.image ?? UIImage.nynja.Contacts.avaPlaceholder.image + imageView.setImage(url: viewModel.avatarUrl, placeHolder: placeholder, options: .delayPlaceholder) setupMuteImageView(viewModel.isMuted) } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift index 2559e0eef..4f4f85ea5 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift @@ -216,6 +216,7 @@ class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, Messag return { (make) in make.right.equalToSuperview().offset(-Constraints.bubble.horizontalInset) make.top.equalTo(verticalInset) + make.width.lessThanOrEqualTo(BaseChatCell.Constraints.bubble.maxWidth).priority(999) } } @@ -345,10 +346,11 @@ class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, Messag } infoView.snp.remakeConstraints { maker in + let infoAnchor = messageContent!.infoAnchor maker.height.equalTo(Constraints.infoView.height) - maker.left.greaterThanOrEqualTo(infoViewInset.x) - maker.right.equalTo((messageContent?.infoAnchor ?? self).snp.right).offset(-infoViewInset.x) - maker.bottom.equalTo((messageContent?.infoAnchor ?? self).snp.bottom).offset(-infoViewInset.y) + maker.left.greaterThanOrEqualTo(infoAnchor).offset(infoViewInset.x) + maker.right.equalTo(infoAnchor.snp.right).offset(-infoViewInset.x) + maker.bottom.equalTo(infoAnchor.snp.bottom).offset(-infoViewInset.y) } infoContent?.configure(with: model) @@ -493,7 +495,7 @@ class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, Messag class func size(for model: BaseChatCellModel) -> CGSize { let senderHeight = CGFloat(shouldShowSender(model) ? fromLabelHeight.adjustedByWidth : 0) - var size = MessageViewFactory.size(for: model) + var size = MessageViewFactory.size(for: model, maxWidth: Constraints.bubble.maxWidth) size.height += senderHeight let footerSize = ChatCellFooterView.size(for: model.messageType) @@ -651,8 +653,10 @@ private extension BaseChatCell { hideNetworkProcessingUI() case .atProgress: updateProcessingLabel(isHidden: false) - processingButton.isHidden = false - setupProcessingCancelButton() + if progress.type == .transcribe { + processingButton.isHidden = false + setupProcessingCancelButton() + } case .initial: if downloadProgressModel?.status != .notStarted { updateProcessingLabel(isHidden: true) diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCellLayout.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCellLayout.swift index 454e5cfc7..b29b0c683 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCellLayout.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCellLayout.swift @@ -8,9 +8,9 @@ extension BaseChatCell { - struct Constraints { + enum Constraints { - struct bubble { + enum bubble { static let maxWidth = CGFloat(290.0.adjustedByWidth) static let verticalInset = CGFloat(4.0.adjustedByWidth) @@ -18,20 +18,20 @@ extension BaseChatCell { static let extendedHorizontalInset = (fromImageView.leftOffset + fromImageView.height + 8.0).adjustedByWidth } - struct fromLabel { + enum fromLabel { static let leftInset = 8.0.adjustedByWidth } - struct fromImageView { + enum fromImageView { static let leftOffset: CGFloat = 10.0 static let height: CGFloat = 36.0 } - struct starView { + enum starView { static let height: CGFloat = 32.0 } - struct infoView { + enum infoView { static let height = 22.0.adjustedByWidth static let defaultHorizontalInset = 8.0.adjustedByWidth @@ -40,18 +40,16 @@ extension BaseChatCell { static let cornerRadius: CGFloat = 4 } - struct processingButton { + enum processingButton { static let width = 44.0 static let fromBubbleInset = 10.0 // NOTE: it is correct value } - struct processingLabel { + enum processingLabel { static let height = 22.0.adjustedByWidth static let bottomInset = 3.0.adjustedByWidth static let leftInset = 8.0.adjustedByWidth static let rightInset = 8.0.adjustedByWidth } - } - } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift index 89bceac02..49e4ec85f 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift @@ -48,6 +48,7 @@ class OponentChatCell: BaseChatCell { self.bubbleLeftToSuperviewConstraint = make.left.equalToSuperview().offset(horizontalInset).constraint self.bubbleLeftToAvatarConstraint = make.left.equalToSuperview().offset(avatarInset).constraint make.top.equalTo(verticalInset) + make.width.lessThanOrEqualTo(BaseChatCell.Constraints.bubble.maxWidth).priority(999) } } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/TimeCell.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/TimeCell.swift index f9e63c8ad..fc63a9b1a 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/TimeCell.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/TimeCell.swift @@ -45,7 +45,7 @@ final class TimeCell: UICollectionViewCell { private func getText(fromDate: Date) -> String { let formatter = TimeCell.dateFormatter formatter.dateFormat = "MMMM d".localizedDateFormat - formatter.locale = Locale(identifier: Language.current.rawValue) + formatter.locale = Locale(identifier: AppLanguage.current.rawValue) return formatter.string(from: fromDate) } } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/BubbleInjectible.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/BubbleInjectible.swift index 5c28aabf3..406bc7741 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/BubbleInjectible.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/BubbleInjectible.swift @@ -13,14 +13,6 @@ protocol BubbleInjectible: MessageContentProtocol { var infoWidth: CGFloat? { get set } func configure(with model: BaseChatCellModel) - static func size(for model: BaseChatCellModel) -> CGSize? - -} - -extension BubbleInjectible { - - static func size(for model: BaseChatCellModel) -> CGSize? { - return nil - } + static func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageContentView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageContentView.swift index 43b590701..60ec6e6c1 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageContentView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageContentView.swift @@ -28,18 +28,29 @@ class MessageContentView: BaseView, BubbleInjectible { return self } + + // MARK: - Init + convenience init(contentAppearance: MessageContentAppearance?) { self.init() self.contentAppearance = contentAppearance } + // MARK: - BubbleInjectible + func configure(with model: BaseChatCellModel) { adjustBackgrounds() } + class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + return .zero + } } + +// MARK: - Testable + extension MessageContentView: TestableViewProtocol { enum Keys: String { diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageViewFactory.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageViewFactory.swift index 28183a6da..fed72bf9f 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageViewFactory.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageViewFactory.swift @@ -21,6 +21,8 @@ typealias MessageViewDependency = MessageBaseImageViewDataSource final class MessageViewFactory { + // MARK: - Content + static func content(for model: BaseChatCellModel, dependency: MessageViewDependency? = nil) -> MessageContent? { let content = usualContent(for: model.type, dependency: dependency) return wrappedContent(for: model, @@ -29,8 +31,12 @@ final class MessageViewFactory { content: content) } - private static func wrappedContent(for model: BaseChatCellModel, dependency: MessageViewDependency? = nil, type: MessageType, content: MessageContent?) -> MessageContent? { - if case .reply(_, let subType) = type { + private static func wrappedContent(for model: BaseChatCellModel, + dependency: MessageViewDependency? = nil, + type: MessageType, + content: MessageContent?) -> MessageContent? { + switch type { + case let .reply(_ ,subtype): switch model.type { case .sticker: return model.isOwner @@ -41,63 +47,81 @@ final class MessageViewFactory { delegate: dependency, messageContent: content) default: + let content = wrappedContent(for: model, dependency: dependency, type: subtype, content: content) return MessageRepliedView(contentAppearance: dependency, delegate: dependency, - messageContent: wrappedContent(for: model, - dependency: dependency, - type: subType, - content: content), + messageContent: content, messageType: model.type) } - } else if case .forward = type, model.type != .sticker { + + case .forward where model.type != .sticker: return MessageForwardView(contentAppearance: dependency, delegate: dependency, messageContent: content) - } else if case .convertion(_, let subType) = type { + + case let .convertion(_, subtype): + let content = wrappedContent(for: model, dependency: dependency, type: subtype, content: content) return MessageConvertionView(contentAppearance: dependency, delegate: dependency, - messageContent: wrappedContent(for: model, - dependency: dependency, - type: subType, - content: content)) + messageContent: content) + default: + return content } - return content } - static func size(for model: BaseChatCellModel) -> CGSize { - let size: CGSize = usualSize(for: model) - - return wrappedSize(for: model, - type: model.messageType, - size: size) + + // MARK: - Size + + static func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + guard let type = content(for: model) else { + // Should never happen + return .zero + } + return layoutSize(for: type, model: model, type: model.messageType, maxWidth: maxWidth) } - private static func wrappedSize(for model: BaseChatCellModel, type: MessageType, size: CGSize) -> CGSize { - if case .reply(_, let subType) = type { + private static func layoutSize(for bubble: BubbleInjectible.Type, + model: BaseChatCellModel, + type: MessageType, + maxWidth: CGFloat) -> CGSize { + switch type { + case .forward where model.type != .sticker: + return MessageForwardView.size(for: model, maxWidth: maxWidth, with: bubble) + + case let .reply(_, subtype): switch model.type { case .sticker: return model.isOwner - ? MessageStickerRepliedView.size(for: model, messageContentSize: size) - : OpponentMessageStickerRepliedView.size(for: model, messageContentSize: size) + ? MessageStickerRepliedView.size(for: model, maxWidth: maxWidth, with: bubble) + : OpponentMessageStickerRepliedView.size(for: model, maxWidth: maxWidth, with: bubble) default: - return MessageRepliedView.size(for: model, messageContentSize: wrappedSize(for: model, - type: subType, - size: size)) + func contentSize(constrainedTo maxWidth: CGFloat) -> CGSize { + if case .convertion = subtype { + return layoutSize(for: bubble, model: model, type: subtype, maxWidth: maxWidth) + } else { + return bubble.size(for: model, maxWidth: maxWidth) + } + } + return MessageRepliedView.size(for: model, maxWidth: maxWidth, contentSize: contentSize) + } + case let .convertion(convertionModel, subtype): + func contentSize(constrainedTo maxWidth: CGFloat) -> CGSize { + return layoutSize(for: bubble, model: model, type: subtype, maxWidth: maxWidth) } - } else if case .forward = type, model.type != .sticker { - return MessageForwardView.size(for: model, messageContentSize: size) - } else if case .convertion(let convertionModel, let subType) = type { - return MessageConvertionView.size(for: convertionModel, - messageContentSize: wrappedSize(for: model, - type: subType, - size: size)) + return MessageConvertionView.size(model: model, + convertionModel: convertionModel, + maxWidth: maxWidth, + contentSize: contentSize) + default: + return bubble.size(for: model, maxWidth: maxWidth) } - return size } + // MARK: - Private + private static func usualContent(for type: SendMessageType, dependency: MessageViewDependency? = nil) -> MessageContent? { - var content: MessageContent? + let content: MessageContent? switch type { case .contact: @@ -129,37 +153,32 @@ final class MessageViewFactory { return content } - private static func usualSize(for model: BaseChatCellModel) -> CGSize { - var size: CGSize? - + private static func content(for model: BaseChatCellModel) -> BubbleInjectible.Type? { switch model.type { case .contact: - size = MessageContactView.size(for: model) + return MessageContactView.self case .location: - size = MessageLocationView.size(for: model) + return MessageLocationView.self case .place: - size = MessagePlaceView.size(for: model) + return MessagePlaceView.self case .image: - size = MessageImageView.size(for: model) + return MessageImageView.self case .sticker: - size = MessageStickerView.size(for: model) + return MessageStickerView.self case .file: - size = MessageFileView.size(for: model) + return MessageFileView.self case .text: - size = MessageTextView.size(for: model) + return MessageTextView.self case .video: - size = MessageVideoView.size(for: model) + return MessageVideoView.self case .audio: - size = MessageVoiceView.size(for: model) + return MessageVoiceView.self case .transfer: - size = MessageTransferView.size(for: model) + return MessageTransferView.self case .audioCall: - size = MessageCallView.size(for: model) + return MessageCallView.self default: - size = .zero + return nil } - - return size ?? .zero } - } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Converting/ConvertionInfoView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Converting/ConvertionInfoView.swift index 07365de07..39d0530fc 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Converting/ConvertionInfoView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Converting/ConvertionInfoView.swift @@ -10,14 +10,19 @@ import SnapKit final class ConvertionInfoView: BaseView { - private static let titleFont = UIFont.makeFont(with: FontFamily.NotoSans.regular.name, height: Constraints.titleLabel.height) - private static let contentFont = UIFont.makeFont(with: FontFamily.NotoSans.regular.name, height: Constraints.contentLabel.height) + private static let titleFont = UIFont.makeFont(with: FontFamily.NotoSans.regular.name, + height: Constraints.titleLabel.height)! + + private static let contentFont = UIFont.makeFont(with: FontFamily.NotoSans.regular.name, + height: Constraints.contentLabel.height)! private static let payloadRenderer: MessagePayloadRendererInput = { - return MessagePayloadRenderer.linkRenderer(font: contentFont!) + return MessagePayloadRenderer.linkRenderer(font: contentFont) }() + // MARK: - Views + private(set) lazy var titleLabel: UILabel = makeTitleLabel() private(set) lazy var outdatedLabel: UILabel = makeOutdatedLabel() private(set) lazy var contentLabel: UILabel = makeContentLabel() @@ -26,7 +31,9 @@ final class ConvertionInfoView: BaseView { return [titleLabel] } - //MARK: - Setup + + // MARK: - Setup + override func baseSetup() { super.baseSetup() setupTestingKeys() @@ -37,25 +44,32 @@ final class ConvertionInfoView: BaseView { outdatedLabel.text = model.state.string contentLabel.attributedText = ConvertionInfoView.payloadRenderer.processPayload(with: model.mentions, in: model.text).text - outdatedLabel.snp.updateConstraints({ make in - make.width.equalTo(model.state != .none ? ConvertionInfoView.calculateOutdatedLabelSize().width : 0.0) - }) + outdatedLabel.isHidden = model.state == .none } - static func size(for model: ConvertionMessageModel, messageContentSize: CGSize) -> CGSize { - var localMaxWidth = ConvertionInfoView.calculateTitleLabelSize().width + 2 * CGFloat(Constraints.titleLabel.horizontalInset) - localMaxWidth = model.state != .none ? localMaxWidth + ConvertionInfoView.calculateOutdatedLabelSize().width : localMaxWidth - let maxWidth = max(BaseChatCell.Constraints.bubble.maxWidth, localMaxWidth) - 2 * CGFloat(Constraints.contentLabel.horizontalInset) + static func size(for model: ConvertionMessageModel, maxWidth: CGFloat) -> CGSize { + let horizontalInset = Constraints.horizontalContentInset + + var localMaxWidth = calculateTitleLabelWidth() + horizontalInset + + switch model.state { + case .updated, .outdated: + localMaxWidth += calculateOutdatedLabelWidth() + horizontalInset / 2 + case .none: + break + } + + let maxWidth = max(maxWidth, localMaxWidth) - horizontalInset let text = payloadRenderer.processPayload(with: model.mentions, in: model.text).text - let textSize = text.boundingRect(with: CGSize(width: maxWidth, height: 0), - options: .usesLineFragmentOrigin, - context: nil).size + + let textSize = text.size(constrainedBy: maxWidth) return textSize + CGSize(width: 0, height: Constraints.height) } } +// MARK: - View Factory private extension ConvertionInfoView { func makeTitleLabel() -> UILabel { let height = Constraints.titleLabel.height @@ -65,14 +79,13 @@ private extension ConvertionInfoView { let horizontalInset = Constraints.titleLabel.horizontalInset - self.addSubview(label) - label.snp.makeConstraints({ (make) in + addSubview(label) + label.snp.makeConstraints { make in make.height.equalTo(height) - make.width.greaterThanOrEqualTo(ConvertionInfoView.calculateTitleLabelSize().width) make.top.equalTo(Constraints.titleLabel.topInset) make.left.equalTo(horizontalInset) make.right.equalTo(outdatedLabel.snp.left).offset(-horizontalInset) - }) + } return label } @@ -85,13 +98,12 @@ private extension ConvertionInfoView { let horizontalInset = Constraints.titleLabel.horizontalInset - self.addSubview(label) - label.snp.makeConstraints({ (make) in + addSubview(label) + label.snp.makeConstraints { make in make.height.equalTo(height) - make.width.equalTo(ConvertionInfoView.calculateOutdatedLabelSize().width) make.top.equalTo(Constraints.titleLabel.topInset) make.right.equalTo(-horizontalInset) - }) + } return label } @@ -116,65 +128,43 @@ private extension ConvertionInfoView { return label } - static func calculateTitleLabelSize() -> CGSize { - let insets = 2 * Constraints.titleLabel.horizontalInset - - let maxWidth = BaseChatCell.Constraints.bubble.maxWidth - CGFloat(insets) - - let text = String.localizable.translatedMessage - - var textSize = text.boundingRect(with: CGSize(width: maxWidth, height: 0), - options: .usesLineFragmentOrigin, - attributes: [NSAttributedStringKey.font: ConvertionInfoView.titleFont as Any], - context: nil).size - - textSize.width = ceil(min(maxWidth, textSize.width)) - textSize.height = ceil(textSize.height) - - return textSize + static func calculateTitleLabelWidth() -> CGFloat { + return String.localizable.translatedMessage.width(for: titleFont) } - static func calculateOutdatedLabelSize() -> CGSize { - let maxWidth = BaseChatCell.Constraints.bubble.maxWidth - - let text = String.localizable.outdated - - var textSize = text.boundingRect(with: CGSize(width: maxWidth, height: 0), - options: .usesLineFragmentOrigin, - attributes: [NSAttributedStringKey.font: ConvertionInfoView.titleFont as Any], - context: nil).size - - textSize.width = ceil(min(maxWidth, textSize.width)) - textSize.height = ceil(textSize.height) - - return textSize + static func calculateOutdatedLabelWidth() -> CGFloat { + return String.localizable.outdated.width(for: titleFont) } } // MARK: - Layout -extension ConvertionInfoView { +private extension ConvertionInfoView { enum Constraints { static let height = titleLabel.topInset + titleLabel.height + contentLabel.topInset + contentLabel.bottomInset + static var horizontalContentInset: CGFloat { + return titleLabel.horizontalInset * 2.0 + } + enum titleLabel { static let height = CGFloat(17.adjustedByWidth) static let topInset = CGFloat(4.adjustedByWidth) - static let horizontalInset = 8.0.adjustedByWidth + static let horizontalInset = CGFloat(8.0.adjustedByWidth) } enum contentLabel { static let height = CGFloat(20.adjustedByWidth) static let topInset = CGFloat(0.0.adjustedByWidth) static let bottomInset = CGFloat(4.0.adjustedByWidth) - static let horizontalInset = 8.0.adjustedByWidth + static let horizontalInset = CGFloat(8.0.adjustedByWidth) } } } -//MARK: - Testable +// MARK: - Testable extension ConvertionInfoView: TestableViewProtocol { private enum Keys: String { case content = "conversion_content_label" diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Converting/MessageConvertionView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Converting/MessageConvertionView.swift index e55aa8152..1e59a1172 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Converting/MessageConvertionView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Converting/MessageConvertionView.swift @@ -25,8 +25,10 @@ final class MessageConvertionView: MessageContainerView { } } + // MARK: - Views - lazy var topSeparator: UIView = { + + private lazy var topSeparator: UIView = { let view = UIView() view.backgroundColor = UIColor.nynja.silver @@ -41,7 +43,7 @@ final class MessageConvertionView: MessageContainerView { }() - private(set) lazy var convertionInfoView: ConvertionInfoView = { + private lazy var convertionInfoView: ConvertionInfoView = { let view = ConvertionInfoView() addSubview(view) @@ -70,9 +72,10 @@ final class MessageConvertionView: MessageContainerView { convertionInfoView.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(longPressed(_:)))) } + // MARK: - Actions - @objc func longPressed(_ recognizer: UILongPressGestureRecognizer) { + @objc private func longPressed(_ recognizer: UILongPressGestureRecognizer) { guard recognizer.state == .began, let convertionModel = convertionModel else { return } @@ -128,11 +131,21 @@ final class MessageConvertionView: MessageContainerView { return inset } - static func size(for model: ConvertionMessageModel, messageContentSize: CGSize) -> CGSize { - let convertionSize = ConvertionInfoView.size(for: model, messageContentSize: messageContentSize) + + // MARK: - Layout Size + + static func size(model: BaseChatCellModel, + convertionModel: ConvertionMessageModel, + maxWidth: CGFloat, + contentSize: (CGFloat) -> CGSize) -> CGSize { + + let convertionSize = ConvertionInfoView.size(for: convertionModel, maxWidth: maxWidth) + let contentSize = contentSize(maxWidth) + + let width = max(contentSize.width, convertionSize.width) + let height = contentSize.height + convertionSize.height - return CGSize(width: max(messageContentSize.width, convertionSize.width), - height: messageContentSize.height + convertionSize.height) + return CGSize(width: width, height: height) } } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageCallView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageCallView.swift index 057cea9aa..f572f8561 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageCallView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageCallView.swift @@ -9,25 +9,26 @@ import Foundation import SnapKit -class MessageCallView: MessageContentView { +final class MessageCallView: MessageContentView { override var coloredViews: [UIView] { return [self, imageView, title] } + // MARK: - Constraints - struct Constraints { + + private enum Constraints { - static let width = BaseChatCell.Constraints.bubble.maxWidth static let height = CGFloat(44.adjustedByWidth) - struct imageView { + enum imageView { static let verticalInset = CGFloat(8.adjustedByWidth) static let width = CGFloat(48.adjustedByWidth) static let leftInset = 8.adjustedByWidth } - struct nameLabel { + enum nameLabel { static let width = 188.adjustedByWidth static let horizontalPadding = 8.adjustedByWidth @@ -35,7 +36,7 @@ class MessageCallView: MessageContentView { static let labelHeight = CGFloat(22.adjustedByWidth) } - struct infoView { + enum infoView { static let inset = CGPoint(x: 0, y: 2.adjustedByWidth) } } @@ -44,8 +45,10 @@ class MessageCallView: MessageContentView { return Constraints.infoView.inset } + // MARK: - Views - lazy var imageView: UIImageView = { + + private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit @@ -64,7 +67,7 @@ class MessageCallView: MessageContentView { return imageView }() - lazy var title: UILabel = { + private lazy var title: UILabel = { let size = CGSize(width: 100, height: Constraints.nameLabel.labelHeight) let label = UILabel(size: size, color: UIColor.nynja.darkLight, fontName: FontFamily.NotoSans.regular.name) self.addSubview(label) @@ -72,7 +75,9 @@ class MessageCallView: MessageContentView { return label }() + // MARK: - BubbleInjectible + override func configure(with model: BaseChatCellModel) { super.configure(with: model) @@ -82,13 +87,13 @@ class MessageCallView: MessageContentView { title.text = model.isOwner ? String.localizable.outgoingCall : String.localizable.incomingCall } - static func size(for model: BaseChatCellModel) -> CGSize? { - return CGSize(width: Constraints.width, height: Constraints.height) + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + return CGSize(width: maxWidth, height: Constraints.height) } - //MARK: - Private Methods - //MARK: - Private/Constraint Maker + // MARK: - Private Methods + private func nameLabelConstraints(default isDefault: Bool = true) -> (ConstraintMaker) -> Void { return { [weak self] make in guard let this = self else { return } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageContactView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageContactView.swift index 8324437bd..ae6e530fb 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageContactView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageContactView.swift @@ -5,9 +5,10 @@ // Created by Volodymyr Hryhoriev on 12/29/17. // Copyright © 2017 TecSynt Solutions. All rights reserved. // + import SnapKit -class MessageContactView: MessageContentView { +final class MessageContactView: MessageContentView { override var coloredViews: [UIView] { return [self, avatarImageView, nameLabel, usernameLabel] @@ -17,8 +18,10 @@ class MessageContactView: MessageContentView { return Constraints.infoView.inset } + // MARK: - Views - lazy var avatarImageView: UIImageView = { + + private lazy var avatarImageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFill let width = Constraints.avatarImageView.width @@ -38,7 +41,7 @@ class MessageContactView: MessageContentView { private var nameLabelTopConstraint: Constraint? private var nameLabelCenterYConstraint: Constraint? - lazy var nameLabel: UILabel = { + private lazy var nameLabel: UILabel = { let size = CGSize(width: 100, height: Constraints.nameLabel.labelHeight) let label = UILabel(size: size, color: UIColor.nynja.darkLight, fontName: FontFamily.NotoSans.medium.name) self.addSubview(label) @@ -55,7 +58,7 @@ class MessageContactView: MessageContentView { return label }() - lazy var usernameLabel: AlignableLabel = { + private lazy var usernameLabel: AlignableLabel = { let size = CGSize(width: 100, height: Constraints.usernameLabel.labelHeight) let label = AlignableLabel(size: size, color: UIColor.nynja.manatee, fontName: FontFamily.NotoSans.regular.name) label.verticalAlignement = .bottom @@ -74,12 +77,16 @@ class MessageContactView: MessageContentView { } } - // MARK: - Base Setup + + // MARK: - Setup + override func baseSetup() { usernameLabel.isHidden = false } + // MARK: - BubbleInjectible + override func configure(with model: BaseChatCellModel) { super.configure(with: model) @@ -92,11 +99,13 @@ class MessageContactView: MessageContentView { } - static func size(for model: BaseChatCellModel) -> CGSize? { - return CGSize(width: Constraints.width, height: Constraints.height) + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + return CGSize(width: maxWidth, height: Constraints.height) } + // MARK: - Configure Cell Appearance + private func configureViewAppearance(for model: BaseChatCellModel) { if model.contact?.nick != nil { usernameLabel.isHidden = false @@ -119,11 +128,10 @@ class MessageContactView: MessageContentView { } // MARK: - Constraints -extension MessageContactView { +private extension MessageContactView { enum Constraints { - static let width = BaseChatCell.Constraints.bubble.maxWidth static let height = 2 * avatarImageView.verticalInset + avatarImageView.width enum avatarImageView { diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageFileView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageFileView.swift index fa8d9bcb3..0356878fc 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageFileView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageFileView.swift @@ -8,7 +8,7 @@ import AutoScrollLabel -class MessageFileView: MessageContentView { +final class MessageFileView: MessageContentView { override var activatedViews: [UIView] { return [extensionLabel, filenameLabel, speedInfoLabel] @@ -18,29 +18,30 @@ class MessageFileView: MessageContentView { return [self, filenameLabel, filesizeLabel] } + // MARK: - Constraints - struct Constraints { + + private enum Constraints { - static let width = BaseChatCell.Constraints.bubble.maxWidth static let height = CGFloat(2 * extensionView.verticalInset + extensionView.width) - struct extensionView { + enum extensionView { static let width = 59.0.adjustedByWidth static let leftInset = 8.0.adjustedByWidth static let verticalInset = 8.0.adjustedByWidth static let cornerRadius: CGFloat = 4.0 } - struct unknownFormatView { + enum unknownFormatView { static let side = 24.adjustedByWidth } - struct extensionLabel { + enum extensionLabel { static let labelHeight = CGFloat(18.adjustedByWidth) static let horizontalInset = 4.0.adjustedByWidth } - struct filenameLabel { + enum filenameLabel { static let width = 202.0.adjustedByWidth static let leftInset = 16.0.adjustedByWidth @@ -52,17 +53,19 @@ class MessageFileView: MessageContentView { static let fadeLength = CGFloat(8.0.adjustedByWidth) } - struct filesizeLabel { + enum filesizeLabel { static let labelHeight = CGFloat(17.0.adjustedByWidth) } - struct infoView { + enum infoView { static let inset = CGPoint(x: 0, y: 2.0.adjustedByWidth) } } + // MARK: - Views - lazy var extensionView: UIView = { + + private lazy var extensionView: UIView = { let view = UIView() view.roundCorners(radius: Constraints.extensionView.cornerRadius) @@ -82,7 +85,7 @@ class MessageFileView: MessageContentView { return view }() - lazy var unknownFormatView: UIImageView = { + private lazy var unknownFormatView: UIImageView = { let questionMark = UIImageView() questionMark.image = UIImage.nynja.GroupStorage.icUnknownFormat.image self.addSubview(questionMark) @@ -95,7 +98,7 @@ class MessageFileView: MessageContentView { return questionMark }() - lazy var extensionLabel: UILabel = { + private lazy var extensionLabel: UILabel = { let size = CGSize(width: 100, height: Constraints.extensionLabel.labelHeight) let label = UILabel(size: size, color: UIColor.nynja.white, fontName: FontFamily.NotoSans.regular.name, textAlignment: .center) label.backgroundColor = UIColor.nynja.clear @@ -112,7 +115,7 @@ class MessageFileView: MessageContentView { return label }() - lazy var filenameLabel: CBAutoScrollLabel = { + private lazy var filenameLabel: CBAutoScrollLabel = { let label = CBAutoScrollLabel() label.textColor = UIColor.nynja.darkLight @@ -133,7 +136,7 @@ class MessageFileView: MessageContentView { return label }() - lazy var filesizeLabel: UILabel = { + private lazy var filesizeLabel: UILabel = { let size = CGSize(width: 100, height: Constraints.filesizeLabel.labelHeight) let label = UILabel(size: size, color: UIColor.nynja.manatee, fontName: FontFamily.NotoSans.regular.name) @@ -163,7 +166,9 @@ class MessageFileView: MessageContentView { return label }() + // MARK: - BubbleInjectible + override func configure(with model: BaseChatCellModel) { super.configure(with: model) @@ -193,12 +198,11 @@ class MessageFileView: MessageContentView { filenameLabel.refreshLabels() } - static func size(for model: BaseChatCellModel) -> CGSize? { - return CGSize(width: Constraints.width, height: Constraints.height) + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + return CGSize(width: maxWidth, height: Constraints.height) } override var infoInset: CGPoint { return Constraints.infoView.inset } - } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageForwardView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageForwardView.swift index d138955aa..049176214 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageForwardView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageForwardView.swift @@ -9,16 +9,15 @@ import SnapKit protocol MessageForwardViewDelegate: class { - func didForwardTapped(_ forwardView: MessageForwardView) - } -class MessageForwardView: MessageContainerView { +final class MessageForwardView: MessageContainerView { weak var delegate: MessageForwardViewDelegate? - private static let forwardFont = UIFont.makeFont(with: FontFamily.NotoSans.regular.name, height: Constraints.forwardLabel.height) + private static let forwardFont = UIFont.makeFont(with: FontFamily.NotoSans.regular.name, + height: Constraints.forwardLabel.height) override var coloredViews: [UIView] { return [self, forwardLabel, contentView] @@ -31,26 +30,33 @@ class MessageForwardView: MessageContainerView { } } + // MARK: - Constraints - struct Constraints { + + private enum Constraints { static let height = forwardLabel.topInset + forwardLabel.height - struct forwardLabel { + static var horizontalContentInset: CGFloat { + return CGFloat(forwardLabel.horizontalInset * 2) + } + + enum forwardLabel { static let height = CGFloat(17.adjustedByWidth) static let topInset = CGFloat(4.adjustedByWidth) static let horizontalInset = 8.0.adjustedByWidth } - struct contentView { + enum contentView { static let topInset = CGFloat(4.0.adjustedByWidth) } - } + // MARK: - Views - lazy var forwardLabel: UILabel = { + + private lazy var forwardLabel: UILabel = { let height = Constraints.forwardLabel.height let label = UILabel(height: height, color: UIColor.nynja.mainRed, fontName: FontFamily.NotoSans.regular.name) @@ -59,25 +65,42 @@ class MessageForwardView: MessageContainerView { let horizontalInset = Constraints.forwardLabel.horizontalInset self.addSubview(label) - label.snp.makeConstraints({ (make) in + label.snp.makeConstraints { make in make.height.equalTo(height) - make.width.greaterThanOrEqualTo(MessageForwardView.calculateForwardLabelSize().width) make.top.equalTo(Constraints.forwardLabel.topInset) make.left.equalTo(horizontalInset) make.right.equalTo(-horizontalInset) - }) + } return label }() - // MARK: - Configure + + // MARK: - Init + + convenience init(contentAppearance: MessageContentAppearance?, delegate: MessageForwardViewDelegate?, messageContent: MessageContent?) { + self.init(contentAppearance: contentAppearance, messageContent: messageContent) + self.delegate = delegate + } + + + // MARK: - Setup + + override func baseSetup() { + super.baseSetup() + + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(forwardTapped)) + forwardLabel.isUserInteractionEnabled = true + forwardLabel.addGestureRecognizer(tapRecognizer) + } + override func configure(messageContent: MessageContent?) { super.configure(messageContent: messageContent) guard let content = messageContent else { return } switch content { - case _ as MessageTextView: + case is MessageTextView: contentView.snp.updateConstraints { $0.top.equalTo(forwardLabel.snp.bottom) } case let content as MessageVoiceView: content.isSeparatorVisible = true @@ -89,64 +112,68 @@ class MessageForwardView: MessageContainerView { } } - // MARK: - Init - convenience init(contentAppearance: MessageContentAppearance?, delegate: MessageForwardViewDelegate?, messageContent: MessageContent?) { - self.init(contentAppearance: contentAppearance, messageContent: messageContent) - self.delegate = delegate - } - // MARK: - BaseView - override func baseSetup() { - super.baseSetup() - - let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(forwardTapped)) - forwardLabel.isUserInteractionEnabled = true - forwardLabel.addGestureRecognizer(tapRecognizer) + // MARK: - Actions + + @objc private func forwardTapped() { + delegate?.didForwardTapped(self) } + // MARK: - BubbleInjectible + override func configure(with model: BaseChatCellModel) { super.configure(with: model) guard case .forward? = model.messageType.ejectForward else { return } - + messageContent?.configure(with: model) } - // MARK: - Actions - @objc func forwardTapped() { - delegate?.didForwardTapped(self) - } - override var infoInset: CGPoint { return messageContent?.infoInset ?? .zero } - static func size(for model: BaseChatCellModel) -> CGSize? { - let topInset = model.type == .text ? 0 : Constraints.forwardLabel.topInset - return CGSize(width: MessageForwardView.calculateForwardLabelSize().width + CGFloat(2 * Constraints.forwardLabel.horizontalInset), height: Constraints.height + topInset) + static func size(for model: BaseChatCellModel, maxWidth: CGFloat, with contentType: BubbleInjectible.Type) -> CGSize { + let maxContentWidth = maxWidth - Constraints.horizontalContentInset + let contentSize = contentType.size(for: model, maxWidth: maxContentWidth) + + let containerSize = self.containerSize(for: model, maxWidth: maxWidth) + + let width = ceil(max(containerSize.width, contentSize.width)) + let height = containerSize.height + contentSize.height + + return CGSize(width: width, height: height) } - static func size(for model: BaseChatCellModel, messageContentSize: CGSize) -> CGSize { - let size = MessageForwardView.size(for: model) ?? .zero - return CGSize(width: ceil(max(size.width, messageContentSize.width)), height: size.height + messageContentSize.height) + private static func containerSize(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + let horizontalInset = Constraints.horizontalContentInset + let width = MessageForwardView.calculateForwardLabelSize(maxWidth: maxWidth).width + horizontalInset + + let topInset = model.type == .text + ? 0 + : Constraints.forwardLabel.topInset + + let height = Constraints.height + topInset + + return CGSize(width: width, height: height) } - private static func calculateForwardLabelSize() -> CGSize { - let insets = 2 * Constraints.forwardLabel.horizontalInset - - let maxWidth = BaseChatCell.Constraints.bubble.maxWidth - CGFloat(insets) + private static func calculateForwardLabelSize(maxWidth: CGFloat) -> CGSize { + let maxWidth = maxWidth - Constraints.horizontalContentInset let text = String.localizable.forwardedMessage - var textSize = text.boundingRect(with: CGSize(width: maxWidth, height: 0), options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: MessageForwardView.forwardFont as Any], context: nil).size + var textSize = text.boundingRect(with: CGSize(width: maxWidth, height: 0), + options: .usesLineFragmentOrigin, + attributes: [.font: forwardFont], + context: nil).size textSize.width = ceil(min(maxWidth, textSize.width)) textSize.height = ceil(textSize.height) return textSize } - } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageImageView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageImageView.swift index e57aae06b..6ab61a114 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageImageView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageImageView.swift @@ -6,19 +6,21 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // -class MessageImageView: MessageBaseImageView { +final class MessageImageView: MessageBaseImageView { - var imageSize: CGSize = .zero weak var dataSource: MessageBaseImageViewDataSource? - // MARK: - BubbleInjectible - // MARK: Init + // MARK: - Init + convenience init(contentAppearance: MessageContentAppearance?, dataSource: MessageBaseImageViewDataSource?) { self.init(contentAppearance: contentAppearance) self.dataSource = dataSource } + + // MARK: - BubbleInjectible + override func configure(with model: BaseChatCellModel) { super.configure(with: model) @@ -40,7 +42,8 @@ class MessageImageView: MessageBaseImageView { } } - static func size(for model: BaseChatCellModel) -> CGSize? { + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + // FIXME: consider maxWidth let size = sizeСonsideringTimestamp(for: model) return adjustedSize(size) } @@ -52,7 +55,11 @@ class MessageImageView: MessageBaseImageView { } } + +// MARK: - ImagePreviewTransitionAnimatable + extension MessageImageView: ImagePreviewTransitionAnimatable { + var transitionImageView: UIImageView? { return imageView } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageLocationView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageLocationView.swift index e2cc2c6e6..9e16c07da 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageLocationView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageLocationView.swift @@ -6,9 +6,22 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // -class MessageLocationView: MessageBaseImageView { +final class MessageLocationView: MessageBaseImageView { - //MARK: - BubbleInjectible + // MARK: - Constraints + + private enum Constraints { + + enum ImageView { + static let size = CGSize(width: width, height: height) + static let width = CGFloat(236.adjustedByWidth) + static let height = CGFloat(116.adjustedByWidth) + } + } + + + // MARK: - BubbleInjectible + override func configure(with model: BaseChatCellModel) { super.configure(with: model) @@ -19,19 +32,8 @@ class MessageLocationView: MessageBaseImageView { imageView.setImage(url: model.text?.locationUrl, placeHolder: UIImage.nynja.camera.image) } - static func size(for model: BaseChatCellModel) -> CGSize? { + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + // FIXME: consider maxWidth return adjustedSize(Constraints.ImageView.size) } } - -private extension MessageLocationView { - - enum Constraints { - - enum ImageView { - static let size = CGSize(width: width, height: height) - static let width = CGFloat(236.adjustedByWidth) - static let height = CGFloat(116.adjustedByWidth) - } - } -} diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessagePlaceView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessagePlaceView.swift index 163e07271..ddff68bc4 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessagePlaceView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessagePlaceView.swift @@ -8,7 +8,7 @@ import SnapKit -class MessagePlaceView: MessageContentView { +final class MessagePlaceView: MessageContentView { override var activatedViews: [UIView] { return [addressLabel] @@ -32,8 +32,10 @@ class MessagePlaceView: MessageContentView { private var rightConstraint: Constraint! + // MARK: - Views - lazy var imageView: UIImageView = { + + private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFill @@ -53,7 +55,7 @@ class MessagePlaceView: MessageContentView { return imageView }() - lazy var nameLabel: UILabel = { + private lazy var nameLabel: UILabel = { let height = Constraints.nameLabel.height let label = UILabel(height: height, color: UIColor.nynja.darkLight, fontName: FontFamily.NotoSans.regular.name) @@ -67,7 +69,7 @@ class MessagePlaceView: MessageContentView { return label }() - lazy var addressLabel: UILabel = { + private lazy var addressLabel: UILabel = { let height = Constraints.addressLabel.height let label = UILabel(height: height, color: UIColor.nynja.manatee, fontName: FontFamily.NotoSans.regular.name) @@ -84,7 +86,9 @@ class MessagePlaceView: MessageContentView { return label }() + // MARK: - Layout + override func layoutSubviews() { super.layoutSubviews() adjustCornerRadius() @@ -96,7 +100,9 @@ class MessagePlaceView: MessageContentView { } } + // MARK: - BubbleInjectible + override func configure(with model: BaseChatCellModel) { super.configure(with: model) @@ -110,14 +116,14 @@ class MessagePlaceView: MessageContentView { } } - static func size(for model: BaseChatCellModel) -> CGSize? { + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + // FIXME: consider maxWidth return CGSize(width: Constraints.width, height: Constraints.height) } override var infoInset: CGPoint { return Constraints.infoView.inset } - } // MARK: - Constraints diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageStickerView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageStickerView.swift index a25780bdb..d4b6bde4e 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageStickerView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageStickerView.swift @@ -12,10 +12,18 @@ final class MessageStickerView: MessageContentView, BubbleImageSizeCalculatable static var imageInset: CGFloat = 0 - static var maxSide = CGFloat(174.0).adjustedByWidth - static var minSide = CGFloat(174.0).adjustedByWidth + static let maxSide = CGFloat(174.0).adjustedByWidth + static let minSide = CGFloat(174.0).adjustedByWidth + + + // MARK: - Constraints + + private enum StickerConstraints { + enum infoView { + static let inset = CGPoint(x: 8.0.adjustedByWidth, y: 8.0.adjustedByWidth) + } + } - var imageSize: CGSize = .zero // MARK: - Views @@ -23,36 +31,28 @@ final class MessageStickerView: MessageContentView, BubbleImageSizeCalculatable return [] } - lazy var imageView: UIImageView = { - let v = UIImageView() - - v.contentMode = .scaleAspectFit - v.clipsToBounds = true + private(set) lazy var imageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFit + imageView.clipsToBounds = true - self.addSubview(v) - v.snp.makeConstraints { make in - make.edges.equalToSuperview() + addSubview(imageView) + imageView.snp.makeConstraints { maker in + maker.edges.equalToSuperview() } - return v + return imageView }() + // MARK: - Setup + override func baseSetup() { super.baseSetup() imageView.isHidden = false } - // MARK: - Constraints - - enum StickerConstraints { - enum infoView { - static let inset = CGPoint(x: 8.0.adjustedByWidth, y: 8.0.adjustedByWidth) - } - } - - // MARK: - BubbleInjectible override func configure(with model: BaseChatCellModel) { @@ -76,7 +76,8 @@ final class MessageStickerView: MessageContentView, BubbleImageSizeCalculatable return StickerConstraints.infoView.inset } - static func size(for model: BaseChatCellModel) -> CGSize? { + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + // FIXME: consider maxWidth let size = calculateImageSize(from: model.imageSize) return adjustedSize(size) } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageTextView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageTextView.swift index 39e1c726f..eae0ddee8 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageTextView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageTextView.swift @@ -12,7 +12,7 @@ protocol MessageTextViewDelegate: class { func showMenuForURL(_ url: URL) } -class MessageTextView: MessageContentView { +final class MessageTextView: MessageContentView { weak var delegate: MessageTextViewDelegate? @@ -26,21 +26,29 @@ class MessageTextView: MessageContentView { return [self, textView] } - class var textViewBottomInset: CGFloat { + private static var textViewBottomInset: CGFloat { return CGFloat(21.0.adjustedByWidth) } var textViewEdges: UIEdgeInsets { - return UIEdgeInsets(top: Constraints.textView.topInset, left: Constraints.textView.horizontalInset, bottom: MessageTextView.textViewBottomInset, right: Constraints.textView.horizontalInset) + return UIEdgeInsets(top: Constraints.textView.topInset, + left: Constraints.textView.horizontalInset, + bottom: MessageTextView.textViewBottomInset, + right: Constraints.textView.horizontalInset) } + + // MARK: - Init + convenience init(contentAppearance: MessageContentAppearance?, delegate: MessageTextViewDelegate?) { self.init(contentAppearance: contentAppearance) self.delegate = delegate } + // MARK: - Views - lazy var textView: UITextView = { + + private lazy var textView: UITextView = { let v = UITextView() v.isUserInteractionEnabled = true @@ -52,16 +60,17 @@ class MessageTextView: MessageContentView { v.textContainerInset = .zero v.textContainer.lineFragmentPadding = 0 - v.linkTextAttributes = [NSAttributedStringKey.foregroundColor.rawValue:UIColor.nynja.blue, - NSAttributedStringKey.underlineColor.rawValue: UIColor.nynja.blue, - NSAttributedStringKey.underlineStyle.rawValue: NSUnderlineStyle.styleSingle.rawValue - ] as [String : Any] + v.linkTextAttributes = [ + NSAttributedStringKey.foregroundColor.rawValue:UIColor.nynja.blue, + NSAttributedStringKey.underlineColor.rawValue: UIColor.nynja.blue, + NSAttributedStringKey.underlineStyle.rawValue: NSUnderlineStyle.styleSingle.rawValue + ] as [String : Any] self.addSubview(v) v.snp.makeConstraints { make in make.edges.equalTo(textViewEdges) - make.width.height.equalTo(0) } + return v }() @@ -106,16 +115,13 @@ class MessageTextView: MessageContentView { textView.attributedText = prepareText(in: model) textView.accessibilityIdentifier = model.text - let size = MessageTextView.calculateSize(for: model) - textView.snp.updateConstraints { $0.size.equalTo(size) } - mentionTapRecognizer.resetSelection() linkTapRecognizer.resetSelection() linkLongPressRecognizer.resetSelection() } - class func size(for model: BaseChatCellModel) -> CGSize? { - var textSize = calculateSize(for: model) + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + var textSize = calculateSize(for: model, maxWidth: maxWidth) textSize.height += Constraints.textView.topInset + textViewBottomInset textSize.width += 2 * Constraints.textView.horizontalInset return textSize @@ -177,10 +183,10 @@ class MessageTextView: MessageContentView { // MARK: - Layout Size - static func calculateSize(for model: BaseChatCellModel) -> CGSize { + private static func calculateSize(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { let insets = 2 * Constraints.textView.horizontalInset - let maxWidth = BaseChatCell.Constraints.bubble.maxWidth - insets + let maxWidth = maxWidth - insets var minWidth: CGFloat = InfoViewFactory.width(for: model) - Constraints.textView.horizontalInset if let headerWidth = BaseChatCell.calculateSenderHeaderSize(for: model)?.width { @@ -199,8 +205,11 @@ class MessageTextView: MessageContentView { } } + // MARK: - Constraints -extension MessageTextView { + +private extension MessageTextView { + enum Constraints { enum textView { diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageTransferView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageTransferView.swift index e70c48a96..063ef35b0 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageTransferView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageTransferView.swift @@ -22,7 +22,8 @@ final class MessageTransferView: MessageContentView { // MARK: - Views - lazy var textView: UITextView = { + + private lazy var textView: UITextView = { let textView = UITextView() textView.isUserInteractionEnabled = true @@ -36,32 +37,33 @@ final class MessageTransferView: MessageContentView { textView.snp.makeConstraints { make in make.edges.equalTo(textViewEdges) - make.width.height.equalTo(0) } + return textView }() - // MARK: - BaseView + + // MARK: - Setup + override func baseSetup() { textView.textContainerInset = .zero } + // MARK: - BubbleInjectible + override func configure(with model: BaseChatCellModel) { super.configure(with: model) guard model.type == .transfer else { return } textView.attributedText = MessageTransferView.prepareText(in: model) - - let size = MessageTransferView.textSize(in: model) - textView.snp.updateConstraints { $0.size.equalTo(size) } } - class func size(for model: BaseChatCellModel) -> CGSize? { - var size = textSize(in: model) + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + var size = textSize(in: model, maxWidth: maxWidth) size.height += Constraints.textView.topInset + Constraints.textView.bottomInset - return CGSize(width: Constraints.maxWidth, height: size.height) + return CGSize(width: maxWidth, height: size.height) } override var infoInset: CGPoint { @@ -145,11 +147,11 @@ final class MessageTransferView: MessageContentView { return finalString } - private static func textSize(in model: BaseChatCellModel) -> CGSize { + private static func textSize(in model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { let insets = 2 * Constraints.textView.horizontalInset - let maxWidth = Constraints.maxWidth - insets + let maxWidth = maxWidth - insets var minWidth: CGFloat = InfoViewFactory.width(for: model) - Constraints.textView.horizontalInset if let headerWidth = BaseChatCell.calculateSenderHeaderSize(for: model)?.width { @@ -172,8 +174,6 @@ final class MessageTransferView: MessageContentView { private extension MessageTransferView { enum Constraints { - - static let maxWidth = BaseChatCell.Constraints.bubble.maxWidth static let height = CGFloat(80.adjustedByWidth) enum textView { diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageVideoView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageVideoView.swift index 2e2af335f..df02aea45 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageVideoView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageVideoView.swift @@ -6,37 +6,51 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // -class MessageVideoView: MessageBaseImageView { +final class MessageVideoView: MessageBaseImageView { weak var dataSource: MessageBaseImageViewDataSource? - private let playVideoImageWidth = 44.adjustedByWidth + + + // MARK: - Constraints + + private enum Constraints { + static let playVideoImageWidth = 44.adjustedByWidth + } + // MARK: - Views - lazy var playVideoImage: UIImageView = { + + private lazy var playVideoImage: UIImageView = { let v = UIImageView() v.image = UIImage.nynja.Messages.Media.icBtnPlay.image self.addSubview(v) v.snp.makeConstraints { make in make.center.equalToSuperview() - make.width.height.equalTo(playVideoImageWidth) + make.width.height.equalTo(Constraints.playVideoImageWidth) } return v }() - // MARK: Init + + // MARK: - Init + convenience init(contentAppearance: MessageContentAppearance?, dataSource: MessageBaseImageViewDataSource?) { self.init(contentAppearance: contentAppearance) self.dataSource = dataSource } + // MARK: - Base Setup + override func baseSetup() { super.baseSetup() playVideoImage.isHidden = false } + // MARK: - BubbleInjectible + override func configure(with model: BaseChatCellModel) { super.configure(with: model) @@ -58,12 +72,13 @@ class MessageVideoView: MessageBaseImageView { } } - static func size(for model: BaseChatCellModel) -> CGSize? { + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + // FIXME: consider maxWidth let size = sizeСonsideringTimestamp(for: model) return adjustedSize(size) } - static func sizeСonsideringTimestamp(for model: BaseChatCellModel) -> CGSize { + private static func sizeСonsideringTimestamp(for model: BaseChatCellModel) -> CGSize { let size = calculateImageSize(from: model.imageSize) let minWidth = InfoViewFactory.width(for: model) + CGFloat(MessageBaseImageView.Constraints.Common.padding) return CGSize(width: max(minWidth, size.width), height: size.height) diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageVoiceView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageVoiceView.swift index ba6fe20d1..f60d9e7e4 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageVoiceView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Message/MessageVoiceView.swift @@ -15,7 +15,7 @@ protocol MessageVoiceViewDelegate: class { func isCanChangeProgress() -> Bool } -class MessageVoiceView: MessageContentView { +final class MessageVoiceView: MessageContentView { weak var delegate: MessageVoiceViewDelegate? @@ -23,8 +23,10 @@ class MessageVoiceView: MessageContentView { return [self, micView, micImageView, durationLabel, waveformView] } + // MARK: - Views - lazy var micView: UIView = { + + private lazy var micView: UIView = { let view = UIView() self.addSubview(view) view.snp.makeConstraints { make in @@ -35,7 +37,7 @@ class MessageVoiceView: MessageContentView { return view }() - lazy var micImageView: UIImageView = { + private lazy var micImageView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage.nynja.icMicDarkGray.image @@ -50,7 +52,7 @@ class MessageVoiceView: MessageContentView { return imageView }() - lazy var durationLabel: UILabel = { + private lazy var durationLabel: UILabel = { let height = Constraints.durationLabel.height let label = AlignableLabel(size: CGSize(width: 100, height: height), color: UIColor.nynja.darkLight, fontName: FontFamily.NotoSans.regular.name, textAlignment: .center) label.verticalAlignement = .top @@ -67,7 +69,7 @@ class MessageVoiceView: MessageContentView { return label }() - lazy var waveformView: DrawableAudioWaveform = { + private lazy var waveformView: DrawableAudioWaveform = { let view = DrawableAudioWaveform() view.delegate = self view.backgroundColor = UIColor.clear @@ -83,7 +85,7 @@ class MessageVoiceView: MessageContentView { return view }() - lazy var playButton: UIButton = { + private lazy var playButton: UIButton = { let v = UIButton() v.contentVerticalAlignment = .center @@ -152,6 +154,7 @@ class MessageVoiceView: MessageContentView { playButton.roundCorners([.topRight], radius: playButtonRadius) } + // MARK: - Actions @objc private func playTapped() { @@ -175,8 +178,8 @@ class MessageVoiceView: MessageContentView { setup(model) } - static func size(for model: BaseChatCellModel) -> CGSize? { - return CGSize(width: Constraints.width, height: Constraints.height) + override class func size(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + return CGSize(width: maxWidth, height: Constraints.height) } override var infoInset: CGPoint { @@ -253,12 +256,11 @@ extension MessageVoiceView: DrawableAudioWaveformDelegate { } } -// MARK: - Cosntraints -extension MessageVoiceView { +// MARK: - Layout +private extension MessageVoiceView { enum Constraints { - static let width = BaseChatCell.Constraints.bubble.maxWidth static let height = CGFloat(playButton.height) enum micView { diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/MessageRepliedView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/MessageRepliedView.swift index ff4ce698a..90a5c191b 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/MessageRepliedView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/MessageRepliedView.swift @@ -139,11 +139,17 @@ final class MessageRepliedView: MessageContainerView, ReplyInfoViewDelegate { return inset } + + // MARK: - ReplyInfoViewDelegate + func calculateLabelSize(with text: String, font: UIFont) -> CGSize { let insets = CGFloat(ReplyInfoView.Constraints.authorLabel.horizontalInset * 2) let maxWidth = width - insets - var textSize = text.boundingRect(with: CGSize(width: maxWidth, height: 0), options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font as Any], context: nil).size + var textSize = text.boundingRect(with: CGSize(width: maxWidth, height: 0), + options: .usesLineFragmentOrigin, + attributes: [.font: font], + context: nil).size textSize.width = ceil(min(maxWidth, textSize.width)) textSize.height = ceil(textSize.height) @@ -151,18 +157,26 @@ final class MessageRepliedView: MessageContainerView, ReplyInfoViewDelegate { return textSize } - static func size(for model: BaseChatCellModel) -> CGSize? { - let width = 2 * Constraints.contentView.inset + + // MARK: - Size + + class func size(for model: BaseChatCellModel, maxWidth: CGFloat, contentSize: (CGFloat) -> CGSize) -> CGSize { + let maxContentWidth = maxWidth - Constraints.horizontalContentInset + let contentSize = contentSize(maxContentWidth) - if case let .reply(repliedModel, _)? = model.messageType.ejectReply, repliedModel.type == .audio { - return CGSize(width: width, height: Constraints.expandedHeight) - } + let containerSize = self.containerSize(for: model, maxWidth: maxWidth) - return CGSize(width: width, height: Constraints.height) + return contentSize + containerSize } - static func size(for model: BaseChatCellModel, messageContentSize: CGSize) -> CGSize { - return (size(for: model) ?? .zero) + messageContentSize + private static func containerSize(for model: BaseChatCellModel, maxWidth: CGFloat) -> CGSize { + let horizontalInset = Constraints.horizontalContentInset + + if case let .reply(repliedModel, _)? = model.messageType.ejectReply, repliedModel.type == .audio { + return CGSize(width: horizontalInset, height: Constraints.expandedHeight) + } + + return CGSize(width: horizontalInset, height: Constraints.height) } } @@ -171,6 +185,10 @@ extension MessageRepliedView { enum Constraints { + static var horizontalContentInset: CGFloat { + return contentView.inset * 2 + } + static let height = replyInfoView.height + contentView.inset static let expandedHeight = replyInfoView.expandedHeight + contentView.inset diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/MessageStickerRepliedView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/MessageStickerRepliedView.swift index 294bdfb35..8524b8cf9 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/MessageStickerRepliedView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/MessageStickerRepliedView.swift @@ -92,6 +92,7 @@ class MessageStickerRepliedView: MessageContainerView, ReplyInfoViewDelegate { replyInfoView.roundCorners(corners, radius: radius) } + // MARK: - Actions @objc private func infoTapped() { @@ -134,6 +135,9 @@ class MessageStickerRepliedView: MessageContainerView, ReplyInfoViewDelegate { return CGPoint(x: inset.x + replyInset.x, y: inset.y + replyInset.y) } + + // MARK: - ReplyInfoViewDelegate + func calculateLabelSize(with text: String, font: UIFont) -> CGSize { let insets = 2 * ReplyInfoView.Constraints.authorLabel.horizontalInset @@ -147,10 +151,15 @@ class MessageStickerRepliedView: MessageContainerView, ReplyInfoViewDelegate { return textSize } - static func size(for model: BaseChatCellModel, messageContentSize: CGSize) -> CGSize { - var size = messageContentSize - size.width += Constraints.replyWidth - return size + + // MARK: - Layout Size + + static func size(for model: BaseChatCellModel, maxWidth: CGFloat, with contentType: BubbleInjectible.Type) -> CGSize { + var contentSize = contentType.size(for: model, maxWidth: maxWidth) + // FIXME: consider maxWidth + contentSize.width += Constraints.replyWidth + + return contentSize } } @@ -158,7 +167,7 @@ class MessageStickerRepliedView: MessageContainerView, ReplyInfoViewDelegate { extension MessageStickerRepliedView { enum Constraints { - + static let replyWidth = replyInfoView.maxWidth + contentView.horizontalInset + contentView.inset enum replyInfoView { diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/ReplyInfoView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/ReplyInfoView.swift index fbf114389..8d971ad2b 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/ReplyInfoView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Reply/ReplyInfoView.swift @@ -45,7 +45,6 @@ final class ReplyInfoView: BaseView { let color = UIColor.nynja.darkLight let label = UILabel(height: height, color: color, fontName: FontFamily.NotoSans.regular.name) - label.setContentCompressionResistancePriority(.required, for: .horizontal) let horizontalInset = Constraints.authorLabel.horizontalInset @@ -82,8 +81,6 @@ final class ReplyInfoView: BaseView { let height = Constraints.voiceView.height let label = UILabel(height: height, color: UIColor.nynja.manatee, fontName: FontFamily.NotoSans.regular.name) - label.setContentCompressionResistancePriority(.required, for: .horizontal) - voiceView.addSubview(label) label.snp.makeConstraints { make in make.top.bottom.left.equalToSuperview() @@ -92,12 +89,11 @@ final class ReplyInfoView: BaseView { return label }() - private(set) lazy var dateLabel: UILabel = { + private(set) lazy var dateLabel: UILabel = { let height = Constraints.voiceView.height let label = UILabel(height: height, color: UIColor.nynja.manatee, fontName: FontFamily.NotoSans.regular.name) - + label.setContentHuggingPriority(.required, for: .horizontal) - label.setContentCompressionResistancePriority(.required, for: .horizontal) voiceView.addSubview(label) label.snp.makeConstraints { make in diff --git a/Nynja/Modules/Message/WireFrame/MessageWireframe.swift b/Nynja/Modules/Message/WireFrame/MessageWireframe.swift index 7b6e4713f..bdb7e114f 100644 --- a/Nynja/Modules/Message/WireFrame/MessageWireframe.swift +++ b/Nynja/Modules/Message/WireFrame/MessageWireframe.swift @@ -161,4 +161,8 @@ class MessageWireFrame: MessageWireframeProtocol, DocumentInteractionWireFrame { guard let navigation = self.navigation else { return } TransferHistoryWireFrame().presentTransferHistory(navigation: navigation, for: profile) } + + func notifyAvailability(snackBar: SnackBar, isAvailable: Bool) { + main?.notifyAvailability(snackBar: snackBar, isAvailable: isAvailable) + } } diff --git a/Nynja/Modules/OtherUser/WireFrame/OtherUserWireFrame.swift b/Nynja/Modules/OtherUser/WireFrame/OtherUserWireFrame.swift index 246112d57..e074b6239 100644 --- a/Nynja/Modules/OtherUser/WireFrame/OtherUserWireFrame.swift +++ b/Nynja/Modules/OtherUser/WireFrame/OtherUserWireFrame.swift @@ -98,6 +98,6 @@ class OtherUserWireFrame: OtherUserWireframeProtocol { } func showLanguageSettings(_ contact: Contact) { - ChatLanguageSettingsWireframe().present(navigation: navigation!, main: main, type: .contact(contact.phoneId)) + ChatLanguageSettingsWireframe().present(navigation: (main?.navigation!)!, main: main, type: .contact(contact.phoneId)) } } diff --git a/Nynja/Modules/Profile/View/TableView/Cells/ScheduledMesssageCell/ProfileScheduledMesssageCell.swift b/Nynja/Modules/Profile/View/TableView/Cells/ScheduledMesssageCell/ProfileScheduledMesssageCell.swift index 287f4e3e7..58beed53e 100644 --- a/Nynja/Modules/Profile/View/TableView/Cells/ScheduledMesssageCell/ProfileScheduledMesssageCell.swift +++ b/Nynja/Modules/Profile/View/TableView/Cells/ScheduledMesssageCell/ProfileScheduledMesssageCell.swift @@ -203,7 +203,7 @@ class ProfileScheduledMesssageCell: UITableViewCell, ConfigurableCell { let date = Date(timestamp: time) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "EEEE MMMM d, yyyy".localizedDateFormat - dateFormatter.locale = Locale(identifier: Language.current.rawValue) + dateFormatter.locale = Locale(identifier: AppLanguage.current.rawValue) dateFormatter.timeZone = with.timeZone dateLabel.text = dateFormatter.string(from: date) } @@ -213,7 +213,7 @@ class ProfileScheduledMesssageCell: UITableViewCell, ConfigurableCell { let date = Date(timestamp: time) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "h:mm a".localizedDateFormat - dateFormatter.locale = Locale(identifier: Language.current.rawValue) + dateFormatter.locale = Locale(identifier: AppLanguage.current.rawValue) dateFormatter.timeZone = with.timeZone let timeZoneTime = dateFormatter.string(from: date) dateFormatter.timeZone = TimeZone.current diff --git a/Nynja/Modules/Replies/Interactor/RepliesInteractor.swift b/Nynja/Modules/Replies/Interactor/RepliesInteractor.swift index 7791c2480..76282e337 100644 --- a/Nynja/Modules/Replies/Interactor/RepliesInteractor.swift +++ b/Nynja/Modules/Replies/Interactor/RepliesInteractor.swift @@ -129,9 +129,9 @@ final class RepliesInteractor: BaseInteractor, RepliesInteractorInputProtocol, M // MARK: - MessageProcessingDelegate - func updateProgress(_ progress: ProgressModel) { + func update(progress: ProgressModel) { dispatchAsyncMain { [weak presenter] in - presenter?.updateProgress(progress) + presenter?.update(progress: progress) } } diff --git a/Nynja/Modules/Replies/Presenter/RepliesPresenter.swift b/Nynja/Modules/Replies/Presenter/RepliesPresenter.swift index d57886a32..9cd4aa015 100644 --- a/Nynja/Modules/Replies/Presenter/RepliesPresenter.swift +++ b/Nynja/Modules/Replies/Presenter/RepliesPresenter.swift @@ -46,8 +46,8 @@ class RepliesPresenter: BasePresenter, RepliesPresenterProtocol, RepliesInteract view.updateRepliesNumberLabel(replied: replied) } - func updateProgress(_ model: ProgressModel) { - view.updateProgress(progressModel: model) + func update(progress: ProgressModel) { + view.update(progress: progress) } func setup(messages: [Message], mentions: [String: [MentionInfo]], progressModels: [URL : ProgressModel], diff --git a/Nynja/Modules/Replies/RepliesProtocols.swift b/Nynja/Modules/Replies/RepliesProtocols.swift index a5bdecdb0..a40350125 100644 --- a/Nynja/Modules/Replies/RepliesProtocols.swift +++ b/Nynja/Modules/Replies/RepliesProtocols.swift @@ -44,7 +44,7 @@ protocol RepliesInteractorOutputProtocol: class { func updateHeaderView(replied: Int) func setup(messages: [Message], mentions: [String: [MentionInfo]], progressModels: [URL : ProgressModel], position: PositionType, reader: Int64?) - func updateProgress(_ model: ProgressModel) + func update(progress: ProgressModel) } protocol RepliesInteractorInputProtocol: BaseInteractorProtocol { @@ -72,5 +72,5 @@ protocol RepliesViewProtocol: class { func removeReplies(_ ids: [String]) func insertReplies(_ cells: [BaseChatCellModel]) - func updateProgress(progressModel: ProgressModel?) + func update(progress: ProgressModel?) } diff --git a/Nynja/Modules/Replies/View/RepliesVC.swift b/Nynja/Modules/Replies/View/RepliesVC.swift index fc0d20fc8..967933a6f 100644 --- a/Nynja/Modules/Replies/View/RepliesVC.swift +++ b/Nynja/Modules/Replies/View/RepliesVC.swift @@ -149,13 +149,13 @@ final class RepliesVC: BaseVC, RepliesViewProtocol { } - func updateProgress(progressModel: ProgressModel?) { + func update(progress: ProgressModel?) { guard let dataSource = collectionViewDataSource else { return } - let url = progressModel?.url + let url = progress?.url let list = dataSource.cells.filter { $0.progressModel?.url == url } - list.forEach { $0.progressModel = progressModel } + list.forEach { $0.progressModel = progress } reloadIfVisible(models: list) } diff --git a/Nynja/Modules/ScheduleMessage/View/Views/DateTime/DateTimeItemView.swift b/Nynja/Modules/ScheduleMessage/View/Views/DateTime/DateTimeItemView.swift index abacd9319..90a412315 100644 --- a/Nynja/Modules/ScheduleMessage/View/Views/DateTime/DateTimeItemView.swift +++ b/Nynja/Modules/ScheduleMessage/View/Views/DateTime/DateTimeItemView.swift @@ -67,7 +67,7 @@ class DateTimeItemView: UIView { func setup(with: Date) { self.date = with let dateFormatter = DateFormatter() - dateFormatter.locale = Locale(identifier: Language.current.rawValue) + dateFormatter.locale = Locale(identifier: AppLanguage.current.rawValue) dateFormatter.dateFormat = "MMMM d, yyyy".localizedDateFormat let upText = dateFormatter.string(from: with) dateFormatter.dateFormat = "EEEE - h:mm a".localizedDateFormat diff --git a/Nynja/Modules/Settings/LanguageSettings/Interactor/LanguageSettingsInteractor.swift b/Nynja/Modules/Settings/LanguageSettings/Interactor/LanguageSettingsInteractor.swift index fa3ef1a1f..ac89bd91d 100644 --- a/Nynja/Modules/Settings/LanguageSettings/Interactor/LanguageSettingsInteractor.swift +++ b/Nynja/Modules/Settings/LanguageSettings/Interactor/LanguageSettingsInteractor.swift @@ -10,16 +10,16 @@ class LanguageSettingsInteractor: LanguageSettingsInteractorInputProtocol { weak var presenter: LanguageSettingsInteractorOutputProtocol! - func getLanguges() -> [Language] { - return Language.allValuesSelectedFirst + func getLanguges() -> [AppLanguage] { + return AppLanguage.allValuesSelectedFirst } - func getSelectedLanguage() -> Language { - return Language.current + func getSelectedLanguage() -> AppLanguage { + return AppLanguage.current } - func setSelectedLanguage(_ language: Language) { - Language.current = language + func setSelectedLanguage(_ language: AppLanguage) { + AppLanguage.current = language } } diff --git a/Nynja/Modules/Settings/LanguageSettings/LanguageSettingsProtocols.swift b/Nynja/Modules/Settings/LanguageSettings/LanguageSettingsProtocols.swift index eb5e83586..69bbb0af6 100644 --- a/Nynja/Modules/Settings/LanguageSettings/LanguageSettingsProtocols.swift +++ b/Nynja/Modules/Settings/LanguageSettings/LanguageSettingsProtocols.swift @@ -36,9 +36,9 @@ protocol LanguageSettingsPresenterProtocol: BasePresenterProtocol { /** * Add here your methods for communication VIEW -> PRESENTER */ - func getLanguges() -> [Language] - func getSelectedLanguage() -> Language - func setSelectedLanguage(_ language: Language) + func getLanguges() -> [AppLanguage] + func getSelectedLanguage() -> AppLanguage + func setSelectedLanguage(_ language: AppLanguage) } @@ -56,7 +56,7 @@ protocol LanguageSettingsInteractorInputProtocol: class { /** * Add here your methods for communication PRESENTER -> INTERACTOR */ - func getLanguges() -> [Language] - func getSelectedLanguage() -> Language - func setSelectedLanguage(_ language: Language) + func getLanguges() -> [AppLanguage] + func getSelectedLanguage() -> AppLanguage + func setSelectedLanguage(_ language: AppLanguage) } diff --git a/Nynja/Modules/Settings/LanguageSettings/Presenter/LanguageSettingsPresenter.swift b/Nynja/Modules/Settings/LanguageSettings/Presenter/LanguageSettingsPresenter.swift index c49d7c526..690e8ff90 100644 --- a/Nynja/Modules/Settings/LanguageSettings/Presenter/LanguageSettingsPresenter.swift +++ b/Nynja/Modules/Settings/LanguageSettings/Presenter/LanguageSettingsPresenter.swift @@ -16,15 +16,15 @@ class LanguageSettingsPresenter: BasePresenter, LanguageSettingsPresenterProtoco var interactor: LanguageSettingsInteractorInputProtocol! var wireFrame: LanguageSettingsWireFrameProtocol! - func getLanguges() -> [Language] { + func getLanguges() -> [AppLanguage] { return interactor.getLanguges() } - func getSelectedLanguage() -> Language { + func getSelectedLanguage() -> AppLanguage { return interactor.getSelectedLanguage() } - func setSelectedLanguage(_ language: Language) { + func setSelectedLanguage(_ language: AppLanguage) { if language.hasResource() { interactor.setSelectedLanguage(language) wireFrame.restart() diff --git a/Nynja/Modules/Settings/Security/Interactor/SecurityInteractor.swift b/Nynja/Modules/Settings/Security/Interactor/SecurityInteractor.swift index e497dfc85..a1b34aa39 100644 --- a/Nynja/Modules/Settings/Security/Interactor/SecurityInteractor.swift +++ b/Nynja/Modules/Settings/Security/Interactor/SecurityInteractor.swift @@ -110,7 +110,7 @@ class SecurityInteractor: SecurityInteractorInputProtocol, AuthHandlerDelegate, func buildCurrentSessionInfoOfflineMode() -> Auth { let auth = Auth() auth.dev_key = UIDevice.current.persistentIdentifier - auth.settings = FeatureFactory.getAuthSettingsToShowOffline() + auth.settings = FeatureFactory.authFeaturesToShowOffline return auth } diff --git a/Nynja/Modules/SettingsGroup/Interactor/SettingsGroupInteractor.swift b/Nynja/Modules/SettingsGroup/Interactor/SettingsGroupInteractor.swift index f8c1dd6d8..34f3422a2 100644 --- a/Nynja/Modules/SettingsGroup/Interactor/SettingsGroupInteractor.swift +++ b/Nynja/Modules/SettingsGroup/Interactor/SettingsGroupInteractor.swift @@ -6,6 +6,8 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // +import SDWebImage + final class SettingsGroupInteractor: BaseInteractor, SettingsGroupInteractorInputProtocol, ConnectionServiceDelegate, MQTTServiceDelegate, SetInjectable { @@ -73,18 +75,23 @@ MQTTServiceDelegate, SetInjectable { MQTTService.sharedInstance.updateRoomWith(id: id, name: name) } - func updateAvatar(_ id: String, _ url: URL, _ success : ((URL?)->())? = nil) { + func updateAvatar(_ id: String, _ localURL: URL) { let sync = SyncFileManager.sharedInstance sync.downloader = AmazonManager.shared - if let shrtPath = url.path.getShortPath() { - SyncFileManager.sharedInstance.updateDB(url: shrtPath, localUrl: shrtPath) { - success?(URL(string: shrtPath)) - } + + if let shortPath = localURL.path.getShortPath() { + SyncFileManager.sharedInstance.updateDB(url: shortPath, localUrl: shortPath, success: nil) } - SyncFileManager.sharedInstance.saveExternalFileLink(localUrl: url.path) { (ext, progress, request) in - guard let extUrl = ext else { return } - MQTTService.sharedInstance.updateRoomWith(id: id, avatar: String(describing: extUrl)) + SyncFileManager.sharedInstance.saveExternalFileLink(localUrl: localURL.path) { externalURL, progress, request in + guard let externalURL = externalURL else { + return + } + if let data = try? Data(contentsOf: localURL) { + // TODO: wrap into custom interface + SDImageCache.shared().storeImageData(toDisk: data, forKey: externalURL.absoluteString) + } + MQTTService.sharedInstance.updateRoomWith(id: id, avatar: String(describing: externalURL)) } } diff --git a/Nynja/Modules/SettingsGroup/Presenter/SettingsGroupPresenter.swift b/Nynja/Modules/SettingsGroup/Presenter/SettingsGroupPresenter.swift index 6a3623ad0..6a6e4be83 100644 --- a/Nynja/Modules/SettingsGroup/Presenter/SettingsGroupPresenter.swift +++ b/Nynja/Modules/SettingsGroup/Presenter/SettingsGroupPresenter.swift @@ -164,18 +164,10 @@ class SettingsGroupPresenter: BasePresenter, SettingsGroupPresenterProtocol, Cre } func photoSavedAtUrl(url: URL) { - guard let theRoom = self.room else { return } - interactor.updateAvatar(room.id!, url) { [unowned self] url in - theRoom.data?.first?.payload = url?.absoluteString - self.updateRoomInDB(with: theRoom) - self.view.setup(room: theRoom, avatarUrl: url, myAlias: (self.ownMember?.alias)!, isAdmin: self.isAdmin, isNotification: self.isNotifications) - } - } - - private func updateRoomInDB(with room: Room) { - DispatchQueue.global(qos: .userInitiated).async { - RoomDAO.patchRoom(room) + guard let roomId = room.id else { + return } + interactor.updateAvatar(roomId, url) } func participantsUpdated(result: ParticipantsResult) { diff --git a/Nynja/Modules/SettingsGroup/SettingsProtocols.swift b/Nynja/Modules/SettingsGroup/SettingsProtocols.swift index de69828ae..54f5dd120 100644 --- a/Nynja/Modules/SettingsGroup/SettingsProtocols.swift +++ b/Nynja/Modules/SettingsGroup/SettingsProtocols.swift @@ -88,7 +88,7 @@ protocol SettingsGroupInteractorInputProtocol: BaseInteractorProtocol { func muteUnmute() func updateName(_ id: String, _ name: String) - func updateAvatar(_ id: String, _ url: URL, _ success : ((URL?)->())?) + func updateAvatar(_ id: String, _ url: URL) func addMembers(_ id: String, _ members: [Member], availableHistoryCount: Int) func removeMembers(_ id: String, _ members: [Member]) func updateAdmins(_ id: String, _ toAdd: [Member], _ toRemove: [Member]) diff --git a/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift b/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift index 5a6b06a43..93aad380c 100644 --- a/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift +++ b/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift @@ -99,7 +99,7 @@ class SettingsGroupWireFrame: SettingsGroupWireframeProtocol { func showLanguageSettings(_ room: Room?) { guard let id = room?.id else { return } - ChatLanguageSettingsWireframe().present(navigation: navigation!, main: main, type: .room(id)) + ChatLanguageSettingsWireframe().present(navigation: (main?.navigation!)!, main: main, type: .room(id)) } func addParticipants(_ members: [Member]?) { diff --git a/Nynja/Modules/Splash/Interactor/SplashInteractor.swift b/Nynja/Modules/Splash/Interactor/SplashInteractor.swift index c791f0e52..cec6ce8a9 100644 --- a/Nynja/Modules/Splash/Interactor/SplashInteractor.swift +++ b/Nynja/Modules/Splash/Interactor/SplashInteractor.swift @@ -53,7 +53,7 @@ class SplashInteractor: SplashInteractorInputProtocol { private func decideRoute() { guard storageService.wasLogined else { - Language.current = .english + AppLanguage.current = .english presenter.showTutorial() return } diff --git a/Nynja/MotionManager/MotionManager.swift b/Nynja/MotionManager/MotionManager.swift deleted file mode 100644 index b0829e81e..000000000 --- a/Nynja/MotionManager/MotionManager.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// MotionManager.swift -// Nynja -// -// Created by Anton M on 17.08.2018. -// Copyright © 2018 TecSynt Solutions. All rights reserved. -// - -import Foundation -import CoreMotion - -class MotionManager { - - static let shared = MotionManager() - private let motion: CMMotionManager - private var timer: Timer? = nil - - private var axCoords: MotionCoords? - private var gCoords: MotionCoords? - - private init() { - motion = CMMotionManager() - } - - func startAccelerometers() { - #if !RELEASE - // Make sure the accelerometer hardware is available. - let interval: TimeInterval = 1.0 / 60 - - guard self.motion.isGyroAvailable, - self.motion.isAccelerometerAvailable else { return } - - self.motion.accelerometerUpdateInterval = interval - self.motion.gyroUpdateInterval = interval - self.motion.startGyroUpdates() - self.motion.startAccelerometerUpdates() - - self.timer = Timer(fire: Date(), interval: interval, repeats: true) { _ in - self.updates(ax: self.motion.accelerometerData, g: self.motion.gyroData) - } - - RunLoop.current.add(self.timer!, forMode: .defaultRunLoopMode) - #endif - } - - var res = 0 - var i = 1 - var y = 1 - - func updates(ax: CMAccelerometerData?, g: CMGyroData?) { - if let _g = self.gCoords,let new = MotionCoords(data: g) { - res += Int(new.z) - if res > 300, Int(new.z) > 0, i > 0 { - i -= 1 - res = 0 - } - if res < -300, Int(new.z) < 0, y > 0 { - y -= 1 - res = 0 - } - - if i == 0 && y == 0 { - guard let nav = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController else { return } - LogOutputWireFrame().presentLogOutputView(navigation: nav) - i = 1 - y = 1 - res = 0 - } - } else { self.gCoords = MotionCoords(data: g) } - } - -} - -struct MotionCoords { - var x: Double = 0 - var y: Double = 0 - var z: Double = 0 - - private init() { - - } - - init?(data: CMAccelerometerData?) { - guard let _data = data else { return nil } - self.x = _data.acceleration.x - self.y = _data.acceleration.y - self.z = _data.acceleration.z - } - - init?(data: CMGyroData?) { - guard let _data = data else { return nil } - self.x = _data.rotationRate.x - self.y = _data.rotationRate.y - self.z = _data.rotationRate.z - } - - func getDiff(newData: MotionCoords) -> MotionCoords { - var result = MotionCoords() - result.x = self.x + newData.x - result.y = self.y + newData.y - result.z = self.z + newData.z - return result - } - - var description: String { - return "x:\(self.x)\n y: \(self.y)\n z: \(self.z)" - } -} - -extension Double { - func absolute() -> Double { - if self < 0 { - return self * -1 - } - return self - } -} diff --git a/Nynja/NotificationManager.swift b/Nynja/NotificationManager.swift index 36e12a720..7b86fdf0a 100644 --- a/Nynja/NotificationManager.swift +++ b/Nynja/NotificationManager.swift @@ -333,28 +333,26 @@ final class NotificationManager { } private func getMessageText(message: Message, room: Room?) -> String? { - guard let lastAttachments = message.files?.last else { return nil } - guard let mime = lastAttachments.mime else { return nil } - guard let payload = lastAttachments.payload else { return nil } - - var result: String? = nil - - if mime == "text" { - if let systemMessage = message.systemMessage(for: room) { - result = systemMessage - } else { - if message.hasMentions { - let mentions = payloadParser.parse(message) - result = MessagePayloadRenderer.processPlainTextPayload(with: mentions, in: payload).text - } else { - result = payload - } - } - } else { - result = mime.capitalized + guard let desc = message.mainFile, + let type = desc.type, + let payload = desc.payload else { + return nil + } + + guard type == .text else { + return type.rawValue.capitalized + } + + if let systemMessage = message.systemMessage(for: room) { + return systemMessage + } + + if message.hasMentions { + let mentions = payloadParser.parse(message) + return MessagePayloadRenderer.processPlainTextPayload(with: mentions, in: payload).text } - return result + return payload } diff --git a/Nynja/P2pChatItemsFactory.swift b/Nynja/P2pChatItemsFactory.swift index ddb12e507..94b964727 100644 --- a/Nynja/P2pChatItemsFactory.swift +++ b/Nynja/P2pChatItemsFactory.swift @@ -39,7 +39,7 @@ class P2pChatItemsFactory: ChatBaseFactory { } override var secondLevelItems: ItemModels { - let items = [payment, location, contact, call, media, camera] + let items = [chatOptions, payment, location, contact, call, media, camera] items.forEach { item in let action = item.action diff --git a/Nynja/P2pChatsItemsFactory.swift b/Nynja/P2pChatsItemsFactory.swift index eb92e5e78..0a9d43d9f 100644 --- a/Nynja/P2pChatsItemsFactory.swift +++ b/Nynja/P2pChatsItemsFactory.swift @@ -12,7 +12,6 @@ class P2pChatsItemsFactory: ChatsItemsFactory { override var chats: ImageActionItemModel { let item = super.chats - // TODO: state item.state = .highlighted return item } @@ -36,9 +35,8 @@ class P2pChatsItemsFactory: ChatsItemsFactory { } override var new: WheelItemModel { - // TODO: need to implement new item - return ImageActionItemModel(nameImage: "ic_new_chat", navItem: .newChat, state: .disabled, action: { [weak navigateDelegate] (item, indexPath) in - + return ImageActionItemModel(nameImage: "ic_new_chat", navItem: .newChat, action: { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showByContacts(indexPath: indexPath) }) } diff --git a/Nynja/Resources/Assets.xcassets/Wheel/WheelItems/ic_p2p_chat_settings.imageset/Contents.json b/Nynja/Resources/Assets.xcassets/Wheel/WheelItems/ic_p2p_chat_settings.imageset/Contents.json new file mode 100644 index 000000000..96bd47b4e --- /dev/null +++ b/Nynja/Resources/Assets.xcassets/Wheel/WheelItems/ic_p2p_chat_settings.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_p2p_chat_settings.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template", + "preserves-vector-representation" : true + } +} \ No newline at end of file diff --git a/Nynja/Resources/Assets.xcassets/Wheel/WheelItems/ic_p2p_chat_settings.imageset/ic_p2p_chat_settings.pdf b/Nynja/Resources/Assets.xcassets/Wheel/WheelItems/ic_p2p_chat_settings.imageset/ic_p2p_chat_settings.pdf new file mode 100644 index 0000000000000000000000000000000000000000..92113ee9e8fc02183df536dfced75ce8b60dbd1b GIT binary patch literal 7596 zcmai(1yEaEw}!D6DMeab1H~N@++B;i6aoa7lHe3*u@-kIZiV9RL5e#Rw*sYjad)`z z$^E}?{yTHeoXOdF_sYu3*=x=`Z#J!}lng5;8xVuGbM0tt`|EM$_s(t%5P$>VXljEY zBm`hrf;d<@TLHKqBy|A0jJ2IJ1p08b13N>cAZCu{5P*mX2Fw`>0o!A^BYMxi9;Rt_ zLiy>Pkv;uXlWhvSsD28)=?L*DkDZCR*xerXMXTD$1VmEh9# znUdIf!JF@*nQV8p)K>4h!rn{BbKhUZ-L-BYWQ#4~@Qa6iXh!{sz`iZYmh7`>K9AO6 z|J-h)4(N|}WnNR)z_XXNOSiqX%8N~`;jn2QtlnkKoZYhd(kagR^!=*_OYHNpIn3E^ z$RpqyH=MF{oxMaiR^#RCF&VE6B>_HHGw^GKff5`*)VI!cLVBRLduq9}Q5LNGqttby6aFbP z=`F0N^OyhY!T|H zi3~m!th0V_$ZulVCv{E^6)ui=)F2T!Y}%o-x3)HSMFPD_cz?*L<%AO2#mRC@gRXp1 z6sk~J!awAUd{)XIF^J!P03#eFyUN5N4$O=>ew!R?@%~;gVW*#= zTA^PfaM&hO5#9X>oOjErPb1&a62D`9jFxk{Ma__Avu7F9yJAMyH_E$p-_<`+2A&o< z(|1x#@8e4)3;4`h!wy38BMvkdHbbM}muB&od1XtP&vNQ?pe?RtP<3vbOUOMg zBhv;V_Y*J1rJ^OHWk9@CV`>^|WJWdIwl|d5zdOy;py97k7Q^tl)(KgPGseqimJRwF zgxkzcR)RNE>i$&5zLw{5ag+?|_{!NPRG{GG2|t#CMY<62N>5T`3Zw+cRCovQl<2P z>h4(5NR{eog_~EX&56PX?X@zi))qY~-@?-+Xn3c}FMu@j0ctDk)Jc7ZwNurgr&ILV z0b3r0DGAvojD~rgu12TL8n1&nt`CcEEe2bG5)-$tD@)Gsw z9jGWvxS#I|o`|#@m!HWrB-a}2Tp@kYP@3o-)_Zk|&G~TwIbdi#S_f5)Jc0F%-P06u znWv0+fpUJIly;nPUvE2QO0g2S4wa-$mXK(o%FE|_o^M_JO0qGvnWM#I|IVkryq52& zw-M|xm3{x+;@(HcO~H1#a94tCWl>eF;rd9>AkIw*`^sxN1h zTQAs9PriSDS9jj3Lm5d<#{N9NLVp>}SFv~4OtkPV*|zqY2zUa-m*uh#9JIIttZE^u zxB1)YTVkD#jxpS$(V;n`b{qKJ1Bpa};Z4yq>S)=pDl-pyZjA5nw*e=kctK5aIjmOk z@{O(&rt-u4Ss6YTQmY7-!JjZ^)2I;K0&FS2uy8Ya(9yT%tU3?JbnDMk*gUUk`*<_1 zA~S0ukSZk|%es)=Hk11ygm&O{E$$+}Oe}$Lj@@e4^4ZR7o60zz{OZ%6cZTTwFIay% zhqrd;YicV5U7Q7GkJsdqZtj(~xu(AVXv>Dky{B1Tl=H4Qz#thBKV+dHZmp(GA0xqD z{#kMaer>qntZVJGTnI(|0!&q19H}B0X^qwm<%31Yfph<7560QG*dJIS#7>{g+(W z(P54AnSwI=I+Zp^73RyJ7-O`|kRav3`WF-IC6l?|odc1Njft{Jm9B|$Ute#sNJXvU zX`5n4;hs81m))xvjbQ-N_gDvH6C&ZCY%u*HvK^bj}4R#j`A$n(T@=2#5A9gM$v zs#H^F5w#G`QbdMiiY(nZx=zxE0iRMsayZ3|g`_uMsC2!s;M0e)*Yjs=R z`>9_O&Uu>Rgt7YnAQ~6Fvv@CqV=n5Gz$sXxl;_J6D`^!%{jjnB(z67tQ1s#k4+HN?6ax^VhIX-l7XT2&4{?s1r(LbMx_Zo^p>h{`A|ErWP( zD${WD=2gOX(87yT<@uzwx5R=)JUGxrnKGh)P z-Fwm**QCRS1mEXnQ>fPj^6OZeQSisaGL)4GO)3)RVi-mpy{mjz*Ba)e?!fG^xu%gV ztt!n$&mqwdK{;eJR?Ky`c5U)7Oo9Knvc$tL@t7;N>kxWf;6DlZ=%>Y^>BHON1J&0o zF^tpcr54{ZRL+-ZWbMD%8z&IHPI&hF^?R-@vN@-{c;<>$5SdfzMJ6wxLYB_KvaRk0AsbR zLQFAebP*l1>`)V`lX>pO>_%r{G?Pu2`r+i~B-;iuJk#{{vjfZKf!D*uMxLeuq^}ZB zYeF{!V4|(Q??BO&E9hSZd~|LTdZ`TPy_EI zogCMPk~zgdqh~tcbGE~Iy_2qA^l;0otBO)@WNbRt@;FVuzaK>Ir zAfiVT;g_i#O%26L@WEW*!Pi(WaZt;^%Lq?p=rQu(Ie9p1 zL%`}ezoid-ThXdxz#H*iiKH##yA~X3-(r-$k>rsP{V1|heLF8E$k`w_zIAuj#5_=@ zMj7@JLxD`)+x5=syHL0X(al_v`sdQ>h(h=}=sp!=?|^aiMqWj6px-*VXs2dYn#eTn zh8PCga#Ge_z^FdSFd9ao+y@LUyweMahW zq`R|I-j7(D7lp5`D9Lo$DmM$w#aIFE1DUEn%h50-FkJ%kL1x~eZK1Cfgep*ekPDtw=Qft)rrRp1#zy65JhQcXUZwfeO`i+|@L?xulC@*iK z_DYZy|2~A;oX&nIlyuXb&7u#%>uV{kMH|zGUz}1y&W6{W%f8x;4J#QRO5Q0@C+s*) zGi-o@S)4+;pr$==mSjTjX7rP4B7TW5czD<{>PX~n6+y3eG``4M(MwYk!Yp0F*UvsK zPV&>kd9gS9vxCX(#{V@D9 zdi^Uq!RHd?=2&HkFh5P)Tk0xlhI8MFFs6@qF*r{O#;_UR%nJ^}>LQ-3FNRyT>ZkA6 zo-xZ@BL7;gAcR?`dSo2)DX-DmQLr{#tS4W1#1&Y!%DvA zhX}==NQ+~X-6*I^+SQisr1*iutu3s_sl>umiz=kIz(||>jg&cf>wQe(kDK%Bw+XFs z)iEJlbYM+k$_Uf`+ zi8bw+_+b6&U~=B{ex+mAikZzzd*^j$F^ z4(5Lu;)l_rkA5`Ik5?cci05(kam@7(kFDwP76M>b1zY~{3WYd01Au?}ZFLCD(FJM- zfdP2_6eJxToFC?4fJcw6{^-5`%J(?`w`W&{I+|%foB;+8Nu^`}MgVq6M>|KT##^u% z1n_9;C1IQZ-oFz3BM09|0R%? z;HIfDOYm`|nYa1-3u*xE35h2Ei8ew;8^8Ina5IM1+HgrHlA&*}?)v_CgA2;-*zo{=kvlZzk=J-e8CrL)ZL?CUx+yzg@& z-D(zz-Y{_!C_N0Wx|mG6oD+tUW%!x)r)ADNR7kU?d@{n?Im7t)MXC}42VRo#;09fm zT`-q49`RoA5fTDgTql1M6f5+S-zpu!J*Y`GN+T!Lm=PR=@5>w$q!DTL?31jbcT322clD;$tu9+8c1F_0 z!_xJZ>~@OTn+XukX!KLat6Ji<5UTB;imfddtLy_1j-DV?FQy(-1lhfb>HLLB|15N8 z#}0;=Vavw6$$OpQ=h)vG#DAVNuy?I3m`5o3;gvi-5F)*n_cc#ZYH$z}D{;j9_8jMa z(PgkmF3-D-E6QhvAr8w*;?x@JXPA-Z zP}*!3;+&-byEdd3QK-(D7@!Yq97rrC&&l2c-y$yp5CY!I;UFgD`5i^skLT@ELlm8F&)+`7d@n185!9y0fw=g=8qMzj2R~c9FpA&{ zc9O&t4yitxhorbFA0Bd-WN;KtjM(LOx_lxg|H3RuRpFCY&LoX!M*dfssNVsgr?-gS z0l1%$#=*~L5NhAgmJ5=h$+i#tlyb*UMJ4<;zpPMCAc9rbPPj~ZfWQ)H+#ZW2){BNb zDx-@=dMG6krAvc|OJy5P{ROE&IynmK3u!~RT(r|F^3#u5;Lk(^-NY*&^}?1-wZT)S z3LKN4%|2@@5w7!C-~c~d`sJEVR^;i@7mK>0^Wdh0vvz8mM4A|sCsrWOfOiDRuzJ1` z{~~+qQ%_{b<-lEvQinYq27kZZ@qC%?(7lOi5pUlQyA5_=`i`mv!?6fp!~!+YVCukgwuQ9faf2X=ut=5iuj)Bu_@C9E&F(L_?@f z!-d^ z<{_D@{yg7dLbvd9W17B7vy``jH@j>sF!4BXrLQEmDB3oDJJE|)C2l&=Ezy`QgWj0R zSK%)2rXESHO{`7hLr%GNmdtW>yKtbqbaHmQ=_k`<(_mBoU))`4v`sM|V(MO~6o_q= zkJ08*kw}c^dW_XqzpfUTgUoS|%kqA?9*^DXI-%TbK0$}jLJZaz*2vaK)~fo|pSE+D zTmaRHbHif8;9-%+p!J54m63U#GV3?@O^orEOb$%%;;G{;;t}H`3zf7SC-ElJ3hfJ3 zCOB;9Y``|NHrJDEMd2e~N8Hj!(u_g(t2ZBimp2A`)?eA2cKjCp9ZQl-(n3N^qD7Jj z>gFa+c1Z3^UQ6Bp^;BEwKdV8}U(?^HdBYK(v^S_}L0c8dpH))2`;~Q6laEV!d zm9Axua@FulqZl>=Uhz7^It@1o6-9PkcH=U&{VWl+jFOBmt+K5KzWv|61LjOZ#Z%hy zvqG|3!Rg?fWwTd>(M6+7Ir=$*-F#-XP9cXPD+YAs!D;d_^5K22HD)FjHBA~vsz)$w z*RAM4McnY7?D8Ladc`tRnfx6>z3*~7QZAXF35V*2rV%U<7_;oMXs~XxQ0g)2J<+wV z{#}06G1r-FYh>o#6J)buRyCZruwGMAz`Eqxy6Y{NY*fBL09S^OoMD|&U(;Q4q6&X> z|JaOmKI81l01xn<#NFOpt{*-OTS2AwqOTa2|GJc>H_SNvd^qPBCgB0$1S=7A)M>e% z3>wX5&K73u{x)kuYd(HOfBS0i^>p}#Yo~qZ!i@0jZT&Sfwjy@4Y-ms1*Vqk`k!No{ zC6?(#>y*{SoSW|3?0*#z5D69u7kSy@+EV1regVEkx^=x;IG#S(xSGCJLE%FwM&HEv zf+B;WjVkht3;o3t7nF`R#5TAeCHt5$jo}0VlEjUqYoHK1H>MH(6^w|D1yoG8<592W z?&I!;!fGllCD{p=i|ifil?@FiP$Y1Z>67h}o|Jtp?IoQkt(d6GqF1#l6Q&;q$P*@@ zB*pilafKF~k1Z&ET-o`Bv9iYoG)$=4nS<7>6%34&Mj5y1IWbX?Qj#y>+y}pGFIZD5 zJuz-@TSJ+Sg@3$`+30582tPEsfZRl*BnDIlj7lYT8U>rlm&)VIuOO+2*Zg#;G(P&U zS`Zf%m%|7w7%dpG4yeAU9+#j6hmw7dKU1{ z5qCnE$ps}2L?eaC&*KHL`EZZ4KBu6IE#zZqU6&oc-R^=#Rf)}fBK4cmQ`3Q8A-_~;f5dDr>3Qp1E)1Rp>^A3~$JNA5xup}-?+^6foWwJj01?*ji=F76oF&i>ulYUyk2 zE43?k7(F5zD4W~U9_cbEI5Ewe&l|_n9v;(OvEtO@c>D8I((7f>mMSo(;R1z^ivRFe z&jsavI;PO9M~HXf7X59;S?zcI5dEyFA5+1#UwEv&l)RR<-jAn|50kTsrrf_f2|G(l zuIjf`9El#rPfg*^aPv8m=ngy#4JQ03pyMpkSiL%yR$5-VIPf8{?9E{Vv|zsZ`bI0U z$g-5*NYFdYyLmV0HhhW#o}OU5<72&5vv1$L^|tCZVWlLZ<%##kmC-f(;#s5jTEu-C zDRSu3t39?BulqMYgtxpGZ%0uJu-}Lddh*>`-J0#JWYc4)Fr^oYmfTKWOj{H*pX_8# zn~YR_vWyI>mt?<>zbsSfcrsEj!YorQ^INt=b|maFY{9eQr0ZZHEOC4#u$A$y_B?*H zD8t9?y4ljSuk*lmbUR5@VKKjj|7z;Ye#3lob==K*KksYEVo{6e*@xS#YqOJ{=&c=< z3zhhca8b8+=r>31boLhOOXmT{*Z&RBj}-a`H1lwB{SCa2{Q9UpK(4%`qy!iSF$X*X ztUAEx?~q4`{+o&a!`LtY`)i1~HCV#Y9bmxm5W>axaQ*?wE)V3*31C;WHitdH^CO}^ zWcvpob3TUuJEJ7n8Eofh`6s@^{^IukU^yq}U!P!j4K@R)XaIBpoE&VNKsFwLnhO}} z>tF0pJ8;aQyE9;Nj*5asw;? ze`&m&91p(Z@c=mdtpWLY9?Jc<#>2<^uoVB+I5@ceD-OsH`Y#Q{@!uK`ALwB%|D6xS z`M4VY)_C~1{wt1$pZlSG{vF55!SR1+|NI@!P_VTf1p3DosD`!Y!~J_OK4j z0}nS+-oe80p;`VMOFguU5C<<9%n9b>Hsdno0&$puIeCE=KwchR3kVOy6wJjZg7N={ a{Ml|W=ZDt#V?lU1dHFGDX{A+UF#ZRhaTUk_ literal 0 HcmV?d00001 diff --git a/Nynja/Resources/Colors.json b/Nynja/Resources/Colors.json index b32e56116..050637876 100644 --- a/Nynja/Resources/Colors.json +++ b/Nynja/Resources/Colors.json @@ -20,7 +20,7 @@ "backgroundGray": "#3f3f3f", "backgroundColorLight": "#DDDDDD", "silver": "#BFBFBF", - "callGreen": "#00E359", + "callGreen": "#01ae45", "darkGreen": "#067655", "subtitleGray": "#696a6b", "wheelTopLevelSeparatorColor": "#32353b", diff --git a/Nynja/Resources/DevConfig.xcconfig b/Nynja/Resources/DevConfig.xcconfig index ce27d9da4..c48955c46 100644 --- a/Nynja/Resources/DevConfig.xcconfig +++ b/Nynja/Resources/DevConfig.xcconfig @@ -14,8 +14,9 @@ AppName = NYNJADev ServerPort = 1883 Config = dev AppGroup = group.com.nynja.mobile.communicator.dev -ModelsVersion = 9 +ModelsVersion = 10 isServerConnectionSecure = false ConfServerAddress = 35.198.118.190 ConfServerPort = 80 ConfServerSecure = false +AssociatedDomain = applinks:join.dev-eu.nynja.net diff --git a/Nynja/Resources/Info.plist b/Nynja/Resources/Info.plist index dc27d29cf..87df03251 100644 --- a/Nynja/Resources/Info.plist +++ b/Nynja/Resources/Info.plist @@ -4,6 +4,8 @@ AppGroup $(AppGroup) + AssociatedDomain + $(AssociatedDomain) CFBundleDevelopmentRegion en CFBundleDisplayName @@ -21,7 +23,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.3.2.RC + 0.5.4.1.RC ConfServerAddress $(ConfServerAddress) ConfServerPort diff --git a/Nynja/Resources/Nynja.entitlements b/Nynja/Resources/Nynja.entitlements index 6678b12c7..0bd789026 100644 --- a/Nynja/Resources/Nynja.entitlements +++ b/Nynja/Resources/Nynja.entitlements @@ -4,6 +4,10 @@ aps-environment development + com.apple.developer.associated-domains + + $(AssociatedDomain) + com.apple.security.application-groups $(AppGroup) diff --git a/Nynja/Resources/PrereleaseConfig.xcconfig b/Nynja/Resources/PrereleaseConfig.xcconfig index 14f6d7d54..06b28b69b 100644 --- a/Nynja/Resources/PrereleaseConfig.xcconfig +++ b/Nynja/Resources/PrereleaseConfig.xcconfig @@ -14,8 +14,9 @@ AppName = NYNJARC ServerPort = 8443 Config = prerelease AppGroup = group.com.nynja.mobile.communicator.rc -ModelsVersion = 9 +ModelsVersion = 10 isServerConnectionSecure = true ConfServerAddress = call.staging.nynja.net ConfServerPort = 443 ConfServerSecure = true +AssociatedDomain = applinks:join.staging.nynja.net diff --git a/Nynja/Resources/ReleaseConfig.xcconfig b/Nynja/Resources/ReleaseConfig.xcconfig index 556cb1b2b..f8b763d10 100644 --- a/Nynja/Resources/ReleaseConfig.xcconfig +++ b/Nynja/Resources/ReleaseConfig.xcconfig @@ -19,3 +19,4 @@ isServerConnectionSecure = true ConfServerAddress = call.nynja.net ConfServerPort = 443 ConfServerSecure = true +AssociatedDomain = applinks:join.nynja.net diff --git a/Nynja/Resources/en.lproj/Localizable.strings b/Nynja/Resources/en.lproj/Localizable.strings index d68af6ad2..71c2d00ae 100644 --- a/Nynja/Resources/en.lproj/Localizable.strings +++ b/Nynja/Resources/en.lproj/Localizable.strings @@ -163,7 +163,8 @@ "call_incoming_video"="Incoming Video Call..."; "call_ringing"="Ringing..."; "call_connecting"="Connecting..."; -"call_failed"="Call failed"; +"call_failed"="Connection dropped"; +"call_disconnected"="Connection lost, reconnecting..."; "accept_audio"="Accept Audio"; "switch_to_audio"="Switch to Audio"; "hang_up"="Hang Up"; @@ -175,7 +176,15 @@ "unmute_call"="Unmute"; "speakerphone"="Speakerphone"; "return_to_chat_from_call"="Return to Chat"; - +"call_invite"="Invite"; +"call_invite_group_participants"="Group Participants"; +"call_invite_copy_invitation_link"="Copy Invitation Link"; +"call_invite_copy_banner_text"="Copied to Clipboard!"; +"call_join_by_link_alert_question" = "You have a call in progress. Would you like to end the current call and accept the new one?"; +"call_join_by_link_error_expired" = "Link is expired"; +"call_join_by_link_error_broken" = "Link is broken"; +"call_join_by_link_error_limit_reached" = "You can't join the call as the participant limit has been reached"; +"call_join_by_link_error_failed_to_join" = "Failed to join conference"; // MARK: chat list "chats_list_title"="Chat List"; @@ -559,6 +568,7 @@ "wheel_item_groups"="Groups"; "wheel_item_newGroup"="New Group"; "wheel_item_groupOptions"="Group Options"; +"wheel_item_chatOptions" = "Chat Options"; "wheel_item_notifications"="Notifications"; "wheel_item_groupInfo"="Group Info"; "wheel_item_groupPhoto"="Group Photo"; @@ -599,7 +609,7 @@ "action_cell_profile_starred_messages"="Starred Messages"; "profile_go_to_chats"="Go to chats"; "profile_go_to_groups"="Go to groups"; -"profile_go_to_history"="Go to history"; +"profile_go_to_history"="Go to contact history"; "profile_go_to_starred_messages"="Go to starred messages"; // MARK: Edit Profile @@ -681,7 +691,7 @@ "invite_to_nynja" = "INVITE TO NYNJA"; "no_contacts_selected" = "No contacts selected"; "contacts_were_not_fetched" = "Contacts were not fetched"; -"nynja_share_string" = "Hey, I'm using NYNJA to chat. Join me! Download it here: https://www.nynja.biz/"; +"nynja_share_string" = "I'm using the new SuperApp! called NYNJA! It is an amazing productivity and communications app for everyone like you've never seen before https://beta.nynja.net/"; "sms_functionality_is_not_available" = "send sms functionality is not available on your phone"; //MARK: Security @@ -757,6 +767,31 @@ "ls_language_for_translation" = "Language for translation on sending"; "ls_auto_t_on_sending" = "Auto-translate on sending"; "ls_transcription_language" = "Language for transcription"; +"ls_translate_incoming" = "Translate incoming messages"; +"ls_translate_outcoming" = "Translate outgoing messages"; + +"ls_auto_translate_inc" = "Auto translate incoming text messages"; +"ls_auto_translate_out" = "Auto translate outgoing text messages"; +"ls_auto_translate_voice" = "Auto translate voice messages"; +"ls_auto_transcribe_voice" = "Auto transcribe voice messages"; + +"ls_translate_inc_description" = "NYNJA will automatically translate incoming text messages when this feature is turned on. When turned off, you can long press on any text message in a chat to translate it manually."; + +"ls_translate_out_description_1" = "What language do you want to send translated messages in?"; +"ls_translate_out_description_2" = "When set to Auto, NYNJA will automatically translate your outgoing messages into the language you choose. On manual, you will see an up arrow icon when sending a message in a chat, where you can check the translation before sending each message. When off, NYNJA will not translate your outgoing messages."; + +"ls_trancribe_description_1" = "What language does %@ speak to you in?"; +"ls_trancribe_description_2" = "NYNJA will automatically transcribe incoming voice messages to text when this feature is turned on. When turned off, you can long press on any voice message in a chat to convert it to text manually."; +"ls_trancribe_description_3" = "Turn on if you want NYNJA to translate voice messages automatically after they have been transcribed to text."; + +"ls_language_selector_title_on_incoming_translate" = "Language to translate incoming message"; +"ls_language_selector_title_on_outgoing_translate" = "Language to send translated message"; +"ls_language_selector_title_on_transcribe" = "Language that other user speaks to you"; +"ls_mode_sheet_title" = "Automatically translate outgoing text messages?"; +"ls_mode_manual" = "Manual"; +"ls_mode_auto" = "Auto"; +"ls_mode_off" = "Off"; +"ls_transcribe" = "Transcribe voice messages to text"; "ls_always" = "Always"; "ls_once" = "Once"; @@ -768,6 +803,10 @@ "tp_translating" = "Translating..."; "tp_translation" = "Translation"; +"tp_translation_failed" = "Translation failed. Please try again."; +"tp_translation_language_is_equal" = "Original and translated message are the same language."; + +"tp_transcribing_failed" = "Transcribing failed. Please try again."; //MARK: - Transcribing "tp_transcribing" = "Transcribing..."; diff --git a/Nynja/ServerModel/NotificationSettingProtocol.swift b/Nynja/ServerModel/NotificationSettingProtocol.swift index 26fe50804..948df2c6a 100644 --- a/Nynja/ServerModel/NotificationSettingProtocol.swift +++ b/Nynja/ServerModel/NotificationSettingProtocol.swift @@ -25,14 +25,14 @@ protocol NotificationSettingProtocol: Customizable { extension Contact: NotificationSettingProtocol { var notifications : Bool { guard let feature = featureOrNil(by: .privateNotifications) else { return true } - return feature.value == true.description ? true : false + return feature.boolValue ?? false } } extension Member: NotificationSettingProtocol { var notifications : Bool { guard let feature = featureOrNil(by: .groupNotifications) else { return true } - return feature.value == true.description ? true : false + return feature.boolValue ?? false } } diff --git a/Nynja/ServerModel/Source/Decoder.swift b/Nynja/ServerModel/Source/Decoder.swift index bf56d8420..0af94781c 100644 --- a/Nynja/ServerModel/Source/Decoder.swift +++ b/Nynja/ServerModel/Source/Decoder.swift @@ -1,5 +1,7 @@ func parseObject(name: String, body:[Model], tuple: BertTuple) -> AnyObject? { + guard tuple.elements.count == body.count + 1 else { return nil } + switch name { case "writer": if body.count != 5 { return nil } diff --git a/Nynja/Services/ConversationLanguageSettingService/ConversationLanguageSettingService.swift b/Nynja/Services/ConversationLanguageSettingService/ConversationLanguageSettingService.swift index 2bf63e389..4d0c9a5c8 100644 --- a/Nynja/Services/ConversationLanguageSettingService/ConversationLanguageSettingService.swift +++ b/Nynja/Services/ConversationLanguageSettingService/ConversationLanguageSettingService.swift @@ -10,91 +10,114 @@ import Foundation final class ConversationLanguageSettingService: ConversationLanguageSettingServiceProtocol, InitializeInjectable { - var settingOwner: Customizable + private(set) var settingStorage: LanguageSettingProtocol //MARK: - Init init(dependencies: Dependencies) { - settingOwner = dependencies.settingOwner + settingStorage = dependencies.settingStorage } //MARK: - ConversationLanguageSettingServiceProtocol - var chatLanguage: LangSignature { - return getChatLanguage() + var myDefaultLanguage: Language { + guard let feature = ProfileDAO.currentProfile? + .settings? + .first(where: { $0.key == LanguageSettingKey.defaultLanguage.rawValue }) else { + return .init(language: AppLanguage.english) + } + return feature.languageValue + } + + var defaultLanguage: Language { + return settingStorage.defaultLanguage } - var autoLanguageChat: Bool { - return getAutoLanguageChat() + var autoTranslate: Bool { + return settingStorage.autoTranslate } - var translateLanguage: LangSignature? { - return getTranslateLanguage() + var outgoingTranslateLanguage: Language { + return settingStorage.outgoingTranslateLanguage + } + var autoOutgoingTranslate: AutoOutgoingTranslate { + return settingStorage.autoOutgoingTranslate } - var autoTranslateLanguage: Bool { - return getAutoTranslateLanguage() + var transcribeLanguage: Language { + return settingStorage.transcribeLanguage + } + var autoTranscribe: Bool { + return settingStorage.autoTranscribe + } + var autoTranslateTranscribe: Bool { + return settingStorage.autoTranslateTranscribe } func feature(by key: LanguageSettingKey) -> Feature { - guard let feature = featureOrNil(by: key) else { - return Feature.languageFeature(key.rawValue) - } - return feature + return settingStorage.feature(by: key) } func featureOrNil(by key: LanguageSettingKey) -> Feature? { - guard let feature = settingOwner.settings?.first(where: { $0.key == key.rawValue }) else { - return nil - } - return feature + return settingStorage.featureOrNil(by: key) } - func updateSettingOwner(_ settingOwner: Customizable) { - self.settingOwner = settingOwner + func update(settingStorage: LanguageSettingProtocol) { + self.settingStorage = settingStorage + } + + func getConversationLanguageSetting() -> ConversationLanguageSettingProtocol { + return ConversationLanguageSetting(myDefaultLanguage: myDefaultLanguage, + defaultLanguage: defaultLanguage, + autoTranslate: autoTranslate, + outgoingTranslateLanguage: outgoingTranslateLanguage, + autoOutgoingTranslate: autoOutgoingTranslate, + transcribeLanguage: transcribeLanguage, + autoTranscribe: autoTranscribe, + autoTranslateTranscribe: autoTranslateTranscribe) } } //MARK: - Injectable extension ConversationLanguageSettingService { struct Dependencies { - let settingOwner: Customizable - } + let settingStorage: LanguageSettingProtocol + } } -//MARK: - Private -private extension ConversationLanguageSettingService { - - func getChatLanguage() -> LangSignature { - guard let feature = featureOrNil(by: .chatLanguage) else { - return (name: Language.current.longValue, lang: Language.current.rawValue) - } - return splitLanguageSignature(feature.value) - } - - func getAutoLanguageChat() -> Bool { - guard let feature = featureOrNil(by: .autoLanguageChat) else { return false } - return feature.value == true.description ? true : false - } - - func getTranslateLanguage() -> LangSignature? { - guard let feature = featureOrNil(by: .translateLanguage) else { return nil } - let signature = splitLanguageSignature(feature.value) - if signature.lang == LanguageSettingConstants.languageNone { - return nil - } - return signature +final class ConversationLanguageSetting: ConversationLanguageSettingProtocol { + + var myDefaultLanguage: Language + + var defaultLanguage: Language + + var autoTranslate: Bool + + var outgoingTranslateLanguage: Language + var autoOutgoingTranslate: AutoOutgoingTranslate + + var transcribeLanguage: Language + var autoTranscribe: Bool + var autoTranslateTranscribe: Bool + + init(myDefaultLanguage: Language, + defaultLanguage: Language, + autoTranslate: Bool, + outgoingTranslateLanguage: Language, + autoOutgoingTranslate: AutoOutgoingTranslate, + transcribeLanguage: Language, + autoTranscribe: Bool, + autoTranslateTranscribe: Bool) { + self.myDefaultLanguage = myDefaultLanguage + self.defaultLanguage = defaultLanguage + self.autoTranslate = autoTranslate + self.outgoingTranslateLanguage = outgoingTranslateLanguage + self.autoOutgoingTranslate = autoOutgoingTranslate + self.transcribeLanguage = transcribeLanguage + self.autoTranscribe = autoTranscribe + self.autoTranslateTranscribe = autoTranslateTranscribe } - func getAutoTranslateLanguage() -> Bool { - guard let feature = featureOrNil(by: .autoTranslateLanguage) else { return false } - return feature.value == true.description ? true : false - } - - func splitLanguageSignature(_ value: String?) -> LangSignature { - guard let signature = value?.split(separator: ":"), let name = signature.first, let lang = signature.last else { return LangExtended.none.toLangSignature } - return (name: String(name), lang: String(lang)) - } } diff --git a/Nynja/Services/ConversationLanguageSettingService/ConversationLanguageSettingServiceProtocol.swift b/Nynja/Services/ConversationLanguageSettingService/ConversationLanguageSettingServiceProtocol.swift index 0b217d075..8f196d3dd 100644 --- a/Nynja/Services/ConversationLanguageSettingService/ConversationLanguageSettingServiceProtocol.swift +++ b/Nynja/Services/ConversationLanguageSettingService/ConversationLanguageSettingServiceProtocol.swift @@ -8,16 +8,42 @@ protocol ConversationLanguageSettingServiceProtocol: class { - var settingOwner: Customizable { get } + var settingStorage: LanguageSettingProtocol { get } - var chatLanguage: LangSignature { get } - var autoLanguageChat: Bool { get } + var myDefaultLanguage: Language { get } - var translateLanguage: LangSignature? { get } - var autoTranslateLanguage: Bool { get } + var defaultLanguage: Language { get } + + var autoTranslate: Bool { get } + + var outgoingTranslateLanguage: Language { get } + var autoOutgoingTranslate: AutoOutgoingTranslate { get } + + var transcribeLanguage: Language { get } + var autoTranscribe: Bool { get } + var autoTranslateTranscribe: Bool { get } func feature(by key: LanguageSettingKey) -> Feature func featureOrNil(by key: LanguageSettingKey) -> Feature? - func updateSettingOwner(_ settingOwner: Customizable) + func update(settingStorage: LanguageSettingProtocol) + + func getConversationLanguageSetting() -> ConversationLanguageSettingProtocol +} + + +protocol ConversationLanguageSettingProtocol { + + var myDefaultLanguage: Language { get set } + + var defaultLanguage: Language { get set } + + var autoTranslate: Bool { get set } + + var outgoingTranslateLanguage: Language { get set } + var autoOutgoingTranslate: AutoOutgoingTranslate { get set } + + var transcribeLanguage: Language { get set } + var autoTranscribe: Bool { get set } + var autoTranslateTranscribe: Bool { get set } } diff --git a/Nynja/Services/FeatureFactory.swift b/Nynja/Services/FeatureFactory.swift index 207c6deea..552bb9584 100644 --- a/Nynja/Services/FeatureFactory.swift +++ b/Nynja/Services/FeatureFactory.swift @@ -8,10 +8,15 @@ import Foundation - class FeatureFactory { - static func getAuthSettings() -> [Feature] { + static func verifyFeatures(wasRunApp: Bool) -> [Feature] { + let languageSetting = Feature.languageFeature(LanguageSettingKey.defaultLanguage.rawValue) + languageSetting.value = wasRunApp ? Locale.systemLanguage.stringValue : Language(language: .english).stringValue + return [languageSetting] + } + + static var authFeatures: [Feature] { var result = [Feature]() let builder = IdBuilder(format: .defaultId) @@ -44,8 +49,8 @@ class FeatureFactory { } //USE as a place holder for current session in offline mode - static func getAuthSettingsToShowOffline() -> [Feature] { - var result = getAuthSettings() + static var authFeaturesToShowOffline: [Feature] { + var result = authFeatures let locale = NSLocale(localeIdentifier: "en_US") let builder = IdBuilder(format: .defaultId) diff --git a/Nynja/Services/HandleServices/HistoryHandler.swift b/Nynja/Services/HandleServices/HistoryHandler.swift index 4b36b4288..11d6bf824 100644 --- a/Nynja/Services/HandleServices/HistoryHandler.swift +++ b/Nynja/Services/HandleServices/HistoryHandler.swift @@ -201,6 +201,12 @@ final class HistoryHandler: BaseHandler { } } + stackForSave.forEach { + if let status = try? MessageDAO.localStatus(message: $0) { + $0.localStatus = status + } + } + // Save new messages after old messages try? MessageDAO.saveMessages(stackForSave.reversed()) ChatService.removeMessages(stackForDelete) diff --git a/Nynja/Services/HandleServices/MessageHandler.swift b/Nynja/Services/HandleServices/MessageHandler.swift index e9bd99580..48eec0c0a 100644 --- a/Nynja/Services/HandleServices/MessageHandler.swift +++ b/Nynja/Services/HandleServices/MessageHandler.swift @@ -109,7 +109,7 @@ final class MessageHandler: BaseHandler { do { try save(message) try ChatService.updateMessage(message) - ChatService.updateLastMessage(message) { $0 == message.id } + ChatService.updateLastMessage(message, shouldChangeUnread: false) { $0 == message.id } } catch { LogService.log(topic: .db) { "\(#function), line: \(#line) - \(error.localizedDescription)" } } @@ -176,6 +176,9 @@ final class MessageHandler: BaseHandler { if let repliedMessage = message.repliedMessage { repliedMessage.localStatus = try MessageDAO.localStatusForRepliedMessage(repliedMessage) } + if let status = try MessageDAO.localStatus(message: message) { + message.localStatus = status + } try MessageDAO.trustIfNextMessageExists(before: message) try storageService.perform(action: .save, with: message) } diff --git a/Nynja/Services/HandleServices/ProfileHandler.swift b/Nynja/Services/HandleServices/ProfileHandler.swift index b1b89d482..d2d68a05c 100644 --- a/Nynja/Services/HandleServices/ProfileHandler.swift +++ b/Nynja/Services/HandleServices/ProfileHandler.swift @@ -102,21 +102,37 @@ class ProfileHandler: BaseHandler { } } + func updateLocalStatus(for message: Message) { + do { + message.localStatus = try MessageDAO.localStatus(message: message) + } catch { + LogService.log(topic: .db) { return error.localizedDescription } + } + } + newRoster.userlist?.forEach { contact in if let currentRoster = currentRoster, let contactId = contact.id, !shouldSave(contact.last_msg) { contact.last_msg = currentRoster.userlist?.first { $0.id == contactId }?.last_msg - - } else if let repliedMessage = contact.last_msg?.repliedMessage { - updateRepliedStatus(for: repliedMessage) + } else { + if let message = contact.last_msg { + if let repliedMessage = message.repliedMessage { + updateRepliedStatus(for: repliedMessage) + } + updateLocalStatus(for: message) + } } } newRoster.roomlist?.forEach { room in if let currentRoster = currentRoster, let roomId = room.id, !shouldSave(room.last_msg) { room.last_msg = currentRoster.roomlist?.first { $0.id == roomId }?.last_msg - - } else if let repliedMessage = room.last_msg?.repliedMessage { - updateRepliedStatus(for: repliedMessage) + } else { + if let message = room.last_msg { + if let repliedMessage = message.repliedMessage { + updateRepliedStatus(for: repliedMessage) + } + updateLocalStatus(for: message) + } } } diff --git a/Nynja/Services/MQTT/MQTTService.swift b/Nynja/Services/MQTT/MQTTService.swift index f750f8d5f..b3639a0f7 100644 --- a/Nynja/Services/MQTT/MQTTService.swift +++ b/Nynja/Services/MQTT/MQTTService.swift @@ -38,6 +38,8 @@ final class MQTTService: NSObject, CocoaMQTTDelegate, ConnectionServiceDelegate let semaphore = DispatchSemaphore(value: 1) + var wasRunApp: Bool = false + typealias MQTTServiceSubscribers = [WeakRef] private var subscribers = MQTTServiceSubscribers() private let subscribersQueue = DispatchQueue(label: "com.nynja.mobile.communicator.mqttservice.subscribers") diff --git a/Nynja/Services/MQTT/MQTTServiceAuth.swift b/Nynja/Services/MQTT/MQTTServiceAuth.swift index 293ceb17a..30e1c9094 100644 --- a/Nynja/Services/MQTT/MQTTServiceAuth.swift +++ b/Nynja/Services/MQTT/MQTTServiceAuth.swift @@ -39,14 +39,13 @@ extension MQTTService { } func registration(number: String) { - let settings = FeatureFactory.getAuthSettings() let model = AuthModel( type: .REG, phoneNumber: number, userID: nil, clientID: "reg_\(deviceId)", deviceToken: deviceId, - settings: settings) + settings: FeatureFactory.authFeatures) publish(model: model) } @@ -55,13 +54,12 @@ extension MQTTService { let phone = StorageService.sharedInstance.phone, let token = StorageService.sharedInstance.tokenAsNSData, let clientID = StorageService.sharedInstance.clientId { - let features = FeatureFactory.getAuthSettings() let model = AuthModel(type: .PUSH, phoneNumber: phone, userID: nil, clientID: clientID, deviceToken: deviceId, - settings: features, + settings: FeatureFactory.authFeatures, token: token, smsCode: nil, languages: nil, @@ -81,7 +79,9 @@ extension MQTTService { userID: nil, clientID: nil, deviceToken: deviceId, - token: nil, smsCode: code) + settings: FeatureFactory.verifyFeatures(wasRunApp: wasRunApp), + token: nil, + smsCode: code) publish(model: model) } diff --git a/Nynja/Services/MQTT/MQTTServiceProfile.swift b/Nynja/Services/MQTT/MQTTServiceProfile.swift index 48f53411f..7fe68e9db 100644 --- a/Nynja/Services/MQTT/MQTTServiceProfile.swift +++ b/Nynja/Services/MQTT/MQTTServiceProfile.swift @@ -13,11 +13,7 @@ extension MQTTService { func updateAccount(id: Int64, name: String, surname: String, phone: String) { if let _ = StorageService.sharedInstance.token { var model:UpdateRosterModel! - if surname != "" { - model = UpdateRosterModel(id: id, name: name, surname: surname, avatar: nil, email: nil,nick: nil) - } else { - model = UpdateRosterModel(id: id, name: name, surname: nil, avatar: nil, email: nil,nick: nil) - } + model = UpdateRosterModel(id: id, name: name, surname: surname, avatar: nil, email: nil,nick: nil) publish(model: model) } } diff --git a/Nynja/Services/MessageProcessing/Operations/UploadOperation.swift b/Nynja/Services/MessageProcessing/Operations/UploadOperation.swift index c048cd077..92fd301e5 100644 --- a/Nynja/Services/MessageProcessing/Operations/UploadOperation.swift +++ b/Nynja/Services/MessageProcessing/Operations/UploadOperation.swift @@ -60,7 +60,7 @@ private extension UploadOperation { switch uploadingResult { case let .success(serverUrl, resultInfo): let progress = ProgressModel(url: serverUrl, status: .done, result: url, transferInfo: resultInfo) - delegate?.updateProgress(progress) + delegate?.update(progress: progress) uploadOperationDelegate?.updateMessageAfterUpload(message: message, url: url, serverUrl: serverUrl) @@ -93,7 +93,7 @@ private extension UploadOperation { uploadOperationDelegate?.sendMessageAndDismissStatusIfNeeded(message: message) case .error: let progress = ProgressModel(url: url, status: .offline, result: nil, transferInfo: .zero) - delegate?.updateProgress(progress) + delegate?.update(progress: progress) } state = .finished diff --git a/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift b/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift index b121fce79..39a8fb5cf 100644 --- a/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift +++ b/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift @@ -57,7 +57,6 @@ extension NynjaCallDelegate { class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDelegate, NYNCallManagerDelegate, ConnectionServiceDelegate { - private var initialized: Bool = false private let storageService = StorageService.sharedInstance let nynComm: NynjaCommunicator @@ -125,16 +124,24 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele self.username = userName self.nynComm.login(withName: userName, andSecret: password) + + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + NynjaJoinByLinkService.shared().joinByUrlIfPossible() + } } - + private func tryReconnect(reason: String) { guard initialized else {return} - + dispatchAsyncMain { self.nynComm.tryReconnect(withReason: reason) } } - + + func isLoggedIn() -> Bool { + return self.nynComm.isLogged + } + func initialize() { guard false == initialized, storageService.isUserLogined, let phoneId = storageService.phoneId else { return @@ -174,6 +181,56 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele withSubject: creator.name) } + func joinConference(conferencId:String, memberId:String) { + + if let c = self.call { + if !c.callId.elementsEqual(conferencId) { + // do not allow rejoining if we have another call running + return + } + } + + if let call = self.nynComm.getCallManager().getCallById(conferencId) { + let callState = call.callState + + if NYNCallState.closed == callState || NYNCallState.new == callState || + NYNCallState.failed == callState + { + self.call = call + self.call?.setDelegate(self) + + delegates.invokeDelegates { delegate in + delegate.creatingGroupCall(name: call.subject, call: call) + } + + self.nynComm.getCallManager().joinConference(withRequestId: UUID().uuidString, + withConferenceId: conferencId, + withMemberId: memberId, + withDeviceId: self.myDeviceId) + + SystemSoundManager.sharedInstance.voipCallStarted() + } + } + } + + func joinConference(linkId: String) { + if self.call != nil { + return + } + + if let mySelf = getMySelf(), let displayName = mySelf.fullName { + let creator = CallCreatorMediator(createId: UUID().uuidString, + members: [], + roomId: "", + name: "") + + self.creators[creator.createId] = creator + self.nynComm.getCallManager().requestJoinConference(withRequestId: creator.createId, + withLinkId: linkId, + andDisplayName: displayName) + } + } + func updateConferenceInfo(callId: String, subject:String) { self.nynComm.getCallManager().updateConferenceInfo(withRequestId: UUID().uuidString, withCallId: callId, @@ -299,6 +356,16 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele return (nil != self.call) } + func hangupActiveCall() -> Bool { + + if let c = self.call { + c.hangup() + return true + } + + return false + } + func rejoinRunningCallWith(_ roomId: String) { if let c = self.call { if !c.externalInfo.elementsEqual(roomId) { @@ -790,7 +857,7 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele LogService.log(topic: .callSystem) { return "callMissed: \(c.callId)" } self.callDelegate?.callEnded(call: c, isMissed: true) self.call?.setDelegate(nil) - + delegates.invokeDelegates { delegate in delegate.callEnded(call: c) } @@ -802,7 +869,7 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele } } } - + self.call = nil isCallInProgress = false SystemSoundManager.sharedInstance.voipCallStopped() @@ -866,6 +933,40 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele { } + func requestConferenceIdByLinkId(withRequestId requestId: String, withCallId callId: String, didFinishWithError error: NynjaError?) { + } + + func joinConferenceByLinkId(withRequestId requestId: String, withCallId callId: String, didFinishWithError error: NynjaError?) { + if let cr = getCreator(createId: requestId) { + removeCreator(createId: cr.createId) + + if let e = error { + var errText: String = String.localizable.callJoinByLinkErrorFailedToJoin + + if e.code == NynjaCode.NYNJA_CODE_OUT_OF_RANGE { + errText = String.localizable.callJoinByLinkErrorExpired + } else if e.code == NynjaCode.NYNJA_CODE_NOT_FOUND { + errText = String.localizable.callJoinByLinkErrorBroken + } else if e.code == NynjaCode.NYNJA_CODE_FAILED_PRECONDITION { + errText = String.localizable.callJoinByLinkErrorLimitReached + } + + AlertManager.sharedInstance.showAlertOk(message: errText) + } else { + if let call = self.nynComm.getCallManager().getCallById(callId) { + self.call = call + self.call?.setDelegate(self) + + delegates.invokeDelegates { delegate in + delegate.creatingGroupCall(name: call.subject, call: call) + } + + SystemSoundManager.sharedInstance.voipCallStarted() + } + } + } + } + // Call invitation state did change func callStateDidChange(_ call: NYNCall) { self.messageInteractorCallProtocol?.didChangeCallInvitationState(call) @@ -873,7 +974,7 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele //MARK: - ConnectionServiceDelegate internal func connectionStatusChanged(_ sender: ConnectionService, service: ConnectionService.Service, oldValue: ConnectionService.ConnectionServiceState) { - + if service == .networking { guard let networkStatus = sender.state[.networking] else { return } switch networkStatus { diff --git a/Nynja/Services/NynjaCalls/NynjaJoinByLinkService.swift b/Nynja/Services/NynjaCalls/NynjaJoinByLinkService.swift new file mode 100644 index 000000000..5a90a5974 --- /dev/null +++ b/Nynja/Services/NynjaCalls/NynjaJoinByLinkService.swift @@ -0,0 +1,106 @@ +// +// NynjaJoinByLinkService.swift +// Nynja +// +// Created by Bozhko Terziev on 11.10.18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import UIKit + +class NynjaJoinByLinkService: NSObject, NynjaCommunicatorServiceDelegate { + + // MARK: - Members + private var joinByLinkURL: URL? + private var callService = NynjaCommunicatorService.sharedInstance + + // MARK: - Properties + private static var sharedNynjaJoinByLinkService: NynjaJoinByLinkService = { + let joinByLinkService = NynjaJoinByLinkService() + + // Configuration + // ... + + return joinByLinkService + }() + + // Initialization + + private override init() { + super.init() + callService.delegates.addDelegate(self) + } + + // MARK: - Accessors + + class func shared() -> NynjaJoinByLinkService { + return sharedNynjaJoinByLinkService + } + + private func linkId() -> String? { + + guard let link = joinByLinkURL, let urlcomponents = URLComponents(url: link, resolvingAgainstBaseURL: true) else { + return nil + } + + return urlcomponents.path.lastPathComponent + } + + private func doJoin() { + guard let linkId = linkId() else { + self.joinByLinkURL = nil + LogService.log(topic: .callSystem) { return "Invalid Join Link" } + return + } + + if callService.isLoggedIn() { + LogService.log(topic: .callSystem) { return "Join By Link now: \(linkId)" } + callService.joinConference(linkId: linkId) + self.joinByLinkURL = nil + } + } + + func joinByUrlIfPossible() -> Bool { + guard let url = self.joinByLinkURL else { + return false + } + + LogService.log(topic: .callSystem) { return "Join By Link is possible" } + return handleJoin(by: url) + } + + func handleJoin(by url:URL) -> Bool { + self.joinByLinkURL = url + + guard let _ = linkId() else { + self.joinByLinkURL = nil + LogService.log(topic: .callSystem) { return "Invalid Join Link" } + return false + } + + if callService.hasCallInProgress() { + AlertManager.sharedInstance.showAlertWithTwoActions(title: "", message: String.localizable.callJoinByLinkAlertQuestion, + firstActionTitle: String.localizable.no, secondActionTitle: String.localizable.yes, + firstAction: { () in}, + secondAction: { () in + // Check again for call im progress because call might be ended during the alert displaying + if self.callService.hasCallInProgress() { + self.callService.hangupActiveCall() + } else { + self.doJoin() + } + + }) + } else { + doJoin() + } + return true + } + + // MARK:- NynjaCommunicatorServiceDelegate + func callEnded(call: NYNCall){ + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(500)) { + self.doJoin() + } + } +} diff --git a/Nynja/Services/PushService.swift b/Nynja/Services/PushService.swift index 90d4f9069..91c423ae4 100644 --- a/Nynja/Services/PushService.swift +++ b/Nynja/Services/PushService.swift @@ -210,6 +210,9 @@ final class PushService: NSObject, PKPushRegistryDelegate, UserSettingsRespondab if let repliedMessage = message.repliedMessage { repliedMessage.localStatus = try MessageDAO.localStatusForRepliedMessage(repliedMessage) } + if let status = try MessageDAO.localStatus(message: message) { + message.localStatus = status + } try MessageDAO.trustIfNextMessageExists(before: message) try StorageService.sharedInstance.perform(action: .save, with: message) } @@ -271,6 +274,9 @@ final class PushService: NSObject, PKPushRegistryDelegate, UserSettingsRespondab if let repliedMessage = message.repliedMessage { repliedMessage.localStatus = try MessageDAO.localStatusForRepliedMessage(repliedMessage) } + if let status = try MessageDAO.localStatus(message: message) { + message.localStatus = status + } if message.messageStatus == .clear { ChatService.clearMessages(before: message) } diff --git a/Nynja/Services/ServiceFactory/ServiceFactory.swift b/Nynja/Services/ServiceFactory/ServiceFactory.swift index 12b4ea8f1..f581ec61b 100644 --- a/Nynja/Services/ServiceFactory/ServiceFactory.swift +++ b/Nynja/Services/ServiceFactory/ServiceFactory.swift @@ -8,7 +8,7 @@ import Foundation -protocol ServiceFactoryProtocol: class { +protocol ServiceFactoryProtocol: SharedServiceFactoryProtocol { func makeMessageSendingService() -> MessageSendingServiceProtocol func makeResourceManager() -> ResourceManagerProtocol func makeMessageFactory() -> MessageFactoryProtocol @@ -18,8 +18,6 @@ protocol ServiceFactoryProtocol: class { func makeCameraSettingsService(with flow: CameraSourceFlow) -> CameraSettingsServiceProtocol - func makeMQTTService() -> MQTTService - func makeStorageService() -> StorageService func makeMesageProcessingManager() -> MessageProcessingManagerInterface func makeHistoryRequestFactory() -> HistoryRequestModelFactoryProtocol @@ -36,7 +34,6 @@ protocol ServiceFactoryProtocol: class { func makeWalletService() -> WalletService func makeSyncFileManager() -> SyncFileManager - func makeAmazonManager() -> AmazonManager func makeMuteChatService() -> MuteChatServiceProtocol @@ -52,7 +49,8 @@ protocol ServiceFactoryProtocol: class { func makeAudioSessionManager() -> AudioSessionManager } -final class ServiceFactory: ServiceFactoryProtocol { +final class ServiceFactory: SharedServiceFactory, ServiceFactoryProtocol { + func makeMessageSendingService() -> MessageSendingServiceProtocol { let dependencies = MessageSendingService.Dependencies( mqttService: makeMQTTService(), @@ -63,13 +61,6 @@ final class ServiceFactory: ServiceFactoryProtocol { return MessageSendingService(dependencies: dependencies) } - func makeTypingSenderService() -> TypingSenderServiceProtocol { - let dependencies = TypingSenderService.Dependencies(mqttService: makeMQTTService(), - storageService: makeStorageService()) - - return TypingSenderService(dependencies: dependencies) - } - func makeResourceManager() -> ResourceManagerProtocol { let resourceManager = ResourceManager(permissionManager: makePermissionManager()) @@ -96,14 +87,6 @@ final class ServiceFactory: ServiceFactoryProtocol { return service } - func makeMQTTService() -> MQTTService { - return MQTTService.sharedInstance - } - - func makeStorageService() -> StorageService { - return StorageService.sharedInstance - } - func makeMesageProcessingManager() -> MessageProcessingManagerInterface { return DefaultMessagesProcessingManager.shared } @@ -157,10 +140,6 @@ final class ServiceFactory: ServiceFactoryProtocol { func makeSyncFileManager() -> SyncFileManager { return SyncFileManager.sharedInstance } - - func makeAmazonManager() -> AmazonManager { - return AmazonManager.shared - } func makeMuteChatService() -> MuteChatServiceProtocol { let dependencies = MuteChatService.Dependencies(mqttService: makeMQTTService()) diff --git a/Nynja/Services/TranscribeService/TranscribeService.swift b/Nynja/Services/TranscribeService/TranscribeService.swift index 2202e8e5f..2fea2adf5 100644 --- a/Nynja/Services/TranscribeService/TranscribeService.swift +++ b/Nynja/Services/TranscribeService/TranscribeService.swift @@ -16,36 +16,20 @@ enum TranscribeServiceState { case cancel case unknown - enum ServiceError: LocalizedError { + enum ServiceError: Error { + case reachability case upload(Error) case convertion(Error) case networkClient(Error) case emptyResponse(String) - - var localizedDescription: String { - switch self { - case .upload(let error): - return error.localizedDescription - case .convertion(let error): - return error.localizedDescription - case .networkClient(let error): - return error.localizedDescription - case .emptyResponse: - return "Empty response" - } - } } - enum UploadError: LocalizedError { + enum UploadError: Error { case empty - - var localizedDescription: String { - return "Uploaded without file name" - } } } -typealias TranscribeProcessingOuterHandler = (String, TranscribeServiceState) -> Void +typealias TranscribeProcessingOuterHandler = (String, TranscribeServiceState, Bool) -> Void typealias TranscribeProcessingInnerHandler = (TranscribeServiceState) -> Void final class TranscribeServiceDataWrapper { @@ -58,6 +42,8 @@ struct TranscribeServiceInput { let message: Message let localUrl: URL let language: String + + let isAuto: Bool } protocol TranscribeServiceProtocol { @@ -83,6 +69,9 @@ final class TranscribeService: InitializeInjectable { newtworkService.configure(config: config) + //Reachability + let reachabilityService = ReachabilityService.sharedInstance + //Processing let processingManager = DefaultMessagesProcessingManager.shared @@ -101,7 +90,8 @@ final class TranscribeService: InitializeInjectable { transcribeNetworkService: newtworkService, processingManager: processingManager, messageFactory: messageFactory, - storageService: storageService) + storageService: storageService, + reachabilityService: reachabilityService) return TranscribeService(dependencies: dependencies) }() @@ -114,6 +104,8 @@ final class TranscribeService: InitializeInjectable { private let messageFactory: MessageFactoryProtocol private let storageService: StorageService + private let reachabilityService: ReachabilityService + private let maxShortTranscribeDuration = 60 private let queue: OperationQueue = { @@ -126,6 +118,7 @@ final class TranscribeService: InitializeInjectable { processingManager = dependencies.processingManager messageFactory = dependencies.messageFactory storageService = dependencies.storageService + reachabilityService = dependencies.reachabilityService } struct Dependencies { @@ -133,12 +126,20 @@ final class TranscribeService: InitializeInjectable { let processingManager: DefaultMessagesProcessingManager let messageFactory: MessageFactoryProtocol let storageService: StorageService + let reachabilityService: ReachabilityService } } extension TranscribeService: TranscribeServiceProtocol { func transcribe(_ input: TranscribeServiceInput) { - guard var processing = saveProcessing(input) else { + guard var process = saveProcessing(input) else { + return + } + + guard reachabilityService.isReachable else { + self.handle(with: process.messageId, + state: .failure(.reachability), + isAuto: process.isAuto) return } @@ -147,8 +148,10 @@ extension TranscribeService: TranscribeServiceProtocol { guard let `self` = self else { return } - processing.process = self.updateProcessing(processing.process, data: dataWrapper, state: state) - self.handle(with: processing.id, state: state) + process = self.updateProcessing(process, data: dataWrapper, state: state) + self.handle(with: process.messageId, + state: state, + isAuto: process.isAuto) } var operations: [Operation] = [] @@ -182,7 +185,7 @@ extension TranscribeService: TranscribeServiceProtocol { operations.append(send) operations.setupDependencies(.backward) - operations.forEach { $0.name = processing.id } + operations.forEach { $0.name = process.messageId } queue.addOperations(operations, waitUntilFinished: false) } @@ -201,7 +204,7 @@ extension TranscribeService: TranscribeServiceProtocol { return } convert = self.updateProcessing(convert, data: dataWrapper, state: state) - self.handle(with: convert.messageId, state: state) + self.handle(with: convert.messageId, state: state, isAuto: convert.isAuto) } let processChain = restoreProcessChain(from: convert) @@ -240,7 +243,7 @@ extension TranscribeService: TranscribeServiceProtocol { private extension TranscribeService { - private func saveProcessing(_ input: TranscribeServiceInput) -> (id: String, process: DBConvertMessage)? { + private func saveProcessing(_ input: TranscribeServiceInput) -> DBConvertMessage? { guard let id = input.message.msg_id else { return nil } @@ -248,11 +251,12 @@ private extension TranscribeService { type: .transcribe, process: .convert, value: input.localUrl.absoluteString, - language: input.language) + language: input.language, + isAuto: input.isAuto) try? storageService.perform(action: .save, with: process) - return (id, process) + return process } private func updateProcessing(_ process: DBConvertMessage, data: TranscribeServiceDataWrapper, state: TranscribeServiceState) -> DBConvertMessage { @@ -260,8 +264,9 @@ private extension TranscribeService { case .updateProccess(_, let type): process.process = type.rawValue process.value = (data.processingResult ?? data.processingURL?.absoluteString) ?? "" - case .success: - break + case .success(let transcription): + process.process = DBConvertMessage.Process.send.rawValue + process.value = transcription case .failure(let error): switch error { case .emptyResponse: @@ -280,8 +285,8 @@ private extension TranscribeService { return process } - private func handle(with id: String, state: TranscribeServiceState) { - transcribeSubscribers.forEach { $0.handler(id, state) } + private func handle(with id: String, state: TranscribeServiceState, isAuto: Bool) { + transcribeSubscribers.forEach { $0.handler(id, state, isAuto) } } private func makeLongTranscribeOperations(with input: TranscribeServiceInput, dataWrapper: TranscribeServiceDataWrapper, completion: @escaping TranscribeProcessingInnerHandler) -> [TranscribeOperation] { diff --git a/Nynja/Services/WheelContainer/Manager/WCDataManager.swift b/Nynja/Services/WheelContainer/Manager/WCDataManager.swift index 2131578ea..00f285153 100644 --- a/Nynja/Services/WheelContainer/Manager/WCDataManager.swift +++ b/Nynja/Services/WheelContainer/Manager/WCDataManager.swift @@ -301,5 +301,4 @@ class WCDataManager: WCDataManagerProtocol, UserSettingsRespondable { _factory.originalFactory = nil invalidateRestoreTimer() } - } diff --git a/Nynja/SyncFileManager/SyncFileManager.swift b/Nynja/SyncFileManager/SyncFileManager.swift index f5fa6afb0..3e1263358 100644 --- a/Nynja/SyncFileManager/SyncFileManager.swift +++ b/Nynja/SyncFileManager/SyncFileManager.swift @@ -188,32 +188,3 @@ class SyncFileManager { } } } - -class SimpleDownloader { - - func download(url: String, result: ((String?, TransferInfo?) -> ())?) { - if let link = URL(string: url) { - URLSession.shared.dataTask(with: link, completionHandler: { (data, response, error) in - if error != nil { - result?(nil,nil) - return - } - let ext = url.split(separator: "/").last ?? "ninja" - let name = "\(Int(Date().timeIntervalSince1970.seconds))_\(ext)" - if let path = FileManagerService.sharedInstance.createFile(folder: Constants.Folders.downloads, name: name) { - let fileUrl = URL(fileURLWithPath: path) - do { - try data?.write(to: fileUrl, options: Data.WritingOptions.atomic) - result?(path,nil) - return - } catch { - result?(nil,nil) - return - } - } - }).resume() - } - } -} - - diff --git a/Nynja/TranslationService/Model/LangExtended.swift b/Nynja/TranslationService/Model/LangExtended.swift deleted file mode 100644 index 16c66c46a..000000000 --- a/Nynja/TranslationService/Model/LangExtended.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// LangExtended.swift -// Nynja -// -// Created by Andrey Reznik on 13.05.2018. -// Copyright © 2018 TecSynt Solutions. All rights reserved. -// - -struct LangExtended: Equatable { - var language: String - var name: String - - init(language: String, name: String) { - self.language = language - self.name = name - } - - init(signature: LangSignature) { - self.language = signature.lang - self.name = signature.name - } - - var toLangSignature: LangSignature { - return (name: name, lang: language) - } - - var toString: String { - return "\(name):\(language)" - } - - var isNone: Bool { - return language == LanguageSettingConstants.languageNone - } - - static var none: LangExtended { - return LangExtended(language: LanguageSettingConstants.languageNone, name: String.localizable.lsNone) - } -} diff --git a/Nynja/TranslationService/Model/Language.swift b/Nynja/TranslationService/Model/Language.swift new file mode 100644 index 000000000..73ea3fdc6 --- /dev/null +++ b/Nynja/TranslationService/Model/Language.swift @@ -0,0 +1,26 @@ +// +// Language.swift +// Nynja +// +// Created by Andrey Reznik on 13.05.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +struct Language: Equatable { + var language: String + var name: String + + init(language: String, name: String = "") { + self.language = language + self.name = name + } + + init(language: AppLanguage) { + self.language = language.rawValue + self.name = language.longValue + } + + var stringValue: String { + return "\(name):\(language)" + } +} diff --git a/Nynja/TranslationService/TranslationService.swift b/Nynja/TranslationService/TranslationService.swift index 644a5379f..6866db53b 100644 --- a/Nynja/TranslationService/TranslationService.swift +++ b/Nynja/TranslationService/TranslationService.swift @@ -14,9 +14,23 @@ protocol TranslationServiceInterface { typealias Lang = String associatedtype ServiceError: Error - func availableLanguages(completion: @escaping (Result<[LangExtended]>) -> Void) + func availableLanguages(completion: @escaping (Result<[Language]>) -> Void) func detectLanguage(text: String, completion: @escaping (Result) -> Void) - func translate(text: String, from sourceLang: Lang?, to destinationLang: Lang, completion: @escaping (Result) -> Void) -> URLSessionDataTask + func translate(text: String, from sourceLang: Lang?, to destinationLang: Lang, timeout: Double?, completion: @escaping (Result) -> Void) -> URLSessionDataTask +} + +extension TranslationServiceInterface { + func translate(text: String, + from sourceLang: Lang?, + to destinationLang: Lang, + timeout: Double? = nil, + completion: @escaping (Result) -> Void) -> URLSessionDataTask { + return translate(text: text, + from: sourceLang, + to: destinationLang, + timeout: timeout, + completion: completion) + } } class TranslationService: TranslationServiceInterface { @@ -33,7 +47,7 @@ class TranslationService: TranslationServiceInterface { //MARK: - TranslationServiceInterface extension TranslationService { - func availableLanguages(completion: @escaping (Result<[LangExtended]>) -> Void) { + func availableLanguages(completion: @escaping (Result<[Language]>) -> Void) { var urlComponents = EndPoints.languages.urlComponents urlComponents.queryItems = [ URLQueryItem(name: Keys.apiKey, value: apiKey), @@ -44,7 +58,7 @@ extension TranslationService { request.httpMethod = EndPoints.languages.method let isSuccessMethod: (Data?, URLResponse?, Error?) -> Bool = isSuccess(data:response:error:) - let parseAvailableLanguagesMethod: (Data?) -> Result<[LangExtended]> = parseAvailableLanguages(data:) + let parseAvailableLanguagesMethod: (Data?) -> Result<[Language]> = parseAvailableLanguages(data:) URLSession.shared.dataTask(with: request) { (data, response, error) in guard isSuccessMethod(data, response, error) else { @@ -81,6 +95,7 @@ extension TranslationService { func translate(text: String, from sourceLang: Lang?, to destinationLang: Lang, + timeout: Double? = nil, completion: @escaping (Result) -> Void) -> URLSessionDataTask { var urlComponents = EndPoints.translate.urlComponents urlComponents.queryItems = [URLQueryItem(name: Keys.apiKey, @@ -93,6 +108,10 @@ extension TranslationService { request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.httpMethod = EndPoints.translate.method request.httpBody = try? JSONSerialization.data(withJSONObject: params) + if let timeout = timeout { + request.timeoutInterval = timeout + } + let isSuccessMethod: (Data?, URLResponse?, Error?) -> Bool = isSuccess(data:response:error:) let parseTranslationMethod: (Data?) -> Result = parseTranslation(data:) @@ -140,7 +159,7 @@ extension TranslationService { } } - struct Keys { + enum Keys { static let apiKey = "key" static let text = "q" static let sourceLang = "source" @@ -151,6 +170,10 @@ extension TranslationService { case wrongJsonSchema case failedRequest } + + enum Constants { + static let translationTimeout: Double = 30.0 + } } //MARK: - Private @@ -167,7 +190,7 @@ private extension TranslationService { return false } - func parseAvailableLanguages(data: Data?) -> Result<[LangExtended]> { + func parseAvailableLanguages(data: Data?) -> Result<[Language]> { guard let data = data, let json = try? JSON(data: data)["data"].dictionaryValue, @@ -175,11 +198,11 @@ private extension TranslationService { else { return (nil, .wrongJsonSchema) } let result = languages - .map { $0.dictionaryValue }.map({ pair -> LangExtended? in + .map { $0.dictionaryValue }.map({ pair -> Language? in guard let language = pair["language"]?.stringValue, let name = pair["name"]?.stringValue else { return nil } - return LangExtended(language: language, name: name) + return Language(language: language, name: name) }).compactMap { $0 } return (result, nil) diff --git a/Nynja/Validation/UseCaseValidationService.swift b/Nynja/Validation/UseCaseValidationService.swift index 08dde2d94..a9de5a187 100644 --- a/Nynja/Validation/UseCaseValidationService.swift +++ b/Nynja/Validation/UseCaseValidationService.swift @@ -9,11 +9,11 @@ import Foundation protocol UseCaseValidationServiceProtocol { - func validateInterpretationLanguages(_ fromLanguage: Language, toLanguage: Language) throws + func validateInterpretationLanguages(_ fromLanguage: AppLanguage, toLanguage: AppLanguage) throws } final class UseCaseValidationService: UseCaseValidationServiceProtocol { - func validateInterpretationLanguages(_ fromLanguage: Language, toLanguage: Language) throws { + func validateInterpretationLanguages(_ fromLanguage: AppLanguage, toLanguage: AppLanguage) throws { if fromLanguage == .empty_default { throw UseCaseValidationError.interpretationLanguageNotSelected } diff --git a/Podfile b/Podfile index 548076973..1116892aa 100644 --- a/Podfile +++ b/Podfile @@ -39,12 +39,12 @@ def commonPodsForNynja pod 'MaterialComponents/FlexibleHeader', '= 55.3.0' pod 'JTAppleCalendar', '= 7.1.5' - pod 'NynjaSDK', '= 1.6.4' + pod 'NynjaSDK', '= 1.7.2' pod 'CryptoSwift', '= 0.10.0' - + pod 'MulticastDelegateSwift', '= 2.1.1' - + pod 'Intercom', '= 5.1.6' end diff --git a/Podfile.lock b/Podfile.lock index 0842ff6a7..dab71d509 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -62,7 +62,7 @@ PODS: - MaterialComponents/private/Application - MDFTextAccessibility (1.2.0) - MulticastDelegateSwift (2.1.1) - - NynjaSDK (1.6.4) + - NynjaSDK (1.7.2) - QRCode (2.0) - SDWebImage (4.4.2): - SDWebImage/Core (= 4.4.2) @@ -95,7 +95,7 @@ DEPENDENCIES: - libPhoneNumber-iOS (= 0.9.13) - MaterialComponents/FlexibleHeader (= 55.3.0) - MulticastDelegateSwift (= 2.1.1) - - NynjaSDK (= 1.6.4) + - NynjaSDK (= 1.7.2) - QRCode (= 2.0) - SDWebImage (= 4.4.2) - SnapKit (= 4.0.0) @@ -174,7 +174,7 @@ SPEC CHECKSUMS: MaterialComponents: 915f4e844400a35db3ea4c710a9af40aa8bcb093 MDFTextAccessibility: 94098925e0853551c5a311ce7c1ecefbe297cdb6 MulticastDelegateSwift: 93eb077c24f50574b3f8a3f23bf71be6de6e3b41 - NynjaSDK: c7a97aec34fa32e03dbfde08543fdd22a778b6e3 + NynjaSDK: 6be9e190568291e37d63a4f894cee992b775ede0 QRCode: f98a1886c8f37523704a7512a4c0cd45b34c18a4 SDWebImage: 624d6e296c69b244bcede364c72ae0430ac14681 SnapKit: a42d492c16e80209130a3379f73596c3454b7694 @@ -183,6 +183,6 @@ SPEC CHECKSUMS: SwiftyTimer: 2efd74b060d69ad4f1496baf5bbedbe132125fcf TestFairy: 842f8ddc45477b208eb85326b0418047b40f7137 -PODFILE CHECKSUM: 719851bfefcc47c78fd1dab78575672ba545d14f +PODFILE CHECKSUM: 0991153587e19d0f90754a96646d87475ef2839f COCOAPODS: 1.5.3 diff --git a/Shared/Library/Extensions/Models/Message/Message+Files.swift b/Shared/Library/Extensions/Models/Message/Message+Files.swift index fa170b9a8..a2199d06a 100644 --- a/Shared/Library/Extensions/Models/Message/Message+Files.swift +++ b/Shared/Library/Extensions/Models/Message/Message+Files.swift @@ -64,4 +64,16 @@ extension Message { var sendType: SendMessageType? { return mainFile?.mime.flatMap { SendMessageType(rawValue: $0) } } + + var isDownloadable: Bool { + return sendType.flatMap { [.audio, .image, .file, .video].contains($0) } ?? false + } + + func isExistFile(for type: SendMessageType) -> Bool { + return file(for: type) != nil + } + + func file(for type: SendMessageType) -> Desc? { + return files?.first(where: { $0.type == type }) + } } diff --git a/Shared/Library/Extensions/Models/Message/Message+LocalStatus.swift b/Shared/Library/Extensions/Models/Message/Message+LocalStatus.swift index 380db65d8..32a6d154d 100644 --- a/Shared/Library/Extensions/Models/Message/Message+LocalStatus.swift +++ b/Shared/Library/Extensions/Models/Message/Message+LocalStatus.swift @@ -18,5 +18,7 @@ extension Message { static let deleted = LocalStatus(rawValue: 1 << 0) static let replied = LocalStatus(rawValue: 1 << 1) + static let transcribe = LocalStatus(rawValue: 1 << 2) + static let translate = LocalStatus(rawValue: 1 << 3) } } diff --git a/Shared/Services/AmazonInitializer/AmazonInitializer.swift b/Shared/Services/AmazonInitializer/AmazonInitializer.swift new file mode 100644 index 000000000..7c59ab9a8 --- /dev/null +++ b/Shared/Services/AmazonInitializer/AmazonInitializer.swift @@ -0,0 +1,13 @@ +// +// AmazonInitializer.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 10/12/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol AmazonInitializer { + func initialize() +} diff --git a/Shared/Services/AmazonInitializer/AmazonInitializerImpl.swift b/Shared/Services/AmazonInitializer/AmazonInitializerImpl.swift new file mode 100644 index 000000000..ffd6cbd11 --- /dev/null +++ b/Shared/Services/AmazonInitializer/AmazonInitializerImpl.swift @@ -0,0 +1,35 @@ +// +// AmazonInitializerImpl.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 10/12/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import AWSS3 + +class AmazonInitializerImpl: AmazonInitializer { + + func initialize() { + let amazonService = ThirdPartyServicesFactory.amazon + + let credentialsProvider = AWSStaticCredentialsProvider( + accessKey: amazonService.serviceConfig.accessKey, + secretKey: amazonService.serviceConfig.secretKey + ) + + let defaultConfiguration = AWSServiceConfiguration( + region: .USWest2, + credentialsProvider: credentialsProvider + )! + AWSS3TransferManager.register(with: defaultConfiguration, forDestination: .default) + + let stickerStorageConfiguration = AWSServiceConfiguration( + region: .EUCentral1, + credentialsProvider: credentialsProvider + )! + AWSS3TransferManager.register(with: stickerStorageConfiguration, forDestination: .stickerPacks) + + AWSServiceManager.default().defaultServiceConfiguration = defaultConfiguration + } +} diff --git a/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift b/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift new file mode 100644 index 000000000..1b8274323 --- /dev/null +++ b/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift @@ -0,0 +1,41 @@ +// +// SharedServiceFactory.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 10/12/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +protocol SharedServiceFactoryProtocol: class { + func makeMQTTService() -> MQTTService + func makeStorageService() -> StorageService + func makeAmazonManager() -> AmazonManager + func makeAmazonInitializer() -> AmazonInitializer + func makeTypingSenderService() -> TypingSenderServiceProtocol +} + +class SharedServiceFactory: SharedServiceFactoryProtocol { + + func makeMQTTService() -> MQTTService { + return MQTTService.sharedInstance + } + + func makeStorageService() -> StorageService { + return StorageService.sharedInstance + } + + func makeAmazonManager() -> AmazonManager { + return AmazonManager.shared + } + + func makeAmazonInitializer() -> AmazonInitializer { + return AmazonInitializerImpl() + } + + func makeTypingSenderService() -> TypingSenderServiceProtocol { + let dependencies = TypingSenderService.Dependencies(mqttService: makeMQTTService(), + storageService: makeStorageService()) + + return TypingSenderService(dependencies: dependencies) + } +} -- GitLab From a0b996c61cc0a497511d1fa4c58a387ccf420a7c Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Wed, 5 Dec 2018 14:52:34 +0200 Subject: [PATCH 03/41] 0.5.5.RC --- Nynja-Share/Resources/Info.plist | 2 +- Nynja.xcodeproj/project.pbxproj | 6 +++--- Nynja/Resources/Info.plist | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist index 30f12f746..a76863273 100644 --- a/Nynja-Share/Resources/Info.plist +++ b/Nynja-Share/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.5 + 0.5.5.RC Config $(Config) ModelsVersion diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index a572cf8e3..6cf444ce2 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -14504,7 +14504,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftgen >/dev/null; then\nswiftgen\nfi"; + shellScript = "if which swiftgen >/dev/null; then\nswiftgen\nfi\n"; }; 3ABCE9081EC93B4A00A80B15 /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -14518,7 +14518,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Fabric/run\" 595b68a8c4deb3533dcdfc24ca73fd3cffd99f3c 27e6d0f170d463c67f2968218ccba54d2f8f44cfa13615771a1dee6f72469dcb"; + shellScript = "\"${PODS_ROOT}/Fabric/run\" 595b68a8c4deb3533dcdfc24ca73fd3cffd99f3c 27e6d0f170d463c67f2968218ccba54d2f8f44cfa13615771a1dee6f72469dcb\n\"${SRCROOT}/Pods/TestFairy/upload-dsym.sh\" 595b68a8c4deb3533dcdfc24ca73fd3cffd99f3c 27e6d0f170d463c67f2968218ccba54d2f8f44cfa13615771a1dee6f72469dcb\n"; }; 5EF78D1226FD7A5DAFF4A6A5 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -18572,7 +18572,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "5dca1418-4061-4c52-958f-3edf7251f19b"; - PROVISIONING_PROFILE_SPECIFIER = NynjaRC_devExt; + PROVISIONING_PROFILE_SPECIFIER = NynjaRC_adhocExt; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/Nynja/Resources/Info.plist b/Nynja/Resources/Info.plist index 9c128eb7e..f49b53a29 100644 --- a/Nynja/Resources/Info.plist +++ b/Nynja/Resources/Info.plist @@ -23,7 +23,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.5 + 0.5.5.RC ConfServerAddress $(ConfServerAddress) ConfServerPort -- GitLab From dfdc8ffe65b28e902630b5327302e9036ad1ee98 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Thu, 6 Dec 2018 15:45:34 +0200 Subject: [PATCH 04/41] [MQTT Refactoring] --- Nynja.xcodeproj/project.pbxproj | 10 ++-- .../Splash/WireFrame/SplashWireframe.swift | 2 +- Nynja/Services/MQTT/MQTTService.swift | 49 +++++++++++++++++-- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 6cf444ce2..8400395d6 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -14518,7 +14518,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Fabric/run\" 595b68a8c4deb3533dcdfc24ca73fd3cffd99f3c 27e6d0f170d463c67f2968218ccba54d2f8f44cfa13615771a1dee6f72469dcb\n\"${SRCROOT}/Pods/TestFairy/upload-dsym.sh\" 595b68a8c4deb3533dcdfc24ca73fd3cffd99f3c 27e6d0f170d463c67f2968218ccba54d2f8f44cfa13615771a1dee6f72469dcb\n"; + shellScript = "\"${PODS_ROOT}/Fabric/run\" 595b68a8c4deb3533dcdfc24ca73fd3cffd99f3c 27e6d0f170d463c67f2968218ccba54d2f8f44cfa13615771a1dee6f72469dcb\n"; }; 5EF78D1226FD7A5DAFF4A6A5 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -18152,7 +18152,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Nynja/Resources/Nynja.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; @@ -18165,7 +18165,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "e4b654e5-ea23-4135-bb19-3edc95e49642"; - PROVISIONING_PROFILE_SPECIFIER = DevBundle_adhoc; + PROVISIONING_PROFILE_SPECIFIER = DevBundle_Dev; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h"; @@ -18185,7 +18185,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = "Nynja-Share/Resources/Nynja-Share.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 9GKQ5AMF2B; @@ -18198,7 +18198,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "5dca1418-4061-4c52-958f-3edf7251f19b"; - PROVISIONING_PROFILE_SPECIFIER = DevBundle_AdHocExt; + PROVISIONING_PROFILE_SPECIFIER = DevBundle_DevExt; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_COMPILATION_MODE = singlefile; diff --git a/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift b/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift index 949955c74..d8f3c594a 100644 --- a/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift +++ b/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift @@ -38,7 +38,7 @@ class SplashWireFrame: SplashWireFrameProtocol { } func showMain() { - MainWireFrame().presentMain(navigation: navigation!, isRegistered: false, checkSession: true) + MainWireFrame().presentMain(navigation: navigation!, isRegistered: false, checkSession: false) } func showEditProfile() { diff --git a/Nynja/Services/MQTT/MQTTService.swift b/Nynja/Services/MQTT/MQTTService.swift index b77c4d60a..76303e7cd 100644 --- a/Nynja/Services/MQTT/MQTTService.swift +++ b/Nynja/Services/MQTT/MQTTService.swift @@ -78,6 +78,8 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat private override init() { MQTTLog.setLogLevel(DDLogLevel.off) mqtt = MQTTSession() + super.init() + ConnectionService.shared.addSubscriber(self) } @@ -102,6 +104,7 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat mqtt.cleanSessionFlag = false } autoReconnectTimeInterval = 15 + logState() } private func commonSetup() { @@ -145,11 +148,14 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat } func reconnect() { - disconnect() + if mqtt.status == .connected { + disconnect() + } connect() } func disconnect() { + logState() queuePool.processingQueue.async { [unowned self] in self.mqtt?.disconnect() } @@ -183,6 +189,7 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat } func connected(_ session: MQTTSession!) { + logState() queuePool.processingQueue.async { [unowned self] in self.handleAccept() } @@ -199,6 +206,7 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat } func connectionClosed(_ session: MQTTSession!) { + logState() queuePool.processingQueue.async { [unowned self] in self.isConnectedSuccess = false } @@ -212,6 +220,10 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat } } } + + func handleEvent(_ session: MQTTSession!, event eventCode: MQTTSessionEvent, error: Error!) { + print(eventCode) + } private func handleBadUsernameOrPassword() { if let actualToken = storageService.token, actualToken == mqtt.password { @@ -243,7 +255,6 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat } } - // MARK: Subscribers func addSubscriber(_ subscriber: MQTTServiceDelegate) { @@ -280,6 +291,12 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat } } + private func logState() { + LogService.log(topic: .MQTT) { () -> String in + return "\n----- MQTT ----- \n ClientID: \(self.mqtt.clientId ?? "") \n Password: \(self.mqtt.password ?? "") \n CleanSession: \(self.mqtt.cleanSessionFlag) \n State: \(self.mqtt.status.descript)) \n----- MQTT ----- \n " + } + } + // MARK: - ConnectionServiceDelegate @@ -291,9 +308,12 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat if service == .networking { guard let networkStatus = sender.state[.networking] else { return } switch networkStatus { - case .connected: self.mqtt?.connect() - case .disconnected: self.disconnect() - case .switched: self.reconnect() + case .connected: + self.mqtt?.connect() + case .disconnected: + self.disconnect() + case .switched: + self.reconnect() default: break } } @@ -314,3 +334,22 @@ struct MQTTMessage { if qos == nil { self.qos = .exactlyOnce } } } + +extension MQTTSessionStatus { + var descript: String { + switch self { + case .closed: + return "Closed" + case .connected: + return "Connected" + case .connecting: + return "Connecting" + case .created: + return "Created" + case .disconnecting: + return "Disconnecting" + case .error: + return "Error" + } + } +} -- GitLab From a4d6c1e98d2b620511932051ad80b0022f769484 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Tue, 11 Dec 2018 22:20:05 +0200 Subject: [PATCH 05/41] Fix some issue with Error handling MQTT (#1519) --- .../Services/Handlers/ProfileHandler.swift | 27 +-- .../ForwardSelectorInteractor.swift | 2 +- Nynja.xcodeproj/project.pbxproj | 58 ++++- Nynja/AppDelegate.swift | 42 +--- .../Login/Interactor/LoginInteractor.swift | 1 - .../Main/Interactor/MainInteractor.swift | 6 - Nynja/Modules/Main/MainProtocols.swift | 3 +- .../Main/WireFrame/MainWireframe.swift | 6 +- .../WireFrame/LanguageSettingsWireframe.swift | 2 +- .../Splash/Interactor/SplashInteractor.swift | 4 - .../Splash/WireFrame/SplashWireframe.swift | 2 +- .../AppNotificationsProvider.swift | 13 +- .../AppNotificationsProviding.swift | 1 + Nynja/Services/ConnectionService.swift | 1 - .../AppGroupConnectionHandler.swift | 41 ++++ .../AppLifecycleConnectionHandler.swift | 26 +++ .../AutoReconnectConnectionHandler.swift | 43 ++++ .../ReachabilityConnectionHandler.swift | 40 ++++ .../TokenConnectionHandler.swift | 24 +++ .../MQTT/Extensions/MQTTService+Helper.swift | 4 +- .../Services/MQTT/MQTTConnectionHandler.swift | 11 + Nynja/Services/MQTT/MQTTService.swift | 199 ++++++++++-------- Nynja/Services/MQTT/MQTTServiceProtocol.swift | 2 - .../ServiceFactory/ServiceFactory.swift | 5 - Nynja/Services/StorageService.swift | 11 + Nynja/UserIdentifiers.swift | 21 ++ Nynja/UserInfo.swift | 13 +- Nynja/UserInfoImpl.swift | 5 +- .../Services/UserInfo/UserInfoTest.swift | 1 + .../Handlers/AuthHandler/AuthHandler.swift | 6 +- .../SharedServiceFactory.swift | 5 + 31 files changed, 435 insertions(+), 190 deletions(-) create mode 100644 Nynja/Services/MQTT/ConnectionHandlers/AppGroupConnectionHandler.swift create mode 100644 Nynja/Services/MQTT/ConnectionHandlers/AppLifecycleConnectionHandler.swift create mode 100644 Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift create mode 100644 Nynja/Services/MQTT/ConnectionHandlers/ReachabilityConnectionHandler.swift create mode 100644 Nynja/Services/MQTT/ConnectionHandlers/TokenConnectionHandler.swift create mode 100644 Nynja/Services/MQTT/MQTTConnectionHandler.swift create mode 100644 Nynja/UserIdentifiers.swift diff --git a/Nynja-Share/Services/Handlers/ProfileHandler.swift b/Nynja-Share/Services/Handlers/ProfileHandler.swift index 200095636..faceba4a8 100644 --- a/Nynja-Share/Services/Handlers/ProfileHandler.swift +++ b/Nynja-Share/Services/Handlers/ProfileHandler.swift @@ -10,33 +10,26 @@ import Foundation protocol ProfileHandlerDelegate: class { func getProfileSuccess(model: Profile) - func removeProfileSuccess() } extension ProfileHandlerDelegate { func getProfileSuccess(model: Profile) {} - func removeProfileSuccess() {} } class ProfileHandler:BaseHandler { static weak var delegate :ProfileHandlerDelegate? static func executeHandle(data: BertTuple) { - if let profile = get_Profile().parse(bert: data) as? Profile { - if let status = profile.status?.string { - switch status { - case "get", "init": - self.delegate?.getProfileSuccess(model: profile) - case "set": - break - case "remove": - self.delegate?.removeProfileSuccess() - MQTTService.sharedInstance.reconnect() - default: - break - } - } + guard let profile = get_Profile().parse(bert: data) as? Profile, + let status = profile.status?.string else { + return + } + + switch status { + case "get", "init": + self.delegate?.getProfileSuccess(model: profile) + default: + break } } - } diff --git a/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift b/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift index 7acf73a6e..da6bc445b 100644 --- a/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift +++ b/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift @@ -93,7 +93,7 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P func connectToServer() { if let token = StorageService.sharedInstance.token { LogService.log(topic: .MQTT) { return "token: \(token)" } - _ = MQTTService.sharedInstance.reconnect() + MQTTService.sharedInstance.connect() } else { handlerServerSignals?(.noToken) } diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 8400395d6..246d453da 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -572,6 +572,12 @@ 4B0CC1FE2195B52000E0BA61 /* IoHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CC1FC2195B52000E0BA61 /* IoHandlerDelegate.swift */; }; 4B0CC1FF2195B58000E0BA61 /* StaticDelegating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B87717C2195AFF50014AD09 /* StaticDelegating.swift */; }; 4B0CC2022195B69900E0BA61 /* LinkHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CC2012195B69900E0BA61 /* LinkHandlerDelegate.swift */; }; + 4B1162FA21BFC1FB003859ED /* AppLifecycleConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AAD21BEB0D900FCF879 /* AppLifecycleConnectionHandler.swift */; }; + 4B1162FC21BFC1FB003859ED /* AutoReconnectConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AB121BEB42600FCF879 /* AutoReconnectConnectionHandler.swift */; }; + 4B1162FD21BFC1FB003859ED /* ReachabilityConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AB321BEB8B100FCF879 /* ReachabilityConnectionHandler.swift */; }; + 4B1162FE21BFC1FB003859ED /* TokenConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AB721BEC1C600FCF879 /* TokenConnectionHandler.swift */; }; + 4B1162FF21BFC213003859ED /* AppNotificationsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B71AC4121622A6A00E4583B /* AppNotificationsProvider.swift */; }; + 4B11630121BFC277003859ED /* AppNotificationsProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B71AC4421622AA700E4583B /* AppNotificationsProviding.swift */; }; 4B1D7DFA2029BF3400703228 /* HistoryItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7DF92029BF3400703228 /* HistoryItemsFactory.swift */; }; 4B1D7DFC2029C37900703228 /* FavoritesItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7DFB2029C37900703228 /* FavoritesItemsFactory.swift */; }; 4B1D7DFE2029C41C00703228 /* AboutItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7DFD2029C41C00703228 /* AboutItemsFactory.swift */; }; @@ -618,6 +624,15 @@ 4B4266BF204D916000194BC1 /* ActionsView+Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4266BE204D916000194BC1 /* ActionsView+Action.swift */; }; 4B4266C1204D917800194BC1 /* ActionsView+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4266C0204D917800194BC1 /* ActionsView+Layout.swift */; }; 4B4266C3204D923400194BC1 /* Array+UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4266C2204D923400194BC1 /* Array+UIView.swift */; }; + 4B483AAC21BEB0A900FCF879 /* MQTTConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AAB21BEB0A900FCF879 /* MQTTConnectionHandler.swift */; }; + 4B483AAE21BEB0D900FCF879 /* AppLifecycleConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AAD21BEB0D900FCF879 /* AppLifecycleConnectionHandler.swift */; }; + 4B483AB021BEB1D000FCF879 /* AppGroupConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AAF21BEB1D000FCF879 /* AppGroupConnectionHandler.swift */; }; + 4B483AB221BEB42600FCF879 /* AutoReconnectConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AB121BEB42600FCF879 /* AutoReconnectConnectionHandler.swift */; }; + 4B483AB421BEB8B100FCF879 /* ReachabilityConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AB321BEB8B100FCF879 /* ReachabilityConnectionHandler.swift */; }; + 4B483AB621BEC0C100FCF879 /* UserIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AB521BEC0C100FCF879 /* UserIdentifiers.swift */; }; + 4B483AB821BEC1C600FCF879 /* TokenConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AB721BEC1C600FCF879 /* TokenConnectionHandler.swift */; }; + 4B483AB921BEC84200FCF879 /* UserIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AB521BEC0C100FCF879 /* UserIdentifiers.swift */; }; + 4B483ABA21BED06A00FCF879 /* MQTTConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B483AAB21BEB0A900FCF879 /* MQTTConnectionHandler.swift */; }; 4B5A0B73216E3BDD002C4160 /* ActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5A0B6F216E3BDD002C4160 /* ActionsView.swift */; }; 4B5A0B74216E3BDD002C4160 /* ForwardAvatarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5A0B70216E3BDD002C4160 /* ForwardAvatarViewModel.swift */; }; 4B5A0B75216E3BDD002C4160 /* ProgressHUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5A0B71216E3BDD002C4160 /* ProgressHUD.swift */; }; @@ -2925,6 +2940,13 @@ 4B4266BE204D916000194BC1 /* ActionsView+Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActionsView+Action.swift"; sourceTree = ""; }; 4B4266C0204D917800194BC1 /* ActionsView+Layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActionsView+Layout.swift"; sourceTree = ""; }; 4B4266C2204D923400194BC1 /* Array+UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+UIView.swift"; sourceTree = ""; }; + 4B483AAB21BEB0A900FCF879 /* MQTTConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTConnectionHandler.swift; sourceTree = ""; }; + 4B483AAD21BEB0D900FCF879 /* AppLifecycleConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLifecycleConnectionHandler.swift; sourceTree = ""; }; + 4B483AAF21BEB1D000FCF879 /* AppGroupConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroupConnectionHandler.swift; sourceTree = ""; }; + 4B483AB121BEB42600FCF879 /* AutoReconnectConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoReconnectConnectionHandler.swift; sourceTree = ""; }; + 4B483AB321BEB8B100FCF879 /* ReachabilityConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReachabilityConnectionHandler.swift; sourceTree = ""; }; + 4B483AB521BEC0C100FCF879 /* UserIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIdentifiers.swift; sourceTree = ""; }; + 4B483AB721BEC1C600FCF879 /* TokenConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenConnectionHandler.swift; sourceTree = ""; }; 4B5A0B6F216E3BDD002C4160 /* ActionsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionsView.swift; sourceTree = ""; }; 4B5A0B70216E3BDD002C4160 /* ForwardAvatarViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForwardAvatarViewModel.swift; sourceTree = ""; }; 4B5A0B71216E3BDD002C4160 /* ProgressHUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressHUD.swift; sourceTree = ""; }; @@ -6121,9 +6143,11 @@ 3A8045CC1F60C8E200AED866 /* MQTT */ = { isa = PBXGroup; children = ( - 4B030F392195CF7600F293B7 /* Entities */, 4B030F342195CD4A00F293B7 /* API */, + 4B483AAA21BEA80200FCF879 /* ConnectionHandlers */, + 4B030F392195CF7600F293B7 /* Entities */, 4B030F382195CDDA00F293B7 /* Extensions */, + 4B483AAB21BEB0A900FCF879 /* MQTTConnectionHandler.swift */, 3A8045CD1F60C8E200AED866 /* MQTTService.swift */, 4B030F312195CD4500F293B7 /* MQTTServiceDelegate.swift */, 4B055C3A219C4101001FE077 /* MQTTServiceProtocol.swift */, @@ -6868,6 +6892,18 @@ path = ActionsView; sourceTree = ""; }; + 4B483AAA21BEA80200FCF879 /* ConnectionHandlers */ = { + isa = PBXGroup; + children = ( + 4B483AAD21BEB0D900FCF879 /* AppLifecycleConnectionHandler.swift */, + 4B483AAF21BEB1D000FCF879 /* AppGroupConnectionHandler.swift */, + 4B483AB121BEB42600FCF879 /* AutoReconnectConnectionHandler.swift */, + 4B483AB321BEB8B100FCF879 /* ReachabilityConnectionHandler.swift */, + 4B483AB721BEC1C600FCF879 /* TokenConnectionHandler.swift */, + ); + path = ConnectionHandlers; + sourceTree = ""; + }; 4B5A0B6E216E3BDD002C4160 /* Views */ = { isa = PBXGroup; children = ( @@ -10290,6 +10326,7 @@ children = ( A4330A642109DFA00060BD93 /* UserInfo.swift */, 4B030F3D2195D88100F293B7 /* UserInfoImpl.swift */, + 4B483AB521BEC0C100FCF879 /* UserIdentifiers.swift */, ); name = UserInfo; sourceTree = ""; @@ -11166,9 +11203,9 @@ isa = PBXGroup; children = ( 4B3B35D521711981005A214A /* AmazonInitializer */, - 4B3B35D421711977005A214A /* SharedServiceFactory */, - 852C3DD0216E3A4300447878 /* Messaging */, A4A242462060372100B0A804 /* Handlers */, + 852C3DD0216E3A4300447878 /* Messaging */, + 4B3B35D421711977005A214A /* SharedServiceFactory */, ); path = Services; sourceTree = ""; @@ -14763,6 +14800,8 @@ A42CE56C20692EDB000889CC /* p2p.swift in Sources */, 85458CF6212D770900BA8814 /* Message+Files.swift in Sources */, A42CE5F620692EDB000889CC /* Person_Spec.swift in Sources */, + 4B11630121BFC277003859ED /* AppNotificationsProviding.swift in Sources */, + 4B1162FE21BFC1FB003859ED /* TokenConnectionHandler.swift in Sources */, A42CE57220692EDB000889CC /* chain.swift in Sources */, 269848CC200EA0ED00590D6F /* StarModels.swift in Sources */, FB61D653214FEC8500CB2A1F /* AssetsConstants.swift in Sources */, @@ -14840,8 +14879,11 @@ 26C0C1DD2073D96F00C530DA /* MucExtension.swift in Sources */, 26A856362074D24300C642EA /* Theme.swift in Sources */, 4B5A0B7D216E3D20002C4160 /* AttachmentProvider.swift in Sources */, + 4B1162FD21BFC1FB003859ED /* ReachabilityConnectionHandler.swift in Sources */, 26C0C1E72073DABB00C530DA /* DateExtensions.swift in Sources */, 26A8561E2074C38F00C642EA /* NavigationView.swift in Sources */, + 4B1162FF21BFC213003859ED /* AppNotificationsProvider.swift in Sources */, + 4B1162FA21BFC1FB003859ED /* AppLifecycleConnectionHandler.swift in Sources */, 359EB2571F9A1E5000147437 /* BaseMQTTModel.swift in Sources */, A42CE5FE20692EDB000889CC /* Test_Spec.swift in Sources */, A42CE58C20692EDB000889CC /* sequenceFlow.swift in Sources */, @@ -15028,6 +15070,7 @@ A4330A722109EBB30060BD93 /* CountriesProviding.swift in Sources */, A42CE5FC20692EDB000889CC /* userTask_Spec.swift in Sources */, A42CE5CE20692EDB000889CC /* Vox_Spec.swift in Sources */, + 4B483ABA21BED06A00FCF879 /* MQTTConnectionHandler.swift in Sources */, A42CE56620692EDB000889CC /* receiveTask.swift in Sources */, A42CE5E220692EDB000889CC /* ExtendedStar_Spec.swift in Sources */, A42CE61220692EDB000889CC /* Typing_Spec.swift in Sources */, @@ -15054,6 +15097,7 @@ 26C0C1D22073CBA200C530DA /* DialogCellModel.swift in Sources */, 26C0C1CC2073C93F00C530DA /* ForwardSelectorMode.swift in Sources */, A45F114D20B4222F00F45004 /* PresenceStatus.swift in Sources */, + 4B483AB921BEC84200FCF879 /* UserIdentifiers.swift in Sources */, 26C0C1C02073C70F00C530DA /* ForwardAvatarCollectionViewCell.swift in Sources */, 4BF090CA21635F6900DCCA5C /* Message+LocalStatus.swift in Sources */, 8524C4DB21777456003BF374 /* Service+Construct.swift in Sources */, @@ -15082,6 +15126,7 @@ 4B3B35DA217119C9005A214A /* AmazonInitializer.swift in Sources */, A42CE58220692EDB000889CC /* CDR.swift in Sources */, 4B3B35D32171120F005A214A /* SharedServiceFactory.swift in Sources */, + 4B1162FC21BFC1FB003859ED /* AutoReconnectConnectionHandler.swift in Sources */, 4BAE7DDB21B69AC70018D3C2 /* TimerHandler.swift in Sources */, A4B544EB20EFB36100EB7B0F /* errors.swift in Sources */, 8551CF12217094CD00829CF1 /* Array+Desc.swift in Sources */, @@ -15146,6 +15191,7 @@ 4B1D7E112029FF5000703228 /* Array+WheelItemModel.swift in Sources */, A46C36342121999100172773 /* DDMechanism.swift in Sources */, 854A4B302080D6C400759152 /* CellWithImageTableViewCell.swift in Sources */, + 4B483AB621BEC0C100FCF879 /* UserIdentifiers.swift in Sources */, 3A8045D81F60C98200AED866 /* MQTTService+Helper.swift in Sources */, 8E9601971FF2EC8100E0C21D /* GroupFilesListVC.swift in Sources */, 4B06D3222028A9C6003B275B /* GroupChatItemsFactory.swift in Sources */, @@ -15183,6 +15229,7 @@ A42D52C6206A53AA00EEB952 /* Feature_Spec.swift in Sources */, F10AFEB620F7B1B000C7CE83 /* WheelImageFullItemPreview.swift in Sources */, A45F112A20B4218D00F45004 /* BubbleInjectible.swift in Sources */, + 4B483AAE21BEB0D900FCF879 /* AppLifecycleConnectionHandler.swift in Sources */, 4B1D7E14202A0A0200703228 /* GroupMode.swift in Sources */, 3A27B0A71EF307A900B4B3CB /* DeleteUserModel.swift in Sources */, 3A1F74FA1F5ED344009A11E4 /* PushService.swift in Sources */, @@ -15222,6 +15269,7 @@ C9C694FD201FA55800A57297 /* SwipeBackHelper.swift in Sources */, 85CE26D820C5593600553FE7 /* HapticSelectionFeedbackGenerator.swift in Sources */, A49381AA21355EE1006D28DD /* MessageInteractor+Forward.swift in Sources */, + 4B483AB421BEB8B100FCF879 /* ReachabilityConnectionHandler.swift in Sources */, 26FA4210201821B400E6F6EC /* StarHandler.swift in Sources */, 4BE2C5DA2142EAC500A73DD9 /* AudioSessionManager.swift in Sources */, 00E98252205C26F7008BF03D /* SessionLineView.swift in Sources */, @@ -15243,6 +15291,7 @@ E7EED2361F740CEA005DAE20 /* NewContactItem.swift in Sources */, FEA65617216779F700B44029 /* WalletServiceWallet.swift in Sources */, 26DCB2502064BA3D001EF0AB /* ContactsPresenter.swift in Sources */, + 4B483AAC21BEB0A900FCF879 /* MQTTConnectionHandler.swift in Sources */, FEA655E92167777E00B44029 /* TransferHistoryPresenter.swift in Sources */, A42D51B4206A361400EEB952 /* container.swift in Sources */, 265F5D29209B6E1F008ACCC8 /* ParticipantsViewControllerLayout.swift in Sources */, @@ -15381,6 +15430,7 @@ 268C340F21067BAD00F1472A /* AudioUploadOperation.swift in Sources */, 8509FC812158E11000734D93 /* TableAlternationExtension.swift in Sources */, 8580BAF320BD9B8000239D9D /* String+Suffix.swift in Sources */, + 4B483AB021BEB1D000FCF879 /* AppGroupConnectionHandler.swift in Sources */, A43B259620AB1DFA00FF8107 /* RecordDisplayInputContent.swift in Sources */, 4B4266C1204D917800194BC1 /* ActionsView+Layout.swift in Sources */, AF440BA5CEBE5170D082FF60 /* LoginProtocols.swift in Sources */, @@ -15990,6 +16040,7 @@ A42D51A5206A361400EEB952 /* act.swift in Sources */, E7C36C371FC469E600740630 /* ProfileExtension.swift in Sources */, 26ED2C1820042683002DBBE8 /* RepliesDS.swift in Sources */, + 4B483AB821BEC1C600FCF879 /* TokenConnectionHandler.swift in Sources */, FEA6562121677B3500B44029 /* MessageTransferView.swift in Sources */, 2605311F21274124002E1CF1 /* LogOutputInteractor.swift in Sources */, 9BE521222189B2E10070C664 /* ThreeButtonHeaderView.swift in Sources */, @@ -16932,6 +16983,7 @@ 4B7C73F4215A5509007924DB /* LogService.swift in Sources */, A42D52D0206A53AB00EEB952 /* Friend_Spec.swift in Sources */, F11786D120A98685007A9A1B /* MessageFactory.swift in Sources */, + 4B483AB221BEB42600FCF879 /* AutoReconnectConnectionHandler.swift in Sources */, 84BB63C68EA124AA7DD21B30 /* LanguageSettingsProtocols.swift in Sources */, FEA656022167777F00B44029 /* WalletBalancesViewController.swift in Sources */, 69CA7311E49F87A5CACC8A73 /* LanguageSettingsViewController.swift in Sources */, diff --git a/Nynja/AppDelegate.swift b/Nynja/AppDelegate.swift index 6e9bfadb2..93abd7815 100644 --- a/Nynja/AppDelegate.swift +++ b/Nynja/AppDelegate.swift @@ -29,9 +29,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD return imgView }() - private let storageService = StorageService.sharedInstance private let antiDebuggingService = AntiDebuggingService() + private var storageService: StorageService { + return .sharedInstance + } + // FIXME: need to be removed from here when share extension won't require new mqtt connection. private var appGroupObserver: AppGroupFlagObserver? @@ -42,9 +45,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD configureDependencies() - observeGroupAppChanges() - - wipeStorage() + wipeKeychain() configureWindow() @@ -70,18 +71,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD func applicationDidBecomeActive(_ application: UIApplication) { backgroundImageView.removeFromSuperview() } - - func applicationDidEnterBackground(_ application: UIApplication) { - MQTTService.sharedInstance.disconnect() - } - - func applicationWillEnterForeground(_ application: UIApplication) { - MQTTService.sharedInstance.reconnect() - } - - func applicationWillTerminate(_ application: UIApplication) { - MQTTService.sharedInstance.disconnect() - } } // MARK: - UNUserNotificationCenterDelegate @@ -124,30 +113,13 @@ private extension AppDelegate { FileManagerService.sharedInstance.createDirectory(dirName: Constants.Folders.downloads) } - private func wipeStorage() { + private func wipeKeychain() { if !storageService.wasRun { LogService.log(topic: .db) { return "Clear storage: AppDelegate - if it is first runs" } - storageService.clearStorage() + storageService.wipeKeychain() storageService.wasRun = true } } - - private func observeGroupAppChanges() { - self.appGroupObserver = AppGroupFlagObserver(fileManager: .default, appGroup: Bundle.main.appGroupName) - do { - try appGroupObserver?.prepare() - try appGroupObserver?.removeFlagIfExists(.shareExtension) - try appGroupObserver?.observe { flags in - if flags.contains(.shareExtension) { - MQTTService.sharedInstance.disconnect() - } else { - MQTTService.sharedInstance.reconnect() - } - } - } catch { - LogService.log(topic: .fileSystem) { error.localizedDescription } - } - } } // MARK: - Setup third party services diff --git a/Nynja/Modules/Auth/Login/Interactor/LoginInteractor.swift b/Nynja/Modules/Auth/Login/Interactor/LoginInteractor.swift index 9e673cac1..608c4b93d 100644 --- a/Nynja/Modules/Auth/Login/Interactor/LoginInteractor.swift +++ b/Nynja/Modules/Auth/Login/Interactor/LoginInteractor.swift @@ -24,7 +24,6 @@ class LoginInteractor: BaseInteractor, LoginInteractorInputProtocol, IoHandlerDe func configure() { IoHandler.delegate = self mqttService.addSubscriber(self) - mqttService.tryReconnect() } deinit { diff --git a/Nynja/Modules/Main/Interactor/MainInteractor.swift b/Nynja/Modules/Main/Interactor/MainInteractor.swift index 244af25b0..682949212 100644 --- a/Nynja/Modules/Main/Interactor/MainInteractor.swift +++ b/Nynja/Modules/Main/Interactor/MainInteractor.swift @@ -81,10 +81,6 @@ final class MainInteractor: BaseInteractor, MainInteractorInputProtocol, EditPho // MARK: - MainInteractorInputProtocol - - func checkSession() { - mqttService.reconnect() - } func findContactBy(phoneId: String) -> Contact? { return ContactDAO.findContactBy(phoneId: phoneId) @@ -94,7 +90,6 @@ final class MainInteractor: BaseInteractor, MainInteractorInputProtocol, EditPho mqttService.logout() LogService.log(topic: .db) { return "Clear storage: logout" } cleanServices() - mqttService.reconnect() } func deleteAccount() { @@ -108,7 +103,6 @@ final class MainInteractor: BaseInteractor, MainInteractorInputProtocol, EditPho private func handleProfileDeleting() { cleanServices() alertManager.showAlertOk(message: String.localizable.authAttemptsRemoved) - mqttService.reconnect() presenter.hideUILocker() } diff --git a/Nynja/Modules/Main/MainProtocols.swift b/Nynja/Modules/Main/MainProtocols.swift index 48a9e0382..66b64a7a2 100644 --- a/Nynja/Modules/Main/MainProtocols.swift +++ b/Nynja/Modules/Main/MainProtocols.swift @@ -22,7 +22,7 @@ protocol MainWireFrameProtocol: class { /** * Add here your methods for communication PRESENTER -> WIREFRAME */ - func presentMain(navigation: UINavigationController, isRegistered: Bool, checkSession: Bool) + func presentMain(navigation: UINavigationController, isRegistered: Bool) func sendStatus(status: TypingModelType) func showQRReader() @@ -231,7 +231,6 @@ protocol MainInteractorInputProtocol: BaseInteractorProtocol { /** * Add here your methods for communication PRESENTER -> INTERACTOR */ - func checkSession () func call(phoneId: String) func videoCall(phoneId: String) func logout() diff --git a/Nynja/Modules/Main/WireFrame/MainWireframe.swift b/Nynja/Modules/Main/WireFrame/MainWireframe.swift index 1091b979e..cfa2d7494 100644 --- a/Nynja/Modules/Main/WireFrame/MainWireframe.swift +++ b/Nynja/Modules/Main/WireFrame/MainWireframe.swift @@ -19,7 +19,7 @@ final class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelega weak var external: EditParticipantsDelegate? = nil - func presentMain(navigation: UINavigationController, isRegistered: Bool, checkSession: Bool = false) { + func presentMain(navigation: UINavigationController, isRegistered: Bool) { let serviceFactory = ServiceFactory() let view = MainViewController() @@ -69,10 +69,6 @@ final class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelega } view.addViewControllerAsChild(childViewController: contentNavigation) - - if checkSession { - interactor.checkSession() - } } private func startNavigate() { diff --git a/Nynja/Modules/Settings/LanguageSettings/WireFrame/LanguageSettingsWireframe.swift b/Nynja/Modules/Settings/LanguageSettings/WireFrame/LanguageSettingsWireframe.swift index 101eb724f..b8ca23ba4 100644 --- a/Nynja/Modules/Settings/LanguageSettings/WireFrame/LanguageSettingsWireframe.swift +++ b/Nynja/Modules/Settings/LanguageSettings/WireFrame/LanguageSettingsWireframe.swift @@ -33,7 +33,7 @@ class LanguageSettingsWireFrame: LanguageSettingsWireFrameProtocol { func restart() { let nav = UINavigationController() nav.isNavigationBarHidden = true - MainWireFrame().presentMain(navigation: nav, isRegistered: false, checkSession: true) + MainWireFrame().presentMain(navigation: nav, isRegistered: false) UIApplication.shared.windows[0].rootViewController = nav } } diff --git a/Nynja/Modules/Splash/Interactor/SplashInteractor.swift b/Nynja/Modules/Splash/Interactor/SplashInteractor.swift index f683f5feb..7a740f094 100644 --- a/Nynja/Modules/Splash/Interactor/SplashInteractor.swift +++ b/Nynja/Modules/Splash/Interactor/SplashInteractor.swift @@ -85,11 +85,7 @@ class SplashInteractor: SplashInteractorInputProtocol { private func prepareToShowAuth() { LogService.log(topic: .db) { return "Clear storage: Splash" } - storageService.clearStorage() - mqttService.reconnect() - presenter.showAuth() } - } diff --git a/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift b/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift index d8f3c594a..84636f7d9 100644 --- a/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift +++ b/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift @@ -38,7 +38,7 @@ class SplashWireFrame: SplashWireFrameProtocol { } func showMain() { - MainWireFrame().presentMain(navigation: navigation!, isRegistered: false, checkSession: false) + MainWireFrame().presentMain(navigation: navigation!, isRegistered: false) } func showEditProfile() { diff --git a/Nynja/Notifications/AppNotificationsProviding/AppNotificationsProvider.swift b/Nynja/Notifications/AppNotificationsProviding/AppNotificationsProvider.swift index 02f17cc70..57dc2096e 100644 --- a/Nynja/Notifications/AppNotificationsProviding/AppNotificationsProvider.swift +++ b/Nynja/Notifications/AppNotificationsProviding/AppNotificationsProvider.swift @@ -6,12 +6,15 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // -class AppNotificationsProvider: AppNotificationsProviding { +import UIKit + +final class AppNotificationsProvider: AppNotificationsProviding { private let notificationCenter = NotificationCenter.default var didEnterBackgroundHandler: AppNotificationsHandler? var willEnterForegroundHandler: AppNotificationsHandler? + var willTerminateHandler: AppNotificationsHandler? // MARK: - Init @@ -27,6 +30,11 @@ class AppNotificationsProvider: AppNotificationsProviding { selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil) + notificationCenter.addObserver( + self, + selector: #selector(willTerminate), + name: UIApplication.willTerminateNotification, + object: nil) } deinit { @@ -44,4 +52,7 @@ class AppNotificationsProvider: AppNotificationsProviding { willEnterForegroundHandler?() } + @objc private func willTerminate() { + willTerminateHandler?() + } } diff --git a/Nynja/Notifications/AppNotificationsProviding/AppNotificationsProviding.swift b/Nynja/Notifications/AppNotificationsProviding/AppNotificationsProviding.swift index 4429c09a7..51509dea5 100644 --- a/Nynja/Notifications/AppNotificationsProviding/AppNotificationsProviding.swift +++ b/Nynja/Notifications/AppNotificationsProviding/AppNotificationsProviding.swift @@ -11,4 +11,5 @@ typealias AppNotificationsHandler = () -> Void protocol AppNotificationsProviding { var didEnterBackgroundHandler: AppNotificationsHandler? { get set } var willEnterForegroundHandler: AppNotificationsHandler? { get set } + var willTerminateHandler: AppNotificationsHandler? { get set } } diff --git a/Nynja/Services/ConnectionService.swift b/Nynja/Services/ConnectionService.swift index 8e63753bd..03f4e3f29 100644 --- a/Nynja/Services/ConnectionService.swift +++ b/Nynja/Services/ConnectionService.swift @@ -53,7 +53,6 @@ class ConnectionService { LogService.log(topic: .connectionState) { let result = "service: \(service) changed value from \(oldValue) to \(state), so connection state is: \n" return result + self.stateString() - } } diff --git a/Nynja/Services/MQTT/ConnectionHandlers/AppGroupConnectionHandler.swift b/Nynja/Services/MQTT/ConnectionHandlers/AppGroupConnectionHandler.swift new file mode 100644 index 000000000..c7109300c --- /dev/null +++ b/Nynja/Services/MQTT/ConnectionHandlers/AppGroupConnectionHandler.swift @@ -0,0 +1,41 @@ +// +// AppGroupConnectionHandler.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/10/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +final class AppGroupConnectionHandler: MQTTConnectionHandler { + + private let appGroupObserver = AppGroupFlagObserver(fileManager: .default, appGroup: Bundle.main.appGroupName) + + private(set) var connectionState: State = .connected + + func configure(with mqttService: MQTTService) { + do { + try appGroupObserver?.prepare() + try appGroupObserver?.removeFlagIfExists(.shareExtension) + try appGroupObserver?.observe { [weak mqttService, weak self] flags in + if flags.contains(.shareExtension) { + mqttService?.disconnect() + self?.connectionState = .disconnected + } else { + mqttService?.connect() + self?.connectionState = .connected + } + } + } catch { + LogService.log(topic: .fileSystem) { error.localizedDescription } + } + } +} + + +extension AppGroupConnectionHandler { + + enum State { + case connected + case disconnected + } +} diff --git a/Nynja/Services/MQTT/ConnectionHandlers/AppLifecycleConnectionHandler.swift b/Nynja/Services/MQTT/ConnectionHandlers/AppLifecycleConnectionHandler.swift new file mode 100644 index 000000000..83a1a43c4 --- /dev/null +++ b/Nynja/Services/MQTT/ConnectionHandlers/AppLifecycleConnectionHandler.swift @@ -0,0 +1,26 @@ +// +// AppLifecycleConnectionHandler.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/10/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +final class AppLifecycleConnectionHandler: MQTTConnectionHandler { + + private let appNotificationProvider = AppNotificationsProvider() + + func configure(with mqttService: MQTTService) { + let disconnectClosure: AppNotificationsHandler = { [weak mqttService] in + mqttService?.disconnect() + } + + appNotificationProvider.didEnterBackgroundHandler = disconnectClosure + + appNotificationProvider.willEnterForegroundHandler = { [weak mqttService] in + mqttService?.connect() + } + + appNotificationProvider.willTerminateHandler = disconnectClosure + } +} diff --git a/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift b/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift new file mode 100644 index 000000000..831da97a4 --- /dev/null +++ b/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift @@ -0,0 +1,43 @@ +// +// AutoReconnectConnectionHandler.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/10/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +final class AutoReconnectConnectionHandler: MQTTConnectionHandler { + + private var timerHandler: TimerHandler? + private let timeInterval: TimeInterval + + var shouldPerformReconnect: (() -> Bool)? + + init(timeInterval: TimeInterval) { + self.timeInterval = timeInterval + } + + func configure(with mqttService: MQTTService) { + mqttService.addSubscriber(self) + + timerHandler = TimerHandler(interval: timeInterval, repeats: true) { [weak mqttService, weak self] _ in + guard let shouldPerformReconnect = self?.shouldPerformReconnect, + shouldPerformReconnect() else { + return + } + + mqttService?.connect() + } + } +} + + +extension AutoReconnectConnectionHandler: MQTTServiceDelegate { + + func mqttServiceDidReceiveWrongServerVersion() { + timerHandler?.invalidate() + timerHandler = nil + } +} diff --git a/Nynja/Services/MQTT/ConnectionHandlers/ReachabilityConnectionHandler.swift b/Nynja/Services/MQTT/ConnectionHandlers/ReachabilityConnectionHandler.swift new file mode 100644 index 000000000..fccbcb717 --- /dev/null +++ b/Nynja/Services/MQTT/ConnectionHandlers/ReachabilityConnectionHandler.swift @@ -0,0 +1,40 @@ +// +// ReachabilityConnectionHandler.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/10/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +final class ReachabilityConnectionHandler: MQTTConnectionHandler { + + private var mqttService: MQTTService? + + func configure(with mqttService: MQTTService) { + self.mqttService = mqttService + ConnectionService.shared.addSubscriber(self) + } +} + + +extension ReachabilityConnectionHandler: ConnectionServiceDelegate { + + func connectionStatusChanged(_ sender: ConnectionService, + service: ConnectionService.Service, + oldValue: ConnectionService.ConnectionServiceState) { + + if service == .networking { + guard let networkStatus = sender.state[.networking] else { return } + switch networkStatus { + case .connected: + mqttService?.connect() + case .disconnected: + mqttService?.disconnect() + case .switched: + mqttService?.reconnect() + default: + break + } + } + } +} diff --git a/Nynja/Services/MQTT/ConnectionHandlers/TokenConnectionHandler.swift b/Nynja/Services/MQTT/ConnectionHandlers/TokenConnectionHandler.swift new file mode 100644 index 000000000..e26cd419b --- /dev/null +++ b/Nynja/Services/MQTT/ConnectionHandlers/TokenConnectionHandler.swift @@ -0,0 +1,24 @@ +// +// TokenConnectionHandler.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/10/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +final class TokenConnectionHandler: MQTTConnectionHandler { + + private var mqttService: MQTTService? + private let userInfo: UserInfo + + init(userInfo: UserInfo) { + self.userInfo = userInfo + } + + func configure(with mqttService: MQTTService) { + self.mqttService = mqttService + userInfo.tokenChanged = { [weak mqttService] _ in + mqttService?.reconnect() + } + } +} diff --git a/Nynja/Services/MQTT/Extensions/MQTTService+Helper.swift b/Nynja/Services/MQTT/Extensions/MQTTService+Helper.swift index 9480c929d..5c75532f0 100644 --- a/Nynja/Services/MQTT/Extensions/MQTTService+Helper.swift +++ b/Nynja/Services/MQTT/Extensions/MQTTService+Helper.swift @@ -10,7 +10,7 @@ import Foundation extension MQTTService { - enum Error: Swift.Error { + enum LogMessagePreparingError: Swift.Error { case messageWithoutPayload } @@ -42,7 +42,7 @@ extension MQTTService { private func prepareOutputMessage(msg: MQTTMessage, isSent: Bool) throws -> (String, BertObject) { guard let payload = msg.payload else { - throw Error.messageWithoutPayload + throw LogMessagePreparingError.messageWithoutPayload } let array = [UInt8](payload) diff --git a/Nynja/Services/MQTT/MQTTConnectionHandler.swift b/Nynja/Services/MQTT/MQTTConnectionHandler.swift new file mode 100644 index 000000000..dbf573c98 --- /dev/null +++ b/Nynja/Services/MQTT/MQTTConnectionHandler.swift @@ -0,0 +1,11 @@ +// +// MQTTConnectionHandler.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/10/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +protocol MQTTConnectionHandler { + func configure(with mqttService: MQTTService) +} diff --git a/Nynja/Services/MQTT/MQTTService.swift b/Nynja/Services/MQTT/MQTTService.swift index 76303e7cd..db7ab0d16 100644 --- a/Nynja/Services/MQTT/MQTTService.swift +++ b/Nynja/Services/MQTT/MQTTService.swift @@ -10,15 +10,13 @@ import Foundation import MQTTClient /// Performs works in the several serial background queues -final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegate, MQTTSessionDelegate { +final class MQTTService: NSObject, MQTTServiceProtocol, MQTTSessionDelegate { private var mqtt: MQTTSession! let queuePool = QueuePool() let showHandlers: Set = [] - private var timerHandler: TimerHandler? - var deviceId: String { return UIDevice.current.persistentIdentifier } @@ -27,25 +25,6 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat return .sharedInstance } - private var autoReconnectTimeInterval: TimeInterval? { - willSet { - timerHandler?.invalidate() - timerHandler = nil - if let newValue = newValue { - timerHandler = TimerHandler(interval: newValue, repeats: true) { [weak self] _ in - self?.tryReconnect() - } - } - } - } - private(set) var isBadProtocolVersion: Bool = false { - willSet { - if newValue { - autoReconnectTimeInterval = nil - } - } - } - private(set) var isConnectedSuccess: Bool = false { didSet { guard oldValue != isConnectedSuccess else { @@ -64,6 +43,29 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat } } + private let connectionHandlers: [MQTTConnectionHandler] = { + let autoReconnectConnectionHandler = AutoReconnectConnectionHandler(timeInterval: 15) + + var handlers: [MQTTConnectionHandler] = [ + AppLifecycleConnectionHandler(), + autoReconnectConnectionHandler, + ReachabilityConnectionHandler(), + TokenConnectionHandler(userInfo: ServiceFactory().makeUserInfo()) + ] + + #if !SHARE_EXTENSION + let appGroupConnectionHandler = AppGroupConnectionHandler() + handlers.append(appGroupConnectionHandler) + + autoReconnectConnectionHandler.shouldPerformReconnect = { [weak appGroupConnectionHandler] in + let connectionState = appGroupConnectionHandler?.connectionState ?? .connected + return connectionState == .connected + } + #endif + + return handlers + }() + // MARK: - Subscribers @@ -78,8 +80,13 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat private override init() { MQTTLog.setLogLevel(DDLogLevel.off) mqtt = MQTTSession() + super.init() - ConnectionService.shared.addSubscriber(self) + + mqtt.transport = makeTransport() + mqtt.delegate = self + + connectionHandlers.forEach { $0.configure(with: self) } } @@ -87,6 +94,11 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat func connect() { queuePool.processingQueue.async { [unowned self] in + let statuses: [MQTTSessionStatus] = [.connected, .connecting] + guard let mqtt = self.mqtt, !statuses.contains(mqtt.status) else { + return + } + self.setup() self.mqtt?.connect() } @@ -103,15 +115,11 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat mqtt.password = "" mqtt.cleanSessionFlag = false } - autoReconnectTimeInterval = 15 logState() } private func commonSetup() { - mqtt.delegate = nil - mqtt = MQTTSession() mqtt.delegate = self - mqtt.transport = makeTransport() mqtt.userName = "api" mqtt.keepAliveInterval = 0 mqtt.willMsg = Data() @@ -138,22 +146,15 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat return transport } - func tryReconnect() { + func reconnect() { queuePool.processingQueue.async { [unowned self] in - let states: [MQTTSessionStatus] = [.created, .closed] - if let connState = self.mqtt?.status, states.contains(connState) { - self.reconnect() + if self.mqtt.status == .connected { + self.disconnect() } + self.connect() } } - func reconnect() { - if mqtt.status == .connected { - disconnect() - } - connect() - } - func disconnect() { logState() queuePool.processingQueue.async { [unowned self] in @@ -166,7 +167,10 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat func publish(model: BaseMQTTModel) { queuePool.processingQueue.async { [weak mqtt] in - guard let mqtt = mqtt, mqtt.status == .connected else { return } + guard let mqtt = mqtt, mqtt.status == .connected else { + return + } + let finalModel: MQTTMessage = model.getMessage() mqtt.publishData(finalModel.payload, onTopic: finalModel.topic, retain: false, qos: finalModel.qos) } @@ -187,14 +191,26 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat let msg = MQTTMessage(payload: data, topic: topic) printMessage(msg: msg, isSent: true) } + - func connected(_ session: MQTTSession!) { - logState() + + func handleEvent(_ session: MQTTSession!, event eventCode: MQTTSessionEvent, error: Error!) { queuePool.processingQueue.async { [unowned self] in - self.handleAccept() + switch eventCode { + case .connected: + self.handleAccept() + case .connectionClosed: + self.isConnectedSuccess = false + case .connectionRefused: + let mqttError = MQTTSessionError(rawValue: (error as NSError).code) + if mqttError == MQTTSessionError.connackBadUsernameOrPassword { + self.handleBadUsernameOrPassword() + } + case .protocolError, .connectionClosedByBroker, .connectionError: break + } } } - + private func handleAccept() { isConnectedSuccess = true @@ -205,26 +221,6 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat #endif } - func connectionClosed(_ session: MQTTSession!) { - logState() - queuePool.processingQueue.async { [unowned self] in - self.isConnectedSuccess = false - } - } - - private func connectionRefused(_ session: MQTTSession!, error: Error!) { - queuePool.processingQueue.async { [unowned self] in - let mqttError = MQTTSessionError(rawValue: (error as NSError).code) - if mqttError == MQTTSessionError.connackBadUsernameOrPassword { - self.handleBadUsernameOrPassword() - } - } - } - - func handleEvent(_ session: MQTTSession!, event eventCode: MQTTSessionEvent, error: Error!) { - print(eventCode) - } - private func handleBadUsernameOrPassword() { if let actualToken = storageService.token, actualToken == mqtt.password { handleInvalidToken() @@ -243,13 +239,11 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat notifySubscribers { (delegate) in delegate.mqttServiceDidReceiveAuthenticationFailure(self) } - self.reconnect() #endif } private func handleBadProtocolVersion() { LogService.log(topic: .MQTT) { return "Bad protocol version" } - isBadProtocolVersion = true notifySubscribers { (delegate) in delegate.mqttServiceDidReceiveWrongServerVersion() } @@ -293,30 +287,7 @@ final class MQTTService: NSObject, MQTTServiceProtocol, ConnectionServiceDelegat private func logState() { LogService.log(topic: .MQTT) { () -> String in - return "\n----- MQTT ----- \n ClientID: \(self.mqtt.clientId ?? "") \n Password: \(self.mqtt.password ?? "") \n CleanSession: \(self.mqtt.cleanSessionFlag) \n State: \(self.mqtt.status.descript)) \n----- MQTT ----- \n " - } - } - - - // MARK: - ConnectionServiceDelegate - - func connectionStatusChanged( - _ sender: ConnectionService, - service: ConnectionService.Service, oldValue: ConnectionService.ConnectionServiceState) { - - queuePool.processingQueue.async { [unowned self] in - if service == .networking { - guard let networkStatus = sender.state[.networking] else { return } - switch networkStatus { - case .connected: - self.mqtt?.connect() - case .disconnected: - self.disconnect() - case .switched: - self.reconnect() - default: break - } - } + return "\n----- MQTT ----- \n ClientID: \(self.mqtt.clientId ?? "") \n Password: \(self.mqtt.password ?? "") \n CleanSession: \(self.mqtt.cleanSessionFlag) \n State: \(self.mqtt.status.descript) \n----- MQTT ----- \n" } } } @@ -353,3 +324,53 @@ extension MQTTSessionStatus { } } } + +extension MQTTSessionError { + var descript: String { + switch self { + case .connackBadUsernameOrPassword: + return "connackBadUsernameOrPassword" + case .connackIdentifierRejected: + return "connackIdentifierRejected" + case .connackNotAuthorized: + return "connackNotAuthorized" + case .connackReserved: + return "connackReserved" + case .connackServeUnavailable: + return "connackServeUnavailable" + case .connackUnacceptableProtocolVersion: + return "connackUnacceptableProtocolVersion" + case .connectionRefused: + return "connectionRefused" + case .droppingOutgoingMessage: + return "droppingOutgoingMessage" + case .encoderNotReady: + return "encoderNotReady" + case .illegalMessageReceived: + return "illegalMessageReceived" + case .invalidConnackReceived: + return "invalidConnackReceived" + case .noConnackReceived: + return "noConnackReceived" + } + } +} + +extension MQTTSessionEvent { + var descript: String { + switch self { + case .connected: + return "connected" + case .connectionClosed: + return "connectionClosed" + case .connectionClosedByBroker: + return "connectionClosedByBroker" + case .connectionError: + return "connectionError" + case .connectionRefused: + return "connectionRefused" + case .protocolError: + return "protocolError" + } + } +} diff --git a/Nynja/Services/MQTT/MQTTServiceProtocol.swift b/Nynja/Services/MQTT/MQTTServiceProtocol.swift index a58000f49..6f9aa25f1 100644 --- a/Nynja/Services/MQTT/MQTTServiceProtocol.swift +++ b/Nynja/Services/MQTT/MQTTServiceProtocol.swift @@ -10,11 +10,9 @@ protocol MQTTServiceProtocol { - var isBadProtocolVersion: Bool { get } var isConnectedSuccess: Bool { get } func connect() - func tryReconnect() func reconnect() func disconnect() diff --git a/Nynja/Services/ServiceFactory/ServiceFactory.swift b/Nynja/Services/ServiceFactory/ServiceFactory.swift index ee390aef5..a0b56ce22 100644 --- a/Nynja/Services/ServiceFactory/ServiceFactory.swift +++ b/Nynja/Services/ServiceFactory/ServiceFactory.swift @@ -63,7 +63,6 @@ protocol ServiceFactoryProtocol: SharedServiceFactoryProtocol { func makeReachabilityService() -> ReachabilityService - func makeUserInfo() -> UserInfo func makeDatabaseManager() -> DBManagerProtocol func makeValidatorFactory() -> ValidatorFactory @@ -234,10 +233,6 @@ final class ServiceFactory: SharedServiceFactory, ServiceFactoryProtocol { return .sharedInstance } - func makeUserInfo() -> UserInfo { - return makeStorageService() - } - func makeDatabaseManager() -> DBManagerProtocol { return makeStorageService() } diff --git a/Nynja/Services/StorageService.swift b/Nynja/Services/StorageService.swift index 4bde6971e..a4188df46 100644 --- a/Nynja/Services/StorageService.swift +++ b/Nynja/Services/StorageService.swift @@ -124,6 +124,12 @@ class StorageService { } dropUserInfo() } + + func wipeKeychain() { + isolationQueue.sync { [weak self] in + self?.keychain.clear() + } + } } #if !SHARE_EXTENSION @@ -176,6 +182,11 @@ extension StorageService: UserInfo { var tokenData: Data? { get { return userInfo.tokenData } } + + var tokenChanged: ((String?) -> Void)? { + get { return userInfo.tokenChanged } + set { userInfo.tokenChanged = newValue } + } var pushToken: String? { get { return userInfo.pushToken } diff --git a/Nynja/UserIdentifiers.swift b/Nynja/UserIdentifiers.swift new file mode 100644 index 000000000..76d175cf0 --- /dev/null +++ b/Nynja/UserIdentifiers.swift @@ -0,0 +1,21 @@ +// +// UserIdentifiers .swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/10/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +enum UserIdentifiers: String { + case token + case pushToken + + case phone + case rosterId + case clientId = "clientID" + + case wasLogined + case wasRun +} diff --git a/Nynja/UserInfo.swift b/Nynja/UserInfo.swift index c04b6179c..59c7862f0 100644 --- a/Nynja/UserInfo.swift +++ b/Nynja/UserInfo.swift @@ -8,21 +8,10 @@ import Foundation -enum UserIdentifiers: String { - case token - case pushToken - - case phone - case rosterId - case clientId = "clientID" - - case wasLogined - case wasRun -} - protocol UserInfo: class { var token: String? { get set } var tokenData: Data? { get } + var tokenChanged: ((String?) -> Void)? { get set } var pushToken: String? { get set } diff --git a/Nynja/UserInfoImpl.swift b/Nynja/UserInfoImpl.swift index 6f81d898d..529aa8c07 100644 --- a/Nynja/UserInfoImpl.swift +++ b/Nynja/UserInfoImpl.swift @@ -39,6 +39,8 @@ class UserInfoImpl: UserInfo, InitializeInjectable { return token } set { + tokenChanged?(token) + guard let token = newValue, let data = token.data(using: self.encoding) else { userDefaults?.removeObject(forKey: UserIdentifiers.token.rawValue) userDefaults?.synchronize() @@ -47,10 +49,11 @@ class UserInfoImpl: UserInfo, InitializeInjectable { set(data as NSData, forId: .token) LogService.log(topic: .userDefaults) { return "Save token: \(token)" } - MQTTService.sharedInstance.reconnect() // FIXME: I don't think it is proper place to do such thing } } + var tokenChanged: ((String?) -> Void)? + var pushToken: String? { get { return value(forId: .pushToken) } set { set(newValue, forId: .pushToken) } diff --git a/NynjaUnitTests/Services/UserInfo/UserInfoTest.swift b/NynjaUnitTests/Services/UserInfo/UserInfoTest.swift index 33f8f8054..23b0bf823 100644 --- a/NynjaUnitTests/Services/UserInfo/UserInfoTest.swift +++ b/NynjaUnitTests/Services/UserInfo/UserInfoTest.swift @@ -111,6 +111,7 @@ private extension UserInfoTest { class UserInfoMock: UserInfo { var token: String? var tokenData: Data? + var tokenChanged: ((String?) -> Void)? var pushToken: String? var phone: String? var rosterId: Int64? diff --git a/Shared/Services/Handlers/AuthHandler/AuthHandler.swift b/Shared/Services/Handlers/AuthHandler/AuthHandler.swift index ef1dd76f4..02c74e852 100644 --- a/Shared/Services/Handlers/AuthHandler/AuthHandler.swift +++ b/Shared/Services/Handlers/AuthHandler/AuthHandler.swift @@ -10,6 +10,10 @@ import Foundation final class AuthHandler: BaseHandler, StaticDelegating { + private static var storageService: StorageService { + return .sharedInstance + } + static weak var delegate: AuthHandlerDelegate? static func executeHandle(data: BertTuple) { @@ -25,7 +29,7 @@ final class AuthHandler: BaseHandler, StaticDelegating { if auth.type?.string == "update" { LogService.log(topic: .MQTT) { return "Recived new token: \(auth.token ?? "")" } if let token = auth.token { - StorageService.sharedInstance.token = token + storageService.token = token } } } diff --git a/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift b/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift index 40e54e9d2..356d44dc5 100644 --- a/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift +++ b/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift @@ -9,6 +9,7 @@ protocol SharedServiceFactoryProtocol: class { func makeMQTTService() -> MQTTService func makeStorageService() -> StorageService + func makeUserInfo() -> UserInfo func makeAmazonManager() -> AmazonManager func makeAmazonInitializer() -> AmazonInitializer func makeTypingSenderService() -> TypingSenderServiceProtocol @@ -25,6 +26,10 @@ class SharedServiceFactory: SharedServiceFactoryProtocol { return StorageService.sharedInstance } + func makeUserInfo() -> UserInfo { + return makeStorageService() + } + func makeAmazonManager() -> AmazonManager { return AmazonManager.shared } -- GitLab From 397c96847d3f0bc32c1162e162673edc23631241 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Tue, 11 Dec 2018 22:54:33 +0200 Subject: [PATCH 06/41] 0.5.5.1.RC --- Nynja-Share/Resources/Info.plist | 2 +- Nynja/Resources/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist index a76863273..3da1ee9b5 100644 --- a/Nynja-Share/Resources/Info.plist +++ b/Nynja-Share/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.5.RC + 0.5.5.1.RC Config $(Config) ModelsVersion diff --git a/Nynja/Resources/Info.plist b/Nynja/Resources/Info.plist index f49b53a29..0ca491858 100644 --- a/Nynja/Resources/Info.plist +++ b/Nynja/Resources/Info.plist @@ -23,7 +23,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.5.RC + 0.5.5.1.RC ConfServerAddress $(ConfServerAddress) ConfServerPort -- GitLab From dcf0637d4493716e0b6e5f5ab4e07e5055cca146 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Tue, 11 Dec 2018 22:54:53 +0200 Subject: [PATCH 07/41] Added XS,XSMax,XR --- Nynja/Extensions/UIDeviceExtension.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Nynja/Extensions/UIDeviceExtension.swift b/Nynja/Extensions/UIDeviceExtension.swift index 60e4be86e..f5d27a4bb 100644 --- a/Nynja/Extensions/UIDeviceExtension.swift +++ b/Nynja/Extensions/UIDeviceExtension.swift @@ -82,7 +82,11 @@ extension UIDevice { "iPhone10,1" : .iPhone8, "iPhone10,2" : .iPhone8plus, "iPhone10,3" : .iPhoneX, - "iPhone10,6" : .iPhoneX + "iPhone10,6" : .iPhoneX, + "iPhone11,2": .iPhoneXS, + "iPhone11,4": .iPhoneXSMax, + "iPhone11,6": .iPhoneXSMax, + "iPhone11,8": .iPhoneXR ] if let model = modelMap[String.init(validatingUTF8: modelCode!)!] { @@ -128,6 +132,9 @@ public enum PhoneModel : String { iPhone8 = "iPhone 8", iPhone8plus = "iPhone 8 Plus", iPhoneX = "iPhone X", + iPhoneXS = "iPhone XS", + iPhoneXSMax = "iPhone XS Max", + iPhoneXR = "iPhone XR", unrecognized = "?unrecognized?" } -- GitLab From 09d1f6d093e6f1cfd5c0e0373b2da2a613d30550 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Wed, 5 Dec 2018 17:04:41 +0200 Subject: [PATCH 08/41] Make requested changes. (#1510) --- Nynja/DB/Models/DBMessage.swift | 5 ++-- Nynja/DatabaseManager.swift | 25 +++---------------- Nynja/MigrationManager/Migration.swift | 4 +-- Nynja/MigrationManager/MigrationManager.swift | 20 ++++++++++++++- .../Migrations/RemoveP2pAndMucTables.swift | 2 +- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Nynja/DB/Models/DBMessage.swift b/Nynja/DB/Models/DBMessage.swift index 414d6098e..71380ce5e 100644 --- a/Nynja/DB/Models/DBMessage.swift +++ b/Nynja/DB/Models/DBMessage.swift @@ -358,9 +358,10 @@ final class DBMessage: Record, DBModel { static func condition(feed: Feed, messageTable: String) -> String { let feedIdCondition: String - if feed.type == .p2p { + switch feed.type { + case .p2p: feedIdCondition = conditionP2p(id: feed.id, messageTable: messageTable) - } else { + case .muc: feedIdCondition = conditionMuc(id: feed.id, messageTable: messageTable) } diff --git a/Nynja/DatabaseManager.swift b/Nynja/DatabaseManager.swift index 3ac46cf9b..c803157bb 100644 --- a/Nynja/DatabaseManager.swift +++ b/Nynja/DatabaseManager.swift @@ -18,7 +18,7 @@ final class DatabaseManager: DBManagerProtocol { private(set) var dbPool: DatabasePool? - private let migrationsProvider = MigrationsProviderImpl() + private let migrationManager = MigrationManager() private let fileManagerService = FileManagerService.sharedInstance private let folderName = "DataBases" @@ -104,34 +104,15 @@ final class DatabaseManager: DBManagerProtocol { try $0.create(in: db) } - try fillMigrationsTable(db) + try migrationManager.fillMigrationsTable(db) return .commit } } - private func fillMigrationsTable(_ database: Database) throws { - let values = migrationsProvider.migrationTitles - - guard !values.isEmpty else { - return - } - - let tableName = "grdb_migrations" - let identifierColumn = "identifier" - - if try !database.tableExists(tableName) { - try database.execute("CREATE TABLE \(tableName) (\(identifierColumn) TEXT NOT NULL PRIMARY KEY)") - - try values.forEach { (value) in - try database.execute("insert into grdb_migrations (identifier) values('\(value)')") - } - } - } - private func performMigration() throws { if let dbPool = dbPool { - try MigrationManager().migrate(dbPool) + try migrationManager.migrate(dbPool) } } diff --git a/Nynja/MigrationManager/Migration.swift b/Nynja/MigrationManager/Migration.swift index 9056538d2..2c346011a 100644 --- a/Nynja/MigrationManager/Migration.swift +++ b/Nynja/MigrationManager/Migration.swift @@ -10,7 +10,7 @@ import GRDBCipher protocol Migration { var identifier: String { get } - var isDisabledFKConstraints: Bool { get } + var isDisabledForeignKeyConstraints: Bool { get } func migrate(_ db: Database) throws } @@ -23,7 +23,7 @@ extension Migration { return "\(firstLetter)\(remainder)" } - var isDisabledFKConstraints: Bool { + var isDisabledForeignKeyConstraints: Bool { return false } } diff --git a/Nynja/MigrationManager/MigrationManager.swift b/Nynja/MigrationManager/MigrationManager.swift index 79ee1bad8..a3f98817f 100644 --- a/Nynja/MigrationManager/MigrationManager.swift +++ b/Nynja/MigrationManager/MigrationManager.swift @@ -31,12 +31,30 @@ final class MigrationManager { } } + func fillMigrationsTable(_ db: Database) throws { + let values = migrationsProvider.migrationTitles + + guard !values.isEmpty else { + return + } + + let tableName = "grdb_migrations" + let identifierColumn = "identifier" + + if try !db.tableExists(tableName) { + try db.execute("CREATE TABLE \(tableName) (\(identifierColumn) TEXT NOT NULL PRIMARY KEY)") + + try values.forEach { (value) in + try db.execute("insert into grdb_migrations (identifier) values('\(value)')") + } + } + } // MARK: - Private methods private func registerMigrations() { migrationsProvider.migrations.forEach { migration in - if migration.isDisabledFKConstraints { + if migration.isDisabledForeignKeyConstraints { migrator.registerMigrationWithDeferredForeignKeyCheck(migration.identifier, migrate: migration.migrate) } else { migrator.registerMigration(migration.identifier, migrate: migration.migrate) diff --git a/Nynja/MigrationManager/Migrations/RemoveP2pAndMucTables.swift b/Nynja/MigrationManager/Migrations/RemoveP2pAndMucTables.swift index e8fd8e39b..a08f44e69 100644 --- a/Nynja/MigrationManager/Migrations/RemoveP2pAndMucTables.swift +++ b/Nynja/MigrationManager/Migrations/RemoveP2pAndMucTables.swift @@ -10,7 +10,7 @@ import GRDBCipher final class RemoveP2pAndMucTables: Migration { - var isDisabledFKConstraints: Bool { + var isDisabledForeignKeyConstraints: Bool { return true } -- GitLab From 45d3cc4d4ced7e756eb642b32ea842fc294bb7c8 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Mon, 10 Dec 2018 17:05:46 +0200 Subject: [PATCH 09/41] Implement 'Make the QR Scanning option in camera be turned on by default'. (#1521) --- .../Services/CameraSettingsService/CameraSettingsService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nynja/Services/CameraSettingsService/CameraSettingsService.swift b/Nynja/Services/CameraSettingsService/CameraSettingsService.swift index 2b8ab4b4b..2db7c0300 100644 --- a/Nynja/Services/CameraSettingsService/CameraSettingsService.swift +++ b/Nynja/Services/CameraSettingsService/CameraSettingsService.swift @@ -90,7 +90,7 @@ extension CameraSettingsService { @discardableResult func makeDefaultSettings() -> CameraSettings { let settings = CameraSettings( - isScanQRCodes: .qrCodes(value: false), + isScanQRCodes: .qrCodes(value: true), isDrawGrid: .grid(value: false), isGeotaggingEnabled: .geotagging(value: false), photoQuality: .photoQuality(value: .high), -- GitLab From efeee4867a255b17486965b9bd0be495ce060ff9 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Wed, 12 Dec 2018 12:03:22 +0200 Subject: [PATCH 10/41] [NY-6053] Rename Security (#1524) --- Nynja.xcodeproj/project.pbxproj | 88 +++++++++---------- ...swift => ActiveSessionsItemsFactory.swift} | 8 +- Nynja/Generated/LocalizableConstants.swift | 66 +++++++------- .../Library/UI/NoInternetNavigationView.swift | 2 +- Nynja/Modules/Main/MainProtocols.swift | 4 +- .../Main/Presenter/MainPresenter.swift | 4 +- .../Main/View/MainNavigationItem.swift | 2 +- .../MainViewController+NavigateProtocol.swift | 4 +- .../Modules/Main/View/NavigateProtocol.swift | 2 +- .../Main/WireFrame/MainWireframe.swift | 4 +- .../ActiveSessionsProtocols.swift} | 24 ++--- .../ActiveSessionsInteractor.swift} | 6 +- .../Presenter/ActiveSessionsPresenter.swift} | 22 ++--- .../View/ActiveSessionsViewController.swift} | 12 +-- .../View/Cell/SessionItemCell.swift | 0 .../View/View/SessionDescView.swift | 0 .../View/View/SessionFooterView.swift | 2 +- .../View/View/SessionHeaderView.swift | 8 +- .../View/View/SessionItemView.swift | 8 +- .../View/View/SessionLineView.swift | 0 .../WireFrame/ActiveSessionsWireframe.swift} | 12 +-- Nynja/OptionsItemsFactory.swift | 8 +- Nynja/Resources/en.lproj/Localizable.strings | 33 ++++--- 23 files changed, 159 insertions(+), 160 deletions(-) rename Nynja/{SecurityItemsFactory.swift => ActiveSessionsItemsFactory.swift} (54%) rename Nynja/Modules/Settings/{Security/SecurityProtocols.swift => ActiveSessions/ActiveSessionsProtocols.swift} (61%) rename Nynja/Modules/Settings/{Security/Interactor/SecurityInteractor.swift => ActiveSessions/Interactor/ActiveSessionsInteractor.swift} (93%) rename Nynja/Modules/Settings/{Security/Presenter/SecurityPresenter.swift => ActiveSessions/Presenter/ActiveSessionsPresenter.swift} (77%) rename Nynja/Modules/Settings/{Security/View/SecurityViewController.swift => ActiveSessions/View/ActiveSessionsViewController.swift} (91%) rename Nynja/Modules/Settings/{Security => ActiveSessions}/View/Cell/SessionItemCell.swift (100%) rename Nynja/Modules/Settings/{Security => ActiveSessions}/View/View/SessionDescView.swift (100%) rename Nynja/Modules/Settings/{Security => ActiveSessions}/View/View/SessionFooterView.swift (90%) rename Nynja/Modules/Settings/{Security => ActiveSessions}/View/View/SessionHeaderView.swift (91%) rename Nynja/Modules/Settings/{Security => ActiveSessions}/View/View/SessionItemView.swift (95%) rename Nynja/Modules/Settings/{Security => ActiveSessions}/View/View/SessionLineView.swift (100%) rename Nynja/Modules/Settings/{Security/WireFrame/SecurityWireframe.swift => ActiveSessions/WireFrame/ActiveSessionsWireframe.swift} (69%) diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 246d453da..2d1162c3f 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -57,7 +57,7 @@ 00E86469204D519600844FF1 /* LanguageSettingsItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E86468204D519600844FF1 /* LanguageSettingsItemsFactory.swift */; }; 00E8646D204D788100844FF1 /* LanguageSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E8646C204D788100844FF1 /* LanguageSettingsCell.swift */; }; 00E864702052B1BE00844FF1 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 00E864722052B1BE00844FF1 /* InfoPlist.strings */; }; - 00E9824C205C1E19008BF03D /* SecurityItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9824B205C1E19008BF03D /* SecurityItemsFactory.swift */; }; + 00E9824C205C1E19008BF03D /* ActiveSessionsItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9824B205C1E19008BF03D /* ActiveSessionsItemsFactory.swift */; }; 00E9824E205C2604008BF03D /* SessionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9824D205C2604008BF03D /* SessionItemView.swift */; }; 00E98250205C2668008BF03D /* SessionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9824F205C2668008BF03D /* SessionHeaderView.swift */; }; 00E98252205C26F7008BF03D /* SessionLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E98251205C26F7008BF03D /* SessionLineView.swift */; }; @@ -440,10 +440,10 @@ 2B924FFB43474DF387A06D67 /* VideoPreviewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0193413D570F0548B2E55F /* VideoPreviewPresenter.swift */; }; 2BE42DBEDF331A9C7E6FCBE1 /* TopUpAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1F219FC7966064C555AC2A4 /* TopUpAccountViewController.swift */; }; 2C95C06FF47CF32C3B35FF4F /* TutorialInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0557EB8B6354410A900A9A7A /* TutorialInteractor.swift */; }; - 2CB54DD94DA23D7160F36472 /* SecurityWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48276F2EE408C27334B2894C /* SecurityWireframe.swift */; }; + 2CB54DD94DA23D7160F36472 /* ActiveSessionsWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48276F2EE408C27334B2894C /* ActiveSessionsWireframe.swift */; }; 2F2A5C12A7202E7834F923DC /* GroupRulesWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520422E90094C6C267AECE7E /* GroupRulesWireframe.swift */; }; 2F7C7F7837BDE6F5767A3A8C /* GroupStorageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1D5302025583482829BBF2E /* GroupStorageViewController.swift */; }; - 3219C8F242591BA17953FF33 /* SecurityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F509C0C8B9C738DBC7ABE07 /* SecurityViewController.swift */; }; + 3219C8F242591BA17953FF33 /* ActiveSessionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F509C0C8B9C738DBC7ABE07 /* ActiveSessionsViewController.swift */; }; 32868DD51F31CADF0028B260 /* ChatsListProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32868DD41F31CADF0028B260 /* ChatsListProtocols.swift */; }; 32868DDB1F31CB500028B260 /* ChatsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32868DDA1F31CB500028B260 /* ChatsListViewController.swift */; }; 32868DDD1F31CB5D0028B260 /* ChatsListPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32868DDC1F31CB5D0028B260 /* ChatsListPresenter.swift */; }; @@ -741,6 +741,10 @@ 4BBAEBCE21AE9F790089B703 /* ValidatorFactoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBAEBCD21AE9F790089B703 /* ValidatorFactoryImpl.swift */; }; 4BBAEBD021AE9F9D0089B703 /* ValidatorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBAEBCF21AE9F9D0089B703 /* ValidatorFactory.swift */; }; 4BC8B38D2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC8B38C2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift */; }; + 4BC95A7721C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; + 4BC95A7821C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; + 4BC95A7921C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; + 4BC95A7A21C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; 4BD53BF4202C8BCA00569C1A /* AVURLAsset+Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E77FBDDC1FFE828400BDB255 /* AVURLAsset+Duration.swift */; }; 4BDC7E61203492CA00BCD381 /* TopSwipable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDC7E60203492CA00BCD381 /* TopSwipable.swift */; }; 4BDC7E63203494C000BCD381 /* ScheduleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDC7E62203494C000BCD381 /* ScheduleButton.swift */; }; @@ -1203,7 +1207,7 @@ 8E23E086200614AB00A59B8C /* GroupVideosCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E23E085200614AB00A59B8C /* GroupVideosCell.swift */; }; 8E23E0882006853000A59B8C /* GroupVideosListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E23E0872006852F00A59B8C /* GroupVideosListVC.swift */; }; 8E47DBEB200EB05900E612B0 /* MapViewControllerLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E47DBEA200EB05900E612B0 /* MapViewControllerLayout.swift */; }; - 8E54E93EA25B11D417A6100E /* SecurityInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45739CD40D10B2FD5E35E0C0 /* SecurityInteractor.swift */; }; + 8E54E93EA25B11D417A6100E /* ActiveSessionsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45739CD40D10B2FD5E35E0C0 /* ActiveSessionsInteractor.swift */; }; 8E55172E200D095B00C12B5D /* UserGroupRulesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E55172D200D095B00C12B5D /* UserGroupRulesVC.swift */; }; 8E551730200D202B00C12B5D /* AdminGroupRulesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E55172F200D202B00C12B5D /* AdminGroupRulesVC.swift */; }; 8E6C4BDE1FF40B97009C8374 /* GroupFilesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E6C4BDD1FF40B97009C8374 /* GroupFilesCell.swift */; }; @@ -1902,7 +1906,7 @@ C493782D4488E45CB1D67DE4 /* CreateGroupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C2CBB5F32D209160D00F744 /* CreateGroupViewController.swift */; }; C4AE8B6EFD76A8C6ADF51422 /* TutorialProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95853EBE3A525E3069F4637 /* TutorialProtocols.swift */; }; C52453F5FF7E703BE0E7561C /* AddContactByUsernameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549BC324D11F1DDF87DAAEB7 /* AddContactByUsernameViewController.swift */; }; - C6B308C6734EFB77892832A0 /* SecurityPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762BA232B5D027BD943DFA18 /* SecurityPresenter.swift */; }; + C6B308C6734EFB77892832A0 /* ActiveSessionsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762BA232B5D027BD943DFA18 /* ActiveSessionsPresenter.swift */; }; C8C6310F83825D7385C3A6E4 /* MapProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2260535ED2762F80FA7A38 /* MapProtocols.swift */; }; C90E6A9B20558C0300D733E0 /* FileSizeFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C90E6A9A20558C0300D733E0 /* FileSizeFormatter.swift */; }; C90EE13E20246E2700FDB873 /* SelctCountryDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C90EE13D20246E2700FDB873 /* SelctCountryDelegate.swift */; }; @@ -2086,7 +2090,7 @@ E7F2CFE21F5EEF1E00806E43 /* PermissionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7F2CFE11F5EEF1E00806E43 /* PermissionManager.swift */; }; E7F68D271FA22C45009C98D1 /* EditProfileVCStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7F68D261FA22C45009C98D1 /* EditProfileVCStrings.swift */; }; E7F8F55C1F7BCA090016FDF9 /* DefaultWheelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7F8F55B1F7BCA090016FDF9 /* DefaultWheelConfiguration.swift */; }; - E8AFC57E49EED25C3F5001B7 /* SecurityProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2144825E69D46CE96C0B7D8 /* SecurityProtocols.swift */; }; + E8AFC57E49EED25C3F5001B7 /* ActiveSessionsProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2144825E69D46CE96C0B7D8 /* ActiveSessionsProtocols.swift */; }; EA7ABD1C9C761A1F58D89F8A /* AddParticipantsWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD5E1768DC8E5A8BB9BBDD96 /* AddParticipantsWireframe.swift */; }; EA9F305BF0215CAC3602D0D9 /* EditGroupPhotoPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5A57913B84E0665E3ABC0E /* EditGroupPhotoPresenter.swift */; }; F0839BACB1A52FCF846584D4 /* EditProfileWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE19B8A785E7FA17723F4D85 /* EditProfileWireframe.swift */; }; @@ -2213,9 +2217,6 @@ FB61D64D214FEC7D00CB2A1F /* FontsConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885752147F9640099B8C3 /* FontsConstants.swift */; }; FB61D64E214FEC7D00CB2A1F /* FontsConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885752147F9640099B8C3 /* FontsConstants.swift */; }; FB61D64F214FEC7E00CB2A1F /* FontsConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885752147F9640099B8C3 /* FontsConstants.swift */; }; - FB61D650214FEC8200CB2A1F /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885762147F9640099B8C3 /* LocalizableConstants.swift */; }; - FB61D651214FEC8200CB2A1F /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885762147F9640099B8C3 /* LocalizableConstants.swift */; }; - FB61D652214FEC8300CB2A1F /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885762147F9640099B8C3 /* LocalizableConstants.swift */; }; FB61D653214FEC8500CB2A1F /* AssetsConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885772147F9640099B8C3 /* AssetsConstants.swift */; }; FB61D654214FEC8600CB2A1F /* AssetsConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885772147F9640099B8C3 /* AssetsConstants.swift */; }; FB61D655214FEC8600CB2A1F /* AssetsConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885772147F9640099B8C3 /* AssetsConstants.swift */; }; @@ -2249,7 +2250,6 @@ FBCE841420E525A6003B7558 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBCE840420E525A5003B7558 /* NetworkService.swift */; }; FBCE841520E525A6003B7558 /* URLRequestConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBCE840520E525A5003B7558 /* URLRequestConvertible.swift */; }; FBD885782147F9640099B8C3 /* FontsConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885752147F9640099B8C3 /* FontsConstants.swift */; }; - FBD885792147F9640099B8C3 /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885762147F9640099B8C3 /* LocalizableConstants.swift */; }; FBD8857A2147F9640099B8C3 /* AssetsConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD885772147F9640099B8C3 /* AssetsConstants.swift */; }; FBDA34E920921079009F4FB6 /* KeyboardLayoutGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBDA34E820921079009F4FB6 /* KeyboardLayoutGuide.swift */; }; FBE3885D2118849000149721 /* AlertActionWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBE3885C2118849000149721 /* AlertActionWrapper.swift */; }; @@ -2481,7 +2481,7 @@ 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 = ""; }; - 00E9824B205C1E19008BF03D /* SecurityItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityItemsFactory.swift; sourceTree = ""; }; + 00E9824B205C1E19008BF03D /* ActiveSessionsItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionsItemsFactory.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 = ""; }; 00E98251205C26F7008BF03D /* SessionLineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionLineView.swift; sourceTree = ""; }; @@ -2861,9 +2861,9 @@ 40444524B52370D471DC9141 /* EditGroupPhotoViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditGroupPhotoViewController.swift; sourceTree = ""; }; 4177485419FF2E8F7CF8FF98 /* EditPhotoPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditPhotoPresenter.swift; sourceTree = ""; }; 4489153750EAC34408B967C0 /* MapViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = ""; }; - 45739CD40D10B2FD5E35E0C0 /* SecurityInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SecurityInteractor.swift; sourceTree = ""; }; + 45739CD40D10B2FD5E35E0C0 /* ActiveSessionsInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ActiveSessionsInteractor.swift; sourceTree = ""; }; 462440AD41D807CE8957FDD9 /* FavoritesProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FavoritesProtocols.swift; sourceTree = ""; }; - 48276F2EE408C27334B2894C /* SecurityWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SecurityWireframe.swift; sourceTree = ""; }; + 48276F2EE408C27334B2894C /* ActiveSessionsWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ActiveSessionsWireframe.swift; sourceTree = ""; }; 4948B03AEE34116DB6A7A06D /* ScheduleMessageProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ScheduleMessageProtocols.swift; sourceTree = ""; }; 498AA2E3A69072FEC336C1ED /* TopUpAccountInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TopUpAccountInteractor.swift; sourceTree = ""; }; 499373C9A81A05B77308A5F0 /* AddContactByUsernameProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddContactByUsernameProtocols.swift; sourceTree = ""; }; @@ -3042,6 +3042,7 @@ 4BBAEBCD21AE9F790089B703 /* ValidatorFactoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorFactoryImpl.swift; sourceTree = ""; }; 4BBAEBCF21AE9F9D0089B703 /* ValidatorFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorFactory.swift; sourceTree = ""; }; 4BC8B38C2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsProvidingFetchingArgs.swift; sourceTree = ""; }; + 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableConstants.swift; sourceTree = ""; }; 4BDC7E60203492CA00BCD381 /* TopSwipable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSwipable.swift; sourceTree = ""; }; 4BDC7E62203494C000BCD381 /* ScheduleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleButton.swift; sourceTree = ""; }; 4BDDAAE8218CA28300F775A7 /* HomeDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataProvider.swift; sourceTree = ""; }; @@ -3096,7 +3097,7 @@ 5D3E868EE32625048BCB13A8 /* HistoryInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = HistoryInteractor.swift; sourceTree = ""; }; 5E0CEA9921490663004B3F7A /* TypingStatusCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingStatusCache.swift; sourceTree = ""; }; 5EEA3D18EFB98D7959F993E4 /* AddParticipantsProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddParticipantsProtocols.swift; sourceTree = ""; }; - 5F509C0C8B9C738DBC7ABE07 /* SecurityViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SecurityViewController.swift; sourceTree = ""; }; + 5F509C0C8B9C738DBC7ABE07 /* ActiveSessionsViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ActiveSessionsViewController.swift; sourceTree = ""; }; 61B964D5CB991533BA5C164C /* HistoryPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = HistoryPresenter.swift; sourceTree = ""; }; 61CB12AA514912C6B8E4F670 /* Pods-Nynja.devautotests.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja.devautotests.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja/Pods-Nynja.devautotests.xcconfig"; sourceTree = ""; }; 628BB7CDB18FDAFAAB6FD17D /* EditGroupPhotoWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditGroupPhotoWireframe.swift; sourceTree = ""; }; @@ -3130,7 +3131,7 @@ 7154170549A4A686815BA4F0 /* SplashPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SplashPresenter.swift; sourceTree = ""; }; 718EF22D86A9656BB6ED89D5 /* Pods-Nynja.translate.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja.translate.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja/Pods-Nynja.translate.xcconfig"; sourceTree = ""; }; 7625A2CFF245BC8A47701724 /* AddParticipantsPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddParticipantsPresenter.swift; sourceTree = ""; }; - 762BA232B5D027BD943DFA18 /* SecurityPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SecurityPresenter.swift; sourceTree = ""; }; + 762BA232B5D027BD943DFA18 /* ActiveSessionsPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ActiveSessionsPresenter.swift; sourceTree = ""; }; 7ADCB0C891B31AF691307B4F /* Pods-Nynja.spotify.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja.spotify.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja/Pods-Nynja.spotify.xcconfig"; sourceTree = ""; }; 7C19AFE8E64821851F4112EE /* ProfileProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ProfileProtocols.swift; sourceTree = ""; }; 7C2CBB5F32D209160D00F744 /* CreateGroupViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CreateGroupViewController.swift; sourceTree = ""; }; @@ -4004,7 +4005,7 @@ B7F5051C2061252100C28FA1 /* DataAndStorageItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataAndStorageItemsFactory.swift; sourceTree = ""; }; B9BA5392968EF1C9E844C927 /* AddParticipantsInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddParticipantsInteractor.swift; sourceTree = ""; }; BD300AEDF58665DC0855B7F8 /* EditProfileViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditProfileViewController.swift; sourceTree = ""; }; - C2144825E69D46CE96C0B7D8 /* SecurityProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SecurityProtocols.swift; sourceTree = ""; }; + C2144825E69D46CE96C0B7D8 /* ActiveSessionsProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ActiveSessionsProtocols.swift; sourceTree = ""; }; C22259D46BE5732B494C4C7D /* SplashWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SplashWireframe.swift; sourceTree = ""; }; C3E427A83589B2A635F99BC0 /* EditGroupPhotoProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditGroupPhotoProtocols.swift; sourceTree = ""; }; C45F64793E8126ABF4E69F7B /* QRCodeGeneratorWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = QRCodeGeneratorWireframe.swift; sourceTree = ""; }; @@ -4331,7 +4332,6 @@ FBCE840420E525A5003B7558 /* NetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = ""; }; FBCE840520E525A5003B7558 /* URLRequestConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLRequestConvertible.swift; sourceTree = ""; }; FBD885752147F9640099B8C3 /* FontsConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontsConstants.swift; sourceTree = ""; }; - FBD885762147F9640099B8C3 /* LocalizableConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableConstants.swift; sourceTree = ""; }; FBD885772147F9640099B8C3 /* AssetsConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetsConstants.swift; sourceTree = ""; }; FBDA34E820921079009F4FB6 /* KeyboardLayoutGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardLayoutGuide.swift; sourceTree = ""; }; FBE3885C2118849000149721 /* AlertActionWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertActionWrapper.swift; sourceTree = ""; }; @@ -4737,16 +4737,16 @@ path = Presenter; sourceTree = ""; }; - 173E53A365A073A0C8A259C6 /* Security */ = { + 173E53A365A073A0C8A259C6 /* ActiveSessions */ = { isa = PBXGroup; children = ( - C2144825E69D46CE96C0B7D8 /* SecurityProtocols.swift */, + C2144825E69D46CE96C0B7D8 /* ActiveSessionsProtocols.swift */, 8C67C8504942462464406A80 /* View */, EDEF587F75F23F688953AD44 /* Presenter */, 66937A3FE42A3A7C143B03DA /* Interactor */, A31F9E0E0E7FEA81D22A0ED7 /* WireFrame */, ); - path = Security; + path = ActiveSessions; sourceTree = ""; }; 18947FBBF486D31575BC5A8F /* View */ = { @@ -6747,16 +6747,16 @@ 4B1D7DFF2029C4A900703228 /* Options */ = { isa = PBXGroup; children = ( - 00E86468204D519600844FF1 /* LanguageSettingsItemsFactory.swift */, - 4B1D7E002029C4BE00703228 /* OptionsItemsFactory.swift */, 4B1D7DFD2029C41C00703228 /* AboutItemsFactory.swift */, + 00E9824B205C1E19008BF03D /* ActiveSessionsItemsFactory.swift */, 85788C4D20443DD2003600C9 /* BuildNumberItemsFactory.swift */, - 00E9824B205C1E19008BF03D /* SecurityItemsFactory.swift */, - 85249D312045B1F800B43007 /* WheelPositionItemsFactory.swift */, - 8596CEF52048AEB8006FC65D /* ThemeItemsFactory.swift */, - 853FB06F2049B396000996C5 /* SupportItemsFactory.swift */, - 850C3024204DAC1000DB26C2 /* PrivacyListItemsFactory.swift */, + 00E86468204D519600844FF1 /* LanguageSettingsItemsFactory.swift */, 8503B52E20504904006F0593 /* NotificationSettingsItemsFactory.swift */, + 4B1D7E002029C4BE00703228 /* OptionsItemsFactory.swift */, + 850C3024204DAC1000DB26C2 /* PrivacyListItemsFactory.swift */, + 853FB06F2049B396000996C5 /* SupportItemsFactory.swift */, + 8596CEF52048AEB8006FC65D /* ThemeItemsFactory.swift */, + 85249D312045B1F800B43007 /* WheelPositionItemsFactory.swift */, ); name = Options; sourceTree = ""; @@ -7712,7 +7712,7 @@ 66937A3FE42A3A7C143B03DA /* Interactor */ = { isa = PBXGroup; children = ( - 45739CD40D10B2FD5E35E0C0 /* SecurityInteractor.swift */, + 45739CD40D10B2FD5E35E0C0 /* ActiveSessionsInteractor.swift */, ); path = Interactor; sourceTree = ""; @@ -9240,7 +9240,7 @@ children = ( C9405152204C802D00D72B04 /* DataAndStorage */, B723C62A204D86BE00884FFD /* SettingsDataAndStorage */, - 173E53A365A073A0C8A259C6 /* Security */, + 173E53A365A073A0C8A259C6 /* ActiveSessions */, B3096BAC6982290B3CB41FD6 /* LanguageSettings */, 2648C3E72069B52000863614 /* ChangeNumber */, 0062D9252062EC4100B915AC /* InviteFriends */, @@ -9592,7 +9592,7 @@ children = ( 00E98258205C2773008BF03D /* Cell */, 00E98257205C274F008BF03D /* View */, - 5F509C0C8B9C738DBC7ABE07 /* SecurityViewController.swift */, + 5F509C0C8B9C738DBC7ABE07 /* ActiveSessionsViewController.swift */, ); path = View; sourceTree = ""; @@ -9877,7 +9877,7 @@ A31F9E0E0E7FEA81D22A0ED7 /* WireFrame */ = { isa = PBXGroup; children = ( - 48276F2EE408C27334B2894C /* SecurityWireframe.swift */, + 48276F2EE408C27334B2894C /* ActiveSessionsWireframe.swift */, ); path = WireFrame; sourceTree = ""; @@ -12803,7 +12803,7 @@ EDEF587F75F23F688953AD44 /* Presenter */ = { isa = PBXGroup; children = ( - 762BA232B5D027BD943DFA18 /* SecurityPresenter.swift */, + 762BA232B5D027BD943DFA18 /* ActiveSessionsPresenter.swift */, ); path = Presenter; sourceTree = ""; @@ -13712,10 +13712,10 @@ FBD885742147F9640099B8C3 /* Generated */ = { isa = PBXGroup; children = ( + FBD885772147F9640099B8C3 /* AssetsConstants.swift */, FB61D661214FF96200CB2A1F /* ColorsConstants.swift */, FBD885752147F9640099B8C3 /* FontsConstants.swift */, - FBD885762147F9640099B8C3 /* LocalizableConstants.swift */, - FBD885772147F9640099B8C3 /* AssetsConstants.swift */, + 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */, ); path = Generated; sourceTree = ""; @@ -14820,7 +14820,6 @@ A42CE5F020692EDB000889CC /* ok2_Spec.swift in Sources */, 4B7C73FD215A553F007924DB /* LogWriter.swift in Sources */, A42CE60220692EDB000889CC /* sequenceFlow_Spec.swift in Sources */, - FB61D650214FEC8200CB2A1F /* LocalizableConstants.swift in Sources */, A42CE60020692EDB000889CC /* Message_Spec.swift in Sources */, A4F3DAA42084935400FF71C7 /* Constants.swift in Sources */, E7ED35131FB33806008B5704 /* CellModel.swift in Sources */, @@ -14856,6 +14855,7 @@ A42CE55C20692EDB000889CC /* error.swift in Sources */, 4B030F3F2195D88100F293B7 /* UserInfoImpl.swift in Sources */, 267D465C20AB4CD500D42242 /* AuthExtension.swift in Sources */, + 4BC95A7821C0077100B462AC /* LocalizableConstants.swift in Sources */, A42CE5F420692EDB000889CC /* Loc_Spec.swift in Sources */, A42CE57A20692EDB000889CC /* serviceTask.swift in Sources */, A42CE5DE20692EDB000889CC /* Star_Spec.swift in Sources */, @@ -15337,7 +15337,6 @@ F10B0E2120B4CF3800528E7A /* CameraCoordinator.swift in Sources */, 2657BE532012405600F21935 /* ImageActionItemModel.swift in Sources */, 26A0CFE12005138C006F6617 /* MemberExtension+BERT.swift in Sources */, - FBD885792147F9640099B8C3 /* LocalizableConstants.swift in Sources */, 2652D6181FA85B28005E62C7 /* ImageSelector.swift in Sources */, 26DE8D9120FE1AF500C41096 /* ChatCellFooterView.swift in Sources */, 6D485DE51F0AD96D00E12FB1 /* Localizable.swift in Sources */, @@ -15406,7 +15405,7 @@ 4B02130220372C5700650298 /* OtherItemView.swift in Sources */, 853801282052CCAD002C6960 /* SoundCellModel.swift in Sources */, A458FABB20EB87BF0075D55E /* ActionContainerContent.swift in Sources */, - 00E9824C205C1E19008BF03D /* SecurityItemsFactory.swift in Sources */, + 00E9824C205C1E19008BF03D /* ActiveSessionsItemsFactory.swift in Sources */, 26342CA920ECBAEF00D2196B /* TranscribeNetworkClient.swift in Sources */, 852003F620D4194A007C0036 /* DBRecentSticker.swift in Sources */, 267BE2831FDE905D00C47E18 /* SettingsProtocols.swift in Sources */, @@ -16773,6 +16772,7 @@ 3362A56D731AC1411C02D037 /* MyGroupAliasWireframe.swift in Sources */, 260313A020A0A4BA009AC66D /* SwitchableActionCell.swift in Sources */, 4B055C37219C313A001FE077 /* FileDownloaderFactory.swift in Sources */, + 4BC95A7721C0077100B462AC /* LocalizableConstants.swift in Sources */, 8ED0F3D11FBC5CF2004916AB /* GroupsListViewController.swift in Sources */, BBF46945EB64E07C58817ACA /* EditGroupNameProtocols.swift in Sources */, 8520040020D466CE007C0036 /* StickerPack_Spec.swift in Sources */, @@ -16992,19 +16992,19 @@ BC1BA70218B40F3F64841848 /* LanguageSettingsInteractor.swift in Sources */, F922EF38E4C1662D54CE533D /* LanguageSettingsWireframe.swift in Sources */, 26A421CF217E541800120542 /* SnackBarLayoutGuide.swift in Sources */, - E8AFC57E49EED25C3F5001B7 /* SecurityProtocols.swift in Sources */, - 3219C8F242591BA17953FF33 /* SecurityViewController.swift in Sources */, + E8AFC57E49EED25C3F5001B7 /* ActiveSessionsProtocols.swift in Sources */, + 3219C8F242591BA17953FF33 /* ActiveSessionsViewController.swift in Sources */, A45F110720B4218D00F45004 /* ChatConfiguration.swift in Sources */, 8580BAED20BD9A7100239D9D /* LinkLongPressGestureRecognizer.swift in Sources */, 4BF090C521635E8600DCCA5C /* Message+LinkedId.swift in Sources */, - C6B308C6734EFB77892832A0 /* SecurityPresenter.swift in Sources */, + C6B308C6734EFB77892832A0 /* ActiveSessionsPresenter.swift in Sources */, A42D52B4206A53AA00EEB952 /* ok_Spec.swift in Sources */, B74BAFFD21076AFA0049CD27 /* Sector.swift in Sources */, - 8E54E93EA25B11D417A6100E /* SecurityInteractor.swift in Sources */, + 8E54E93EA25B11D417A6100E /* ActiveSessionsInteractor.swift in Sources */, A43B259420AB1DFA00FF8107 /* InputBar.swift in Sources */, F11DF06820BD996200F3E005 /* NavigationProtocol.swift in Sources */, 855C9FE62125B4C0000E3429 /* MessageHandlerSubscriber.swift in Sources */, - 2CB54DD94DA23D7160F36472 /* SecurityWireframe.swift in Sources */, + 2CB54DD94DA23D7160F36472 /* ActiveSessionsWireframe.swift in Sources */, 26EA201520BECDCA00FBB9CA /* ConversationLanguageSettingService.swift in Sources */, F11786ED20AC383D007A9A1B /* CollapsedView.swift in Sources */, F11786C920A8E4FD007A9A1B /* CameraVideoPreviewProtocols.swift in Sources */, @@ -17050,13 +17050,13 @@ 4B8FC31C2163CD8C00602D6B /* ChatCellModel.swift in Sources */, 2611CEF92182090900FFD4DD /* LogWriterProtocol.swift in Sources */, 85458CE4212D731300BA8814 /* MessageIdentifiers.swift in Sources */, - FB61D651214FEC8200CB2A1F /* LocalizableConstants.swift in Sources */, A4330A562109D60D0060BD93 /* QueryFactoryProtocol.swift in Sources */, 85458CE6212D73CF00BA8814 /* P2pExtension.swift in Sources */, A4330A582109D6130060BD93 /* QueryFactory.swift in Sources */, 4B8FC31E2163D98F00602D6B /* CollectionsExtensions.swift in Sources */, A49EE6DC210B257000B700B1 /* UserInfo.swift in Sources */, A49EE6D8210B110C00B700B1 /* Service.swift in Sources */, + 4BC95A7921C0077100B462AC /* LocalizableConstants.swift in Sources */, 4BF090C921635F4300DCCA5C /* Message+LocalStatus.swift in Sources */, A4AB8E582105F47C005F9B0C /* InputsCachePolicyTest.swift in Sources */, FB816EF920B5B8A700093DCD /* Desc.swift in Sources */, @@ -17096,6 +17096,7 @@ files = ( 4B348FD8216366AE00CCB0E3 /* LogServiceTopic.swift in Sources */, 2611CEFA2182090900FFD4DD /* LogWriterProtocol.swift in Sources */, + 4BC95A7A21C0077100B462AC /* LocalizableConstants.swift in Sources */, FE21ACB92113AB3C006010A0 /* KeychainServiceTest.swift in Sources */, FE21ACBC2113AB92006010A0 /* QueryFactoryProtocol.swift in Sources */, 4B348FD7216366A900CCB0E3 /* LogServiceProtocol.swift in Sources */, @@ -17103,7 +17104,6 @@ FE21ACBA2113AB4B006010A0 /* KeychainService.swift in Sources */, 4BF2C3E92189B49500E59F6C /* Localizable.swift in Sources */, 85458CDC212D6FFF00BA8814 /* String+Split.swift in Sources */, - FB61D652214FEC8300CB2A1F /* LocalizableConstants.swift in Sources */, 85458CE5212D731300BA8814 /* MessageIdentifiers.swift in Sources */, FB61D64F214FEC7E00CB2A1F /* FontsConstants.swift in Sources */, FB61D665214FF96200CB2A1F /* ColorsConstants.swift in Sources */, diff --git a/Nynja/SecurityItemsFactory.swift b/Nynja/ActiveSessionsItemsFactory.swift similarity index 54% rename from Nynja/SecurityItemsFactory.swift rename to Nynja/ActiveSessionsItemsFactory.swift index f94137083..5d44515bd 100644 --- a/Nynja/SecurityItemsFactory.swift +++ b/Nynja/ActiveSessionsItemsFactory.swift @@ -1,17 +1,17 @@ // -// SessionsItemsFactory.swift +// ActiveSessionsItemsFactory.swift // Nynja // // Created by Michael Katkov on 3/16/18. // Copyright © 2018 TecSynt Solutions. All rights reserved. // -class SecurityItemsFactory: OptionsItemsFactory { +class ActiveSessionsItemsFactory: OptionsItemsFactory { // MARK: - Second lvl - override var security: ImageActionItemModel { - let item = super.security + override var activeSessions: ImageActionItemModel { + let item = super.activeSessions item.state = .highlighted return item } diff --git a/Nynja/Generated/LocalizableConstants.swift b/Nynja/Generated/LocalizableConstants.swift index 2066e5d6e..ed2c0ac9b 100644 --- a/Nynja/Generated/LocalizableConstants.swift +++ b/Nynja/Generated/LocalizableConstants.swift @@ -20,6 +20,36 @@ internal extension String { static var actions: String { return localizable.tr("Localizable", "Actions") } /// active static var active: String { return localizable.tr("Localizable", "active") } + /// active + static var activeSessionsActive: String { return localizable.tr("Localizable", "active_sessions_active") } + /// Current session + static var activeSessionsCurrentSession: String { return localizable.tr("Localizable", "active_sessions_current_session") } + /// Logs out all devices except for this one. + static var activeSessionsLogsOutAllDevices: String { return localizable.tr("Localizable", "active_sessions_logs_out_all_devices") } + /// No + static var activeSessionsNo: String { return localizable.tr("Localizable", "active_sessions_no") } + /// No Data + static var activeSessionsNoData: String { return localizable.tr("Localizable", "active_sessions_no_data") } + /// Please check Internet connection and try again. + static var activeSessionsNoInternetConnection: String { return localizable.tr("Localizable", "active_sessions_no_internet_connection") } + /// NYNJA + static var activeSessionsNynja: String { return localizable.tr("Localizable", "active_sessions_nynja") } + /// Tap on a session to terminate. + static var activeSessionsTapOnASession: String { return localizable.tr("Localizable", "active_sessions_tap_on_a_session") } + /// Terminate this session? + static var activeSessionsTerminate: String { return localizable.tr("Localizable", "active_sessions_terminate") } + /// Terminate All Other Sessions + static var activeSessionsTerminateAllOtherSessions: String { return localizable.tr("Localizable", "active_sessions_terminate_all_other_sessions") } + /// Active sessions + static var activeSessionsTitle: String { return localizable.tr("Localizable", "active_sessions_title") } + /// Undefined + static var activeSessionsUndefined: String { return localizable.tr("Localizable", "active_sessions_undefined") } + /// Waiting for Network... + static var activeSessionsWaitingForNetwork: String { return localizable.tr("Localizable", "active_sessions_waiting_for_network") } + /// Yes + static var activeSessionsYes: String { return localizable.tr("Localizable", "active_sessions_yes") } + /// Are you sure you want to terminate all other sessions? + static var activeSessionsYouSure: String { return localizable.tr("Localizable", "active_sessions_you_sure") } /// Can't add conference member static var addConferenceMemberFailed: String { return localizable.tr("Localizable", "Add_conference_member_failed") } /// Add to contacts @@ -958,38 +988,6 @@ internal extension String { static var searchInterpreter: String { return localizable.tr("Localizable", "search_interpreter") } /// seconds static var seconds: String { return localizable.tr("Localizable", "seconds") } - /// active - static var securityActive: String { return localizable.tr("Localizable", "security_active") } - /// Active sessions - static var securityActiveSessions: String { return localizable.tr("Localizable", "security_active_sessions") } - /// Current session - static var securityCurrentSession: String { return localizable.tr("Localizable", "security_current_session") } - /// Logs out all devices except for this one. - static var securityLogsOutAllDevices: String { return localizable.tr("Localizable", "security_logs_out_all_devices") } - /// No - static var securityNo: String { return localizable.tr("Localizable", "security_no") } - /// No Data - static var securityNoData: String { return localizable.tr("Localizable", "security_no_data") } - /// Please check Internet connection and try again. - static var securityNoInternetConnection: String { return localizable.tr("Localizable", "security_no_internet_connection") } - /// NYNJA - static var securityNynja: String { return localizable.tr("Localizable", "security_nynja") } - /// Tap on a session to terminate. - static var securityTapOnASession: String { return localizable.tr("Localizable", "security_tap_on_a_session") } - /// Terminate this session? - static var securityTerminate: String { return localizable.tr("Localizable", "security_terminate") } - /// Terminate All Other Sessions - static var securityTerminateAllOtherSessions: String { return localizable.tr("Localizable", "security_terminate_all_other_sessions") } - /// SECURITY - static var securityTitle: String { return localizable.tr("Localizable", "security_title") } - /// Undefined - static var securityUndefined: String { return localizable.tr("Localizable", "security_undefined") } - /// Waiting for Network... - static var securityWaitingForNetwork: String { return localizable.tr("Localizable", "security_waiting_for_network") } - /// Yes - static var securityYes: String { return localizable.tr("Localizable", "security_yes") } - /// Are you sure you want to terminate all other sessions? - static var securityYouSure: String { return localizable.tr("Localizable", "security_you_sure") } /// Select Country static var selectCountryTitle: String { return localizable.tr("Localizable", "select_country_title") } /// Send as File @@ -1324,6 +1322,8 @@ internal extension String { static var wheelPositionTitle: String { return localizable.tr("Localizable", "wheel position title") } /// Right hand static var wheelRightHand: String { return localizable.tr("Localizable", "wheel right hand") } + /// Active sessions + static var wheelActiveSessions: String { return localizable.tr("Localizable", "wheel_active_sessions") } /// Build number static var wheelBuildNumber: String { return localizable.tr("Localizable", "wheel_build_number") } /// Data and Storage @@ -1462,8 +1462,6 @@ internal extension String { static var wheelPosition: String { return localizable.tr("Localizable", "wheel_position") } /// Privacy static var wheelPrivacy: String { return localizable.tr("Localizable", "wheel_privacy") } - /// Security - static var wheelSecurity: String { return localizable.tr("Localizable", "wheel_security") } /// Support static var wheelSupport: String { return localizable.tr("Localizable", "wheel_support") } /// Theme diff --git a/Nynja/Library/UI/NoInternetNavigationView.swift b/Nynja/Library/UI/NoInternetNavigationView.swift index 2f112c9dc..60bcaf1e8 100644 --- a/Nynja/Library/UI/NoInternetNavigationView.swift +++ b/Nynja/Library/UI/NoInternetNavigationView.swift @@ -24,7 +24,7 @@ class NoInternetNavigationView: UIView { func setUp() { backgroundColor = UIColor.nynja.mainRed - label.text = String.localizable.securityWaitingForNetwork + label.text = String.localizable.activeSessionsWaitingForNetwork } struct Constraints { diff --git a/Nynja/Modules/Main/MainProtocols.swift b/Nynja/Modules/Main/MainProtocols.swift index 66b64a7a2..45bcb168e 100644 --- a/Nynja/Modules/Main/MainProtocols.swift +++ b/Nynja/Modules/Main/MainProtocols.swift @@ -62,7 +62,7 @@ protocol MainWireFrameProtocol: class { func showBuildNumber() func showChangeNumber() func showChangeNumberStep2() - func showSecuritySettings() + func showActiveSessions() func showSupport() func showLanguageSettings() func showThemePicker() @@ -174,7 +174,7 @@ protocol MainPresenterProtocol: BasePresenterProtocol { func showWheelPositionPicker() func showBuildNumber() func showChangeNumber() - func showSecuritySettings() + func showActiveSessions() func showSupport() func showThemePicker() func showPrivacy() diff --git a/Nynja/Modules/Main/Presenter/MainPresenter.swift b/Nynja/Modules/Main/Presenter/MainPresenter.swift index f934d3da6..29f6f801d 100644 --- a/Nynja/Modules/Main/Presenter/MainPresenter.swift +++ b/Nynja/Modules/Main/Presenter/MainPresenter.swift @@ -203,8 +203,8 @@ final class MainPresenter: BasePresenter, MainPresenterProtocol, MainInteractorO self.wireFrame.showThemePicker() } - func showSecuritySettings() { - self.wireFrame.showSecuritySettings() + func showActiveSessions() { + self.wireFrame.showActiveSessions() } func showSupport() { diff --git a/Nynja/Modules/Main/View/MainNavigationItem.swift b/Nynja/Modules/Main/View/MainNavigationItem.swift index 7fd4c3064..7b5d4e7f7 100644 --- a/Nynja/Modules/Main/View/MainNavigationItem.swift +++ b/Nynja/Modules/Main/View/MainNavigationItem.swift @@ -93,7 +93,7 @@ enum MainNavigationItem: String { case language = "wheel_item_language" case theme = "wheel_theme" case dataAndStorage = "wheel_data_and_storage" - case security = "wheel_security" + case activeSessions = "wheel_active_sessions" case privacy = "wheel_privacy" case logOut = "wheel_item_logOut" case about = "wheel_item_about" diff --git a/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift b/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift index 23e4a8d39..39327dbaf 100644 --- a/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift +++ b/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift @@ -287,8 +287,8 @@ extension MainViewController: NavigateProtocol { closeWheel(indexPath: indexPath) } - func showSecuritySettings(indexPath: IndexPath?) { - presenter.showSecuritySettings() + func showActiveSessions(indexPath: IndexPath?) { + presenter.showActiveSessions() closeWheel(indexPath: indexPath) } diff --git a/Nynja/Modules/Main/View/NavigateProtocol.swift b/Nynja/Modules/Main/View/NavigateProtocol.swift index 8b2368203..779032165 100644 --- a/Nynja/Modules/Main/View/NavigateProtocol.swift +++ b/Nynja/Modules/Main/View/NavigateProtocol.swift @@ -93,7 +93,7 @@ protocol SecondLevelNavigateProtocol: class { func showPrivacy(indexPath: IndexPath?) func logout(indexPath: IndexPath?) func showAbout(indexPath: IndexPath?) - func showSecuritySettings(indexPath: IndexPath?) + func showActiveSessions(indexPath: IndexPath?) func deleteAccount(indexPath: IndexPath?) func showChangeNumber(indexPath: IndexPath?) func dataAndStorage(indexPath: IndexPath?) diff --git a/Nynja/Modules/Main/WireFrame/MainWireframe.swift b/Nynja/Modules/Main/WireFrame/MainWireframe.swift index cfa2d7494..e4c29281a 100644 --- a/Nynja/Modules/Main/WireFrame/MainWireframe.swift +++ b/Nynja/Modules/Main/WireFrame/MainWireframe.swift @@ -438,9 +438,9 @@ final class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelega ChangeNumberStep2WireFrame().presentChangeNumberStep2(navigation: nav) } - func showSecuritySettings() { + func showActiveSessions() { if let navigation = contentNavigation { - SecurityWireFrame().presentSecurity(navigation: navigation, mainWireFrame: self) + ActiveSessionsWireFrame().presentActiveSessions(navigation: navigation, mainWireFrame: self) } } diff --git a/Nynja/Modules/Settings/Security/SecurityProtocols.swift b/Nynja/Modules/Settings/ActiveSessions/ActiveSessionsProtocols.swift similarity index 61% rename from Nynja/Modules/Settings/Security/SecurityProtocols.swift rename to Nynja/Modules/Settings/ActiveSessions/ActiveSessionsProtocols.swift index d26d6ed52..aa89ef0a8 100644 --- a/Nynja/Modules/Settings/Security/SecurityProtocols.swift +++ b/Nynja/Modules/Settings/ActiveSessions/ActiveSessionsProtocols.swift @@ -1,5 +1,5 @@ // -// SecuritySecurityProtocols.swift +// ActiveSessionsProtocols.swift // Nynja // // Created by Michael Katkov on 16/03/2018. @@ -8,9 +8,9 @@ import UIKit -protocol SecurityWireFrameProtocol: class { +protocol ActiveSessionsWireFrameProtocol: class { - func presentSecurity(navigation: UINavigationController, mainWireFrame: MainWireFrameProtocol) + func presentActiveSessions(navigation: UINavigationController, mainWireFrame: MainWireFrameProtocol) /** * Add here your methods for communication PRESENTER -> WIREFRAME @@ -19,9 +19,9 @@ protocol SecurityWireFrameProtocol: class { func hideNoInternetView() } -protocol SecurityViewProtocol: class { +protocol ActiveSessionsViewProtocol: class { - var presenter: SecurityPresenterProtocol! { get set } + var presenter: ActiveSessionsPresenterProtocol! { get set } /** * Add here your methods for communication PRESENTER -> VIEW @@ -29,11 +29,11 @@ protocol SecurityViewProtocol: class { func updateUI() } -protocol SecurityPresenterProtocol: BasePresenterProtocol { +protocol ActiveSessionsPresenterProtocol: BasePresenterProtocol { - var view: SecurityViewProtocol! { get set } - var interactor: SecurityInteractorInputProtocol! { get set } - var wireFrame: SecurityWireFrameProtocol! { get set } + var view: ActiveSessionsViewProtocol! { get set } + var interactor: ActiveSessionsInteractorInputProtocol! { get set } + var wireFrame: ActiveSessionsWireFrameProtocol! { get set } /** * Add here your methods for communication VIEW -> PRESENTER @@ -47,7 +47,7 @@ protocol SecurityPresenterProtocol: BasePresenterProtocol { func wasHidden() } -protocol SecurityInteractorOutputProtocol: class { +protocol ActiveSessionsInteractorOutputProtocol: class { /** * Add here your methods for communication INTERACTOR -> PRESENTER @@ -57,9 +57,9 @@ protocol SecurityInteractorOutputProtocol: class { } -protocol SecurityInteractorInputProtocol: class { +protocol ActiveSessionsInteractorInputProtocol: class { - var presenter: SecurityInteractorOutputProtocol! { get set } + var presenter: ActiveSessionsInteractorOutputProtocol! { get set } /** * Add here your methods for communication PRESENTER -> INTERACTOR diff --git a/Nynja/Modules/Settings/Security/Interactor/SecurityInteractor.swift b/Nynja/Modules/Settings/ActiveSessions/Interactor/ActiveSessionsInteractor.swift similarity index 93% rename from Nynja/Modules/Settings/Security/Interactor/SecurityInteractor.swift rename to Nynja/Modules/Settings/ActiveSessions/Interactor/ActiveSessionsInteractor.swift index a1b34aa39..ad11b2d1b 100644 --- a/Nynja/Modules/Settings/Security/Interactor/SecurityInteractor.swift +++ b/Nynja/Modules/Settings/ActiveSessions/Interactor/ActiveSessionsInteractor.swift @@ -1,14 +1,14 @@ // -// SecuritySecurityInteractor.swift +// ActiveSessionsInteractor.swift // Nynja // // Created by Michael Katkov on 16/03/2018. // Copyright © 2018 Michael Katkov. All rights reserved. // -class SecurityInteractor: SecurityInteractorInputProtocol, AuthHandlerDelegate, IoHandlerDelegate, ConnectionServiceDelegate { +final class ActiveSessionsInteractor: ActiveSessionsInteractorInputProtocol, AuthHandlerDelegate, IoHandlerDelegate, ConnectionServiceDelegate { - weak var presenter: SecurityInteractorOutputProtocol! + weak var presenter: ActiveSessionsInteractorOutputProtocol! private var items : [Auth] = [Auth]() private var current : Auth? diff --git a/Nynja/Modules/Settings/Security/Presenter/SecurityPresenter.swift b/Nynja/Modules/Settings/ActiveSessions/Presenter/ActiveSessionsPresenter.swift similarity index 77% rename from Nynja/Modules/Settings/Security/Presenter/SecurityPresenter.swift rename to Nynja/Modules/Settings/ActiveSessions/Presenter/ActiveSessionsPresenter.swift index a918dec5b..69784a8bb 100644 --- a/Nynja/Modules/Settings/Security/Presenter/SecurityPresenter.swift +++ b/Nynja/Modules/Settings/ActiveSessions/Presenter/ActiveSessionsPresenter.swift @@ -1,20 +1,20 @@ // -// SecuritySecurityPresenter.swift +// ActiveSessionsPresenter.swift // Nynja // // Created by Michael Katkov on 16/03/2018. // Copyright © 2018 Michael Katkov. All rights reserved. // -class SecurityPresenter: BasePresenter, SecurityPresenterProtocol, SecurityInteractorOutputProtocol { +final class ActiveSessionsPresenter: BasePresenter, ActiveSessionsPresenterProtocol, ActiveSessionsInteractorOutputProtocol { override var itemsFactory: WCItemsFactory? { - return SecurityItemsFactory() + return ActiveSessionsItemsFactory() } - weak var view: SecurityViewProtocol! - var interactor: SecurityInteractorInputProtocol! - var wireFrame: SecurityWireFrameProtocol! + weak var view: ActiveSessionsViewProtocol! + var interactor: ActiveSessionsInteractorInputProtocol! + var wireFrame: ActiveSessionsWireFrameProtocol! //MARK: - SecurityPresenterProtocol func getAllSessions() { @@ -63,8 +63,8 @@ class SecurityPresenter: BasePresenter, SecurityPresenterProtocol, SecurityInter //MARK: - Private func askAndDeleteAllSessions() { - AlertManager.sharedInstance.showAlertWithTwoActions(title: "", message: String.localizable.securityYouSure, - firstActionTitle: String.localizable.securityNo, secondActionTitle: String.localizable.securityYes, + AlertManager.sharedInstance.showAlertWithTwoActions(title: "", message: String.localizable.activeSessionsYouSure, + firstActionTitle: String.localizable.activeSessionsNo, secondActionTitle: String.localizable.activeSessionsYes, firstAction: { () in }, secondAction: { () in self.interactor.deleteAllSessions() @@ -72,8 +72,8 @@ class SecurityPresenter: BasePresenter, SecurityPresenterProtocol, SecurityInter } func askAndDeleteSession(clientId: String) { - AlertManager.sharedInstance.showAlertWithTwoActions(title: "", message: String.localizable.securityTerminate, - firstActionTitle: String.localizable.securityNo, secondActionTitle: String.localizable.securityYes, + AlertManager.sharedInstance.showAlertWithTwoActions(title: "", message: String.localizable.activeSessionsTerminate, + firstActionTitle: String.localizable.activeSessionsNo, secondActionTitle: String.localizable.activeSessionsYes, firstAction: { () in }, secondAction: { () in self.interactor.deleteSession(clientId: clientId) @@ -82,7 +82,7 @@ class SecurityPresenter: BasePresenter, SecurityPresenterProtocol, SecurityInter //MARK: - Private private func showNoInternetAlert() { - AlertManager.sharedInstance.showAlertOk(message: String.localizable.securityNoInternetConnection) + AlertManager.sharedInstance.showAlertOk(message: String.localizable.activeSessionsNoInternetConnection) } private func makeRequireInternetAction(action: () -> ()) { diff --git a/Nynja/Modules/Settings/Security/View/SecurityViewController.swift b/Nynja/Modules/Settings/ActiveSessions/View/ActiveSessionsViewController.swift similarity index 91% rename from Nynja/Modules/Settings/Security/View/SecurityViewController.swift rename to Nynja/Modules/Settings/ActiveSessions/View/ActiveSessionsViewController.swift index dbae2a513..911dadf64 100644 --- a/Nynja/Modules/Settings/Security/View/SecurityViewController.swift +++ b/Nynja/Modules/Settings/ActiveSessions/View/ActiveSessionsViewController.swift @@ -1,5 +1,5 @@ // -// SecuritySecurityViewController.swift +// ActiveSessionsViewController.swift // Nynja // // Created by Michael Katkov on 16/03/2018. @@ -8,9 +8,9 @@ import UIKit -class SecurityViewController: BaseVC, SecurityViewProtocol { +final class ActiveSessionsViewController: BaseVC, ActiveSessionsViewProtocol { - var presenter: SecurityPresenterProtocol! { + var presenter: ActiveSessionsPresenterProtocol! { didSet { _presenter = presenter } @@ -49,7 +49,7 @@ class SecurityViewController: BaseVC, SecurityViewProtocol { override func viewDidLoad() { super.viewDidLoad() presenter.wasShown() - self.screenTitle = String.localizable.securityTitle + self.screenTitle = String.localizable.activeSessionsTitle.uppercased() shouldShowSeparator = true } @@ -96,8 +96,10 @@ class SecurityViewController: BaseVC, SecurityViewProtocol { } } + // MARK: - Table View -extension SecurityViewController: UITableViewDataSource, UITableViewDelegate { + +extension ActiveSessionsViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return presenter.getItems().count diff --git a/Nynja/Modules/Settings/Security/View/Cell/SessionItemCell.swift b/Nynja/Modules/Settings/ActiveSessions/View/Cell/SessionItemCell.swift similarity index 100% rename from Nynja/Modules/Settings/Security/View/Cell/SessionItemCell.swift rename to Nynja/Modules/Settings/ActiveSessions/View/Cell/SessionItemCell.swift diff --git a/Nynja/Modules/Settings/Security/View/View/SessionDescView.swift b/Nynja/Modules/Settings/ActiveSessions/View/View/SessionDescView.swift similarity index 100% rename from Nynja/Modules/Settings/Security/View/View/SessionDescView.swift rename to Nynja/Modules/Settings/ActiveSessions/View/View/SessionDescView.swift diff --git a/Nynja/Modules/Settings/Security/View/View/SessionFooterView.swift b/Nynja/Modules/Settings/ActiveSessions/View/View/SessionFooterView.swift similarity index 90% rename from Nynja/Modules/Settings/Security/View/View/SessionFooterView.swift rename to Nynja/Modules/Settings/ActiveSessions/View/View/SessionFooterView.swift index 9711c67fc..d0a163e58 100644 --- a/Nynja/Modules/Settings/Security/View/View/SessionFooterView.swift +++ b/Nynja/Modules/Settings/ActiveSessions/View/View/SessionFooterView.swift @@ -27,6 +27,6 @@ class SessionFooterView : UIView { } func setUp() { - descView.descLabel.text = String.localizable.securityTapOnASession + descView.descLabel.text = String.localizable.activeSessionsTapOnASession } } diff --git a/Nynja/Modules/Settings/Security/View/View/SessionHeaderView.swift b/Nynja/Modules/Settings/ActiveSessions/View/View/SessionHeaderView.swift similarity index 91% rename from Nynja/Modules/Settings/Security/View/View/SessionHeaderView.swift rename to Nynja/Modules/Settings/ActiveSessions/View/View/SessionHeaderView.swift index f2c2582a0..b51280a7d 100644 --- a/Nynja/Modules/Settings/Security/View/View/SessionHeaderView.swift +++ b/Nynja/Modules/Settings/ActiveSessions/View/View/SessionHeaderView.swift @@ -96,12 +96,12 @@ class SessionHeaderView : UIView { func setUp(auth: Auth, isCurrent: Bool) { topTitleView.backgroundColor = UIColor.nynja.black.withAlphaComponent(0.1) - topTitleView.titleLabel.text = String.localizable.securityCurrentSession + topTitleView.titleLabel.text = String.localizable.activeSessionsCurrentSession activeSessionView.setItem(item: auth, isCurrent: isCurrent) - terminateButton.text = String.localizable.securityTerminateAllOtherSessions + terminateButton.text = String.localizable.activeSessionsTerminateAllOtherSessions separatorView.backgroundColor = UIColor.nynja.backgroundGray - descView.descLabel.text = String.localizable.securityLogsOutAllDevices + descView.descLabel.text = String.localizable.activeSessionsLogsOutAllDevices bottomTitleView.backgroundColor = UIColor.nynja.black.withAlphaComponent(0.1) - bottomTitleView.titleLabel.text = String.localizable.securityActiveSessions + bottomTitleView.titleLabel.text = String.localizable.activeSessionsTitle } } diff --git a/Nynja/Modules/Settings/Security/View/View/SessionItemView.swift b/Nynja/Modules/Settings/ActiveSessions/View/View/SessionItemView.swift similarity index 95% rename from Nynja/Modules/Settings/Security/View/View/SessionItemView.swift rename to Nynja/Modules/Settings/ActiveSessions/View/View/SessionItemView.swift index 754869203..dc93d3b0b 100644 --- a/Nynja/Modules/Settings/Security/View/View/SessionItemView.swift +++ b/Nynja/Modules/Settings/ActiveSessions/View/View/SessionItemView.swift @@ -77,14 +77,14 @@ class SessionItemView : UIView { setSubTitle(item: item) setDesc(item: item) if isCurrent { - dateLabel.text = String.localizable.securityActive + dateLabel.text = String.localizable.activeSessionsActive } else if let online = item.last_online { setDate(online) } } func setTitle(item: Auth) { - var title = String.localizable.securityNynja + var title = String.localizable.activeSessionsNynja if let osVersion = item.osValue, let os = osVersion.split(separator: " ").first { title = title + " " + os } @@ -102,7 +102,7 @@ class SessionItemView : UIView { if let osVersion = item.osValue { subTitle = subTitle + ", " + osVersion } - subTitleLabel.text = subTitle.isEmpty ? String.localizable.securityNoData : subTitle + subTitleLabel.text = subTitle.isEmpty ? String.localizable.activeSessionsNoData : subTitle } func setDesc(item: Auth) { @@ -116,7 +116,7 @@ class SessionItemView : UIView { if let country = item.country { desc = desc + ", " + country } - descLabel.text = desc.isEmpty ? String.localizable.securityNoData : desc + descLabel.text = desc.isEmpty ? String.localizable.activeSessionsNoData : desc } func setDate(_ lastOnline: Int64) { diff --git a/Nynja/Modules/Settings/Security/View/View/SessionLineView.swift b/Nynja/Modules/Settings/ActiveSessions/View/View/SessionLineView.swift similarity index 100% rename from Nynja/Modules/Settings/Security/View/View/SessionLineView.swift rename to Nynja/Modules/Settings/ActiveSessions/View/View/SessionLineView.swift diff --git a/Nynja/Modules/Settings/Security/WireFrame/SecurityWireframe.swift b/Nynja/Modules/Settings/ActiveSessions/WireFrame/ActiveSessionsWireframe.swift similarity index 69% rename from Nynja/Modules/Settings/Security/WireFrame/SecurityWireframe.swift rename to Nynja/Modules/Settings/ActiveSessions/WireFrame/ActiveSessionsWireframe.swift index 4d313e80c..2fc07c281 100644 --- a/Nynja/Modules/Settings/Security/WireFrame/SecurityWireframe.swift +++ b/Nynja/Modules/Settings/ActiveSessions/WireFrame/ActiveSessionsWireframe.swift @@ -1,5 +1,5 @@ // -// SecuritySecurityWireframe.swift +// ActiveSessionsWireframe.swift // Nynja // // Created by Michael Katkov on 16/03/2018. @@ -8,15 +8,15 @@ import UIKit -class SecurityWireFrame: SecurityWireFrameProtocol { +final class ActiveSessionsWireFrame: ActiveSessionsWireFrameProtocol { weak var navigation: UINavigationController? weak var mainWireFrame: MainWireFrameProtocol? - func presentSecurity(navigation: UINavigationController, mainWireFrame: MainWireFrameProtocol) { - let view = SecurityViewController() - let presenter = SecurityPresenter() - let interactor = SecurityInteractor() + func presentActiveSessions(navigation: UINavigationController, mainWireFrame: MainWireFrameProtocol) { + let view = ActiveSessionsViewController() + let presenter = ActiveSessionsPresenter() + let interactor = ActiveSessionsInteractor() self.navigation = navigation self.mainWireFrame = mainWireFrame diff --git a/Nynja/OptionsItemsFactory.swift b/Nynja/OptionsItemsFactory.swift index ca864d3d2..0b8077a49 100644 --- a/Nynja/OptionsItemsFactory.swift +++ b/Nynja/OptionsItemsFactory.swift @@ -19,7 +19,7 @@ class OptionsItemsFactory: WCBaseItemsFactory { override var secondLevelItems: ItemModels { var items = [ logout, notifications, changeNumber, wheelPosition, buildNumber, - support, languageSettings, theme, dataAndStorage, security, privacy + support, languageSettings, theme, dataAndStorage, activeSessions, privacy ] #if DEBUG @@ -118,9 +118,9 @@ class OptionsItemsFactory: WCBaseItemsFactory { return item } - var security: ImageActionItemModel { - let item = ImageActionItemModel(navItem: .security, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showSecuritySettings(indexPath: indexPath) + var activeSessions: ImageActionItemModel { + let item = ImageActionItemModel(navItem: .activeSessions, action: { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showActiveSessions(indexPath: indexPath) }) return item } diff --git a/Nynja/Resources/en.lproj/Localizable.strings b/Nynja/Resources/en.lproj/Localizable.strings index 16152023f..90e05f045 100644 --- a/Nynja/Resources/en.lproj/Localizable.strings +++ b/Nynja/Resources/en.lproj/Localizable.strings @@ -596,7 +596,7 @@ "wheel_support"="Support"; "wheel_theme"="Theme"; "wheel_data_and_storage" = "Data and Storage"; -"wheel_security" = "Security"; +"wheel_active_sessions" = "Active sessions"; "wheel_privacy"="Privacy"; "wheel_invite_friends"="Invite Friends"; "wheel_item_transfer" = "Transfer"; @@ -697,22 +697,21 @@ "sms_functionality_is_not_available" = "send sms functionality is not available on your phone"; //MARK: Security -"security_title" = "SECURITY"; -"security_current_session" = "Current session"; -"security_active_sessions" = "Active sessions"; -"security_terminate_all_other_sessions" = "Terminate All Other Sessions"; -"security_logs_out_all_devices" = "Logs out all devices except for this one."; -"security_tap_on_a_session" = "Tap on a session to terminate."; -"security_terminate" = "Terminate this session?"; -"security_you_sure" = "Are you sure you want to terminate all other sessions?"; -"security_waiting_for_network" = "Waiting for Network..."; -"security_no_internet_connection" = "Please check Internet connection and try again."; -"security_nynja" = "NYNJA"; -"security_undefined" = "Undefined"; -"security_no" = "No"; -"security_yes" = "Yes"; -"security_active" = "active"; -"security_no_data" = "No Data"; +"active_sessions_title" = "Active sessions"; +"active_sessions_current_session" = "Current session"; +"active_sessions_terminate_all_other_sessions" = "Terminate All Other Sessions"; +"active_sessions_logs_out_all_devices" = "Logs out all devices except for this one."; +"active_sessions_tap_on_a_session" = "Tap on a session to terminate."; +"active_sessions_terminate" = "Terminate this session?"; +"active_sessions_you_sure" = "Are you sure you want to terminate all other sessions?"; +"active_sessions_waiting_for_network" = "Waiting for Network..."; +"active_sessions_no_internet_connection" = "Please check Internet connection and try again."; +"active_sessions_nynja" = "NYNJA"; +"active_sessions_undefined" = "Undefined"; +"active_sessions_no" = "No"; +"active_sessions_yes" = "Yes"; +"active_sessions_active" = "active"; +"active_sessions_no_data" = "No Data"; //MARK: Privacy "privacy screen title" = "PRIVACY"; -- GitLab From 1394460f9c78c6fe637337595c82e953754006ed Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Wed, 12 Dec 2018 12:31:03 +0200 Subject: [PATCH 11/41] Replace `another language` icons for `translate` and `transcribe` items of context menu. (#1525) --- ...other_language_transcribe_context_menu.pdf | Bin 4965 -> 4785 bytes ...nother_language_translate_context_menu.pdf | Bin 4547 -> 4249 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Nynja/Resources/Assets.xcassets/Messages/ContextMenu/ic_another_language_transcribe_context_menu.imageset/ic_another_language_transcribe_context_menu.pdf b/Nynja/Resources/Assets.xcassets/Messages/ContextMenu/ic_another_language_transcribe_context_menu.imageset/ic_another_language_transcribe_context_menu.pdf index 65793145db7d5f5f668b103b50d50ae1648a2120..9bafb2a1629a25d3f45d7047f0fe5efaa55f1433 100644 GIT binary patch delta 1372 zcmaE=wo!FLKz;2r?>-rG0hjNkTi=LW|Ah^T%(={$?#N%@o)mi3miwws zo1~B0XYrH&9{ww_k0~zOnRQhbO-CUi0a?{Ufrlemr->0X2>A%kY7s-V`cGU0bC_5AVsB(ST zH1_D{FZbpK9~2gy{_>)mosnR%(*N+`%3@E>Wks~9E<~F z?io2a6!rMVM1B!Xx+}EY#;E8msymL|NEGTgviyrzM3H~iiPtwDtd^7B%~?IKR`pwF;BDEnwd_~# zt4zE0Zl-V21Uq(K+rmi_IXp7;7w61Y2{SBCh+Cf)QW(EGROa8^%S+3rPM^5r)!&lH zC)d3f{ra<}!F7pRF2iwu2m36Erq1?nti1Y%7`tbFe{;#Tmv73Q7Y`yQ#IgJTX8iW+jEC3#b(ze|D$QhIyjhEtlZVl4vWcKJm!XlNfsuisiLvqIOu+^% zBTHii0}xQiQ{VzK3=9oT4bjC+49q4A3#ru`8e>Qq8exi=7#JI&8)0ZB^jrg nrzY8P*$`Bs5UXHk$5mXCSX5F`l$yq6Vr*u?rK;-c@5TiH(nDL6 delta 1553 zcmaiuX*kpg9LFidFuFJF~t{fA1z$$BkWu{|$ zFF5-yQn?-F2^OS#*EF(^_!GkW*Z~yO%My_d6fj+ zF0{2~us`BRHtz2;_$|BsXFa9jiN=YV_NV+qMI)K=QA=UYCN{e+%rVMwDQa(iR=pnK zkBgRNM@E`PLYKJbg4!$D$qK9_*e{*&(U5Dc0O6{7b(lv2@o-5RDX#%4T68ZlT?#wd z4`nni)P6>;Z$MXbP2nR`d0>)j0p5kCq(v#DZ+lqtQ#o??1}r6d>{;5KXTM?~X^xb@ z*$Lr{K7r=uvb>Zc?L41hMleccT#raFL4@7f5jqG}g>l(8Yz{r(rbJ3l+$CL?sKAhpcPYYF4L`E|`xWr2uPDPcaTz?6;~$`9GJ=^HQ3NUibKQz4STT zjLdoOm%I||nl-c(Ov5pk#~%0BJfb86~{5FS40N^A}|r%_;( z-W4mXTlQ=RL7?h4)y2GdFuJTe82$;wa0_3X4;Zk^zZ&(hMyj?wLP$cbDof7?nxC%W z_>*8<=&cs_1)h0QA#kQO#+r7}?PAVk%|g$4CU?`NFxMycN|&3G37t`KFHVQ@LH2E! zaHJFIG2Af>po_PmI9PXIJ&1?*n_y}yDyVO9LH?sep+T#%#zC#|G6K$2D$Z{ncen3?{Zu_7xd(`ME-z-2ox`;m^Sq5h06cL#raaz;S$f)k$V`S7#M_CcWy zv-S+4h~hppig!&!LQ)u$aRUqoAq~)mC^Qm@Hpr+4-_QYY7**uIr+P&d^erG!7!2xr z2B3iq_d^;C3~En>0`@WhjY5AvfdX*&?-_u?@9_W(+HjAD!F^*g|NRpHFd2ts&D_a& zEFSCWi2=w+ESZ8vd3%z*sZ?*A7Z&S9HN<(Ef{g!HQN5&UY6=PoB?pH_1bb6K02VL= LsjHjYTY&xn3pl1_ diff --git a/Nynja/Resources/Assets.xcassets/Messages/ContextMenu/ic_another_language_translate_context_menu.imageset/ic_another_language_translate_context_menu.pdf b/Nynja/Resources/Assets.xcassets/Messages/ContextMenu/ic_another_language_translate_context_menu.imageset/ic_another_language_translate_context_menu.pdf index dd2b796d3bc9a10e969888799e13c0ef5df07767..d0a053dd70e0bc3092f2b43b6badcc986200d1b0 100644 GIT binary patch delta 846 zcmX@CJX3K(V100OpR^;#pZUQBjjFtNO+M^ZKM~HCpnA*0Z%OZa{iQD|EmmuE<(zbu z)L4~p@1^nH$r|aGC#p2xoo)G7A#={lR^M$4@4oO;x}#EjJZ*d2j3W2UoJVtFFMC+6 z?k$;=lARImvF4wa&N|B%ZQDv*Uo?5#R1&c@FKCvu@n7@Iasy-iwUmNo>@SbMc;)z5 z>)t!j2FrQEyZQe&aJF}6ot4pI>b&r3=46!{21V@ZZl^MrZV^!_@0g|ST(x;Y!tsc0 z6G9AFUVkxMeCwBtp2qBoCZQcg2{X>`+Z)C3nBVilA_g-d(cEEOABGT5}@p=eF}UPvAOss=?;qMv(`r z8JF2H-iZ6D9eX>jbVJVS_tB-Col(;^9oE%+$2tGOm46CW-=BGKZ~NZZ@weRAce z`trmtNmpK{@ORfSpS<|+23KlcN^wb1YGN+eW)$&7I5~Jd9?OC-G}@85$WH z7#SFv7#mMM&R?%%Y;J0^a}04sM% ARR910 delta 1157 zcmbQKcvyKtVEq*Dyg8OUE#LoZmM~@?Y1hb{D|{x4@4}3OEF0K8YU&l_zkW~7QTO}4 zs5ProM=bJB*gu{7T^V!t%@WNt-8F0Kbfuu>9nbT73{QP_yP{-fa=q)wma<~`$`32; z_%_~(nI*!v?aXhx8$nK$TkUpD_mCRHySX&9Ll>}Lf8(*SdKe-Tyc!>}{_5gE0S|O<{9w1l~;)5DaSzDpYI< zTXL=|T6Rvyyx6G4!r9k8JgmRWAt1%gCF#AjJecpS`mVWNYPE;Fa=kxAP1@!eQ?%M- zYRkRQs{-oVg=_@-}jrJ6*)bN4o_(-QUezaBOf$~7zsu6q3G0Nb@}B_AVY#o#Xn zJT2BI&Xn41z9V?;81H4R;=3!WuJ2s&r%N;J>3wAbhx3Ql9jL2IF1Y78joJFS==Y=Z z7F)dEmR6MFXzG>`v0N7s{-fWZg5KtO`fK1rP_NoRG{!apjM?wcx_q zzF!isfj#2&OR-eNH&4ByYd1XPT>DU0WP63^;qN;W@66G-sr2tu>`|wpWO17blPiQQ zHtK4a2{zC1kF1bcTgs8M;$hvnvh?^c9wqg?=PJ(KT`g=^t#ME$GWt04j+3Q7Tj$nY zUaGFW#lF?>)7I~Ee=;8UTb#m`nwL^sQk0sQ3u5IbWpSA*7$|^fb4!cO@k|^%jK-5Q z__VnU%#17yjSLKo%qGv}tJg6xFa;_E0fjsTE-=Huz|zndUCh|R$a1m=ziPdOr3t#6 zfu$v;dUH&5h6ZMa=z5LK%`wa~wlp-rP-kfd785{rsEL80@#H`JcJXQE#%X58CME{P zNvS4gNro0lrpe}JX% Date: Wed, 12 Dec 2018 13:26:27 +0200 Subject: [PATCH 12/41] [NY-4682] Hide reply counter from UI (#1527) --- Nynja.xcodeproj/project.pbxproj | 14 +++++++ Nynja/FeatureFlags/FeatureFlags.swift | 42 +++++++++++++++++++ .../Message/Presenter/MessagePresenter.swift | 5 ++- .../Message/WireFrame/MessageWireframe.swift | 2 +- Nynja/Modules/Replies/RepliesProtocols.swift | 2 +- .../Replies/WireFrame/RepliesWireFrame.swift | 2 +- 6 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 Nynja/FeatureFlags/FeatureFlags.swift diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 2d1162c3f..23d80b349 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -741,6 +741,8 @@ 4BBAEBCE21AE9F790089B703 /* ValidatorFactoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBAEBCD21AE9F790089B703 /* ValidatorFactoryImpl.swift */; }; 4BBAEBD021AE9F9D0089B703 /* ValidatorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBAEBCF21AE9F9D0089B703 /* ValidatorFactory.swift */; }; 4BC8B38D2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC8B38C2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift */; }; + 4BC95A8021C1224100B462AC /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */; }; + 4BC95A8121C1246800B462AC /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */; }; 4BC95A7721C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; 4BC95A7821C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; 4BC95A7921C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; @@ -3042,6 +3044,7 @@ 4BBAEBCD21AE9F790089B703 /* ValidatorFactoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorFactoryImpl.swift; sourceTree = ""; }; 4BBAEBCF21AE9F9D0089B703 /* ValidatorFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorFactory.swift; sourceTree = ""; }; 4BC8B38C2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsProvidingFetchingArgs.swift; sourceTree = ""; }; + 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = ""; }; 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableConstants.swift; sourceTree = ""; }; 4BDC7E60203492CA00BCD381 /* TopSwipable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSwipable.swift; sourceTree = ""; }; 4BDC7E62203494C000BCD381 /* ScheduleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleButton.swift; sourceTree = ""; }; @@ -6073,6 +6076,7 @@ 3A768E1C1ECD152300108F7C /* Services */ = { isa = PBXGroup; children = ( + 4BC95A7E21C1220D00B462AC /* FeatureFlags */, 4B2C502521B568FD00FBA9B1 /* MigrationManager */, 851769D420D584CA008ACF6B /* Amazon */, 26ABCA3D21189DA400EA4782 /* Aps.swift */, @@ -7405,6 +7409,14 @@ name = Entity; sourceTree = ""; }; + 4BC95A7E21C1220D00B462AC /* FeatureFlags */ = { + isa = PBXGroup; + children = ( + 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */, + ); + path = FeatureFlags; + sourceTree = ""; + }; 4BDDAAE7218CA27200F775A7 /* HomeDataProvider */ = { isa = PBXGroup; children = ( @@ -14905,6 +14917,7 @@ 8509FC8A2159095900734D93 /* AppGroupFlagContainer.swift in Sources */, 4B5A0B84216F4A61002C4160 /* AttachmentTransformer.swift in Sources */, 4B5A0B75216E3BDD002C4160 /* ProgressHUD.swift in Sources */, + 4BC95A8121C1246800B462AC /* FeatureFlags.swift in Sources */, A42CE58820692EDB000889CC /* Cursor.swift in Sources */, A49E1BD320A9AA0E0074DFD3 /* BaseChatModel.swift in Sources */, A42CE5C820692EDB000889CC /* act_Spec.swift in Sources */, @@ -16657,6 +16670,7 @@ 85629ECA2137EF2400A79C97 /* VoiceAudioInteractive.swift in Sources */, 265EB71D20A86A1900C1483E /* ConvertionProgressModel.swift in Sources */, 85D66A0320BD963C00FBD803 /* NSAttributedStringKey+Mention.swift in Sources */, + 4BC95A8021C1224100B462AC /* FeatureFlags.swift in Sources */, E7417E991FBED91100E5C124 /* Table.swift in Sources */, 0008E9192032F4C8003E316E /* SendJob.swift in Sources */, 4DAEBCF361B86B0AD3C98749 /* EditUsernameInteractor.swift in Sources */, diff --git a/Nynja/FeatureFlags/FeatureFlags.swift b/Nynja/FeatureFlags/FeatureFlags.swift new file mode 100644 index 000000000..8e92d30f6 --- /dev/null +++ b/Nynja/FeatureFlags/FeatureFlags.swift @@ -0,0 +1,42 @@ +// +// FeatureFlags.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/12/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +enum FeatureFlags { + static let isRepliesCounterEnabled: Flag = Flag(value: false, reason: "Should be removed for 0.8.0 Live. Blocked by 'Threads' feature.") +} + +extension FeatureFlags { + + struct Flag { + let value: T + let reason: String? + + init(value: T, reason: String? = nil) { + self.value = value + self.reason = reason + } + } +} + +extension FeatureFlags.Flag: CustomStringConvertible where T: CustomStringConvertible { + + var description: String { + var text = """ + Feature Flag: + value = \(value) + """ + + if let reason = reason { + text.append("\nreason = \(reason)") + } + + return text + } +} diff --git a/Nynja/Modules/Message/Presenter/MessagePresenter.swift b/Nynja/Modules/Message/Presenter/MessagePresenter.swift index ab06e074e..8b9aac0ad 100644 --- a/Nynja/Modules/Message/Presenter/MessagePresenter.swift +++ b/Nynja/Modules/Message/Presenter/MessagePresenter.swift @@ -774,7 +774,10 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract model.isOwner = isOutgoingMessage(message) - model.replied = message.repliedby?.count ?? 0 + if FeatureFlags.isRepliesCounterEnabled.value { + model.replied = message.repliedby?.count ?? 0 + } + model.timeStamp = Date(timestamp: message.createdInt) let size = attach.size ?? 0 diff --git a/Nynja/Modules/Message/WireFrame/MessageWireframe.swift b/Nynja/Modules/Message/WireFrame/MessageWireframe.swift index 71987cd8c..7e71ff8c6 100644 --- a/Nynja/Modules/Message/WireFrame/MessageWireframe.swift +++ b/Nynja/Modules/Message/WireFrame/MessageWireframe.swift @@ -62,7 +62,7 @@ class MessageWireFrame: MessageWireframeProtocol, DocumentInteractionWireFrame { } func openReplies(redirectAction: ((String)->Void)?) { - RepliesWireFrame().present(navigation: navigation!, main: main, redirectAction: redirectAction) + RepliesWireFrame().presentReplies(navigation: navigation!, main: main, redirectAction: redirectAction) } func showProfileScreen(contact: Contact) { diff --git a/Nynja/Modules/Replies/RepliesProtocols.swift b/Nynja/Modules/Replies/RepliesProtocols.swift index 8c4f3cfa1..183283204 100644 --- a/Nynja/Modules/Replies/RepliesProtocols.swift +++ b/Nynja/Modules/Replies/RepliesProtocols.swift @@ -13,7 +13,7 @@ import CoreLocation protocol RepliesWireframeProtocol: class { var main: MainWireFrame? { get set } - func present(navigation: UINavigationController, main: MainWireFrame?, redirectAction: ((String)->Void)?) + func presentReplies(navigation: UINavigationController, main: MainWireFrame?, redirectAction: ((String)->Void)?) func hide(completion: (()->())?) } diff --git a/Nynja/Modules/Replies/WireFrame/RepliesWireFrame.swift b/Nynja/Modules/Replies/WireFrame/RepliesWireFrame.swift index b12b43e0c..a60840252 100644 --- a/Nynja/Modules/Replies/WireFrame/RepliesWireFrame.swift +++ b/Nynja/Modules/Replies/WireFrame/RepliesWireFrame.swift @@ -13,7 +13,7 @@ class RepliesWireFrame: RepliesWireframeProtocol { weak var navigation : UINavigationController? weak var main: MainWireFrame? - func present(navigation: UINavigationController, main: MainWireFrame?, redirectAction: ((String)->Void)?) { + func presentReplies(navigation: UINavigationController, main: MainWireFrame?, redirectAction: ((String)->Void)?) { guard let messageId = UserDefaults.standard.string(forKey: String(SharedParameters.messageId.rawValue)) else { return } -- GitLab From acf60221a937f3c0be70d50024cf4e07ef53243e Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Wed, 12 Dec 2018 13:35:13 +0200 Subject: [PATCH 13/41] [NY-6059] Remove access to marketplace from UI (#1526) --- Nynja/FeatureFlags/FeatureFlags.swift | 1 + ...ynjaContextMenuItemsFactory+Messages.swift | 19 ++++++++--- Nynja/Modules/Message/View/MessageVC.swift | 32 +++++++++++++++---- Nynja/WCBaseItemsFactory.swift | 6 +++- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/Nynja/FeatureFlags/FeatureFlags.swift b/Nynja/FeatureFlags/FeatureFlags.swift index 8e92d30f6..1c746d67d 100644 --- a/Nynja/FeatureFlags/FeatureFlags.swift +++ b/Nynja/FeatureFlags/FeatureFlags.swift @@ -10,6 +10,7 @@ import Foundation enum FeatureFlags { static let isRepliesCounterEnabled: Flag = Flag(value: false, reason: "Should be removed for 0.8.0 Live. Blocked by 'Threads' feature.") + static let isMarketplaceEnabled: Flag = Flag(value: false, reason: "Should be removed for 0.8.0 Live.") } extension FeatureFlags { diff --git a/Nynja/Library/UI/ContextMenu/NynjaContextMenuItemsFactory+Messages.swift b/Nynja/Library/UI/ContextMenu/NynjaContextMenuItemsFactory+Messages.swift index a031bd165..dda5b6bf3 100644 --- a/Nynja/Library/UI/ContextMenu/NynjaContextMenuItemsFactory+Messages.swift +++ b/Nynja/Library/UI/ContextMenu/NynjaContextMenuItemsFactory+Messages.swift @@ -10,7 +10,7 @@ import NynjaUIKit extension NynjaContextMenuItemsFactory { - convenience init(type: SendMessageType, model: BaseChatCellModel, userInfo: NynjaContextMenuUserInfo?) { + convenience init?(type: SendMessageType, model: BaseChatCellModel, userInfo: NynjaContextMenuUserInfo?) { let rows: [ContextMenuRow] switch type { case .text: @@ -28,7 +28,11 @@ extension NynjaContextMenuItemsFactory { case .sticker: rows = NynjaContextMenuItemsFactory.stickerMessageItems(for: model) case .audioCall: - rows = NynjaContextMenuItemsFactory.audioCallMessageItems(for: model) + if FeatureFlags.isMarketplaceEnabled.value { + rows = NynjaContextMenuItemsFactory.audioCallMessageItems(for: model) + } else { + return nil + } case .transfer: rows = NynjaContextMenuItemsFactory.makeTransferMessageItems(for: model) default: @@ -81,7 +85,7 @@ extension NynjaContextMenuItemsFactory { } private static func textMessageItems(for model: BaseChatCellModel) -> [ContextMenuRow] { - return [ + var rows = [ ContextMenuRow(items: [ starItem(for: model), reply(), forward(), delete()] ), @@ -89,10 +93,15 @@ extension NynjaContextMenuItemsFactory { translation(for: model), copy(), shouldAddEdit(for: model) ? edit() : placeholder()] - ), - ContextMenuRow(items: [marketplace(with: .full)] ) ] + + if FeatureFlags.isMarketplaceEnabled.value { + let marketplaceRow = ContextMenuRow(items: [marketplace(with: .full)]) + rows.append(marketplaceRow) + } + + return rows } private static func audioMessageItems(for model: BaseChatCellModel) -> [ContextMenuRow] { diff --git a/Nynja/Modules/Message/View/MessageVC.swift b/Nynja/Modules/Message/View/MessageVC.swift index f9c89e06c..67f03c306 100644 --- a/Nynja/Modules/Message/View/MessageVC.swift +++ b/Nynja/Modules/Message/View/MessageVC.swift @@ -1050,13 +1050,12 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw } let userInfo = contextMenuUserInfo(for: model, ofType: type, convertionModel: convertingModel) - let itemsFactory: NynjaContextMenuItemsFactory - if let convertingModel = convertingModel { - itemsFactory = NynjaContextMenuItemsFactory.messageConvertedMenuConfig( - cellModel: model, convertionModel: convertingModel, userInfo: userInfo - ) - } else { - itemsFactory = NynjaContextMenuItemsFactory(type: type, model: model, userInfo: userInfo) + guard let itemsFactory = tryMakeContextMenuItemsFactory( + convertingModel: convertingModel, + model: model, + type: type, + userInfo: userInfo) else { + return } let maskFrame = inputBar.superview!.convert(inputBar.frame, to: inputBar.window!) @@ -1077,6 +1076,25 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw contextMenuPresented = true } + + private func tryMakeContextMenuItemsFactory( + convertingModel: ConvertionMessageModel?, + model: BaseChatCellModel, + type: SendMessageType, + userInfo: NynjaContextMenuUserInfo) -> NynjaContextMenuItemsFactory? { + + if let convertingModel = convertingModel { + return NynjaContextMenuItemsFactory.messageConvertedMenuConfig( + cellModel: model, + convertionModel: convertingModel, + userInfo: userInfo) + } else { + return NynjaContextMenuItemsFactory( + type: type, + model: model, + userInfo: userInfo) + } + } private func contextMenuUserInfo(for model: BaseChatCellModel, ofType type: SendMessageType, convertionModel: ConvertionMessageModel? = nil) -> NynjaContextMenuUserInfo { let userInfo = NynjaContextMenuUserInfo() diff --git a/Nynja/WCBaseItemsFactory.swift b/Nynja/WCBaseItemsFactory.swift index 4d77d14ec..1ecfa4174 100644 --- a/Nynja/WCBaseItemsFactory.swift +++ b/Nynja/WCBaseItemsFactory.swift @@ -21,7 +21,11 @@ class WCBaseItemsFactory: WCItemsFactory { } var firstLevelItems: ItemModels { - return [marketplace, calls, options, home, search, mySelf, actions, chats, groups, contacts, channels] + if FeatureFlags.isMarketplaceEnabled.value { + return [marketplace, calls, options, home, search, mySelf, actions, chats, groups, contacts, channels] + } else { + return [calls, options, home, search, mySelf, actions, chats, groups, contacts, channels] + } } var secondLevelItems: ItemModels { -- GitLab From 735611a3f82de36e27ecd8c4b8a09b34fdcf8e67 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Wed, 12 Dec 2018 22:14:02 +0200 Subject: [PATCH 14/41] Remove Build\Release version from splash screen. (#1528) --- .../Splash/View/SplashViewController.swift | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Nynja/Modules/Splash/View/SplashViewController.swift b/Nynja/Modules/Splash/View/SplashViewController.swift index 43a74e7c4..8bd60fbda 100644 --- a/Nynja/Modules/Splash/View/SplashViewController.swift +++ b/Nynja/Modules/Splash/View/SplashViewController.swift @@ -38,20 +38,6 @@ class SplashViewController: BaseVC, SplashViewProtocol { return UIView() }() - lazy var info: UILabel = { - let lbl = UILabel() - lbl.font = UIFont(name: FontFamily.NotoSans.bold.name, size: 16)! - lbl.numberOfLines = 0 - lbl.textColor = UIColor.nynja.manatee - self.view.addSubview(lbl) - - lbl.snp.makeConstraints({ (make) in - make.left.equalTo(self.view).offset(30) - self.adjustVerticalInset(.top, make: make, offset: 30) - }) - return lbl - }() - // MARK: - View lifecycle @@ -59,9 +45,6 @@ class SplashViewController: BaseVC, SplashViewProtocol { super.viewDidLoad() self.view.frame = UIScreen.main.bounds splash.isHidden = false - if let buildVersion = Bundle.main.buildVersion { - info.text = "Build: \(buildVersion)" - } } override func viewDidAppear(_ animated: Bool) { -- GitLab From d8ba0948284e4d6feaf65ea5a077b331681f1665 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Wed, 12 Dec 2018 22:14:33 +0200 Subject: [PATCH 15/41] Make max length of alias equal to 65. (#1529) --- .../MyGroupAlias/Presenter/MyGroupAliasPresenter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nynja/Modules/Flows/CreateGroupFlow/MyGroupAlias/Presenter/MyGroupAliasPresenter.swift b/Nynja/Modules/Flows/CreateGroupFlow/MyGroupAlias/Presenter/MyGroupAliasPresenter.swift index 13799f69d..2d7b3b7d8 100644 --- a/Nynja/Modules/Flows/CreateGroupFlow/MyGroupAlias/Presenter/MyGroupAliasPresenter.swift +++ b/Nynja/Modules/Flows/CreateGroupFlow/MyGroupAlias/Presenter/MyGroupAliasPresenter.swift @@ -69,7 +69,7 @@ class MyGroupAliasPresenter: BasePresenter, GroupInputPresenterProtocol, Initial // MARK: - MyGroupAliasPresenterProtocol var validators: [MTIValidator] { - return validatorFactory.makeGroupInputValidators(maxLength: 64, emptyWarningMessage: String.localizable.aliasEmptyMessage) + return validatorFactory.makeGroupInputValidators(maxLength: 65, emptyWarningMessage: String.localizable.aliasEmptyMessage) } func cancel() { -- GitLab From 92e26a09c57351561ee1c52d23791a84ada367db Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Wed, 12 Dec 2018 22:39:27 +0200 Subject: [PATCH 16/41] Implement 'Add sender's alias to sticker message in group'. (#1530) --- .../ChatCells/BaseChatCell/BaseChatCell.swift | 4 +--- .../BaseChatCell/OponentChatCell.swift | 23 +++---------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift index 9b7f9ca1f..a376d550b 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift @@ -322,9 +322,7 @@ class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, Messag fromLabel.snp.updateConstraints { $0.height.equalTo(BaseChatCell.fromLabelHeight.adjustedByWidth) } } else { fromLabel.text = nil - fromLabel.snp.updateConstraints { - $0.height.equalTo(0) - } + fromLabel.snp.updateConstraints { $0.height.equalTo(0) } fromImageView.snp.updateConstraints { $0.width.equalTo(BaseChatCell.avatarHeight.adjustedByWidth/2) } fromImageView.isHidden = true } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift index 255242771..f9e84b9c7 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift @@ -63,14 +63,12 @@ class OponentChatCell: BaseChatCell { } } - private(set) var messageViewTopToBubbleConstraint: Constraint? private(set) var messageViewTopToAliasMinimizedConstraint: Constraint? override func makeMessageView() -> UIView { let view = super.makeMessageView() view.snp.makeConstraints { maker in - messageViewTopToBubbleConstraint = maker.top.equalTo(bubble.snp.top).constraint messageViewTopToAliasMinimizedConstraint = maker.top.equalTo(fromLabel.snp.bottom).offset(-OponentChatCell.topInset).constraint } @@ -83,20 +81,12 @@ class OponentChatCell: BaseChatCell { override func setupMessageContent(_ model: BaseChatCellModel) { super.setupMessageContent(model) - if !OponentChatCell.shouldDisplayAlias(for: model) { - fromLabel.isHidden = true - messageViewTopToAliasConstraint?.deactivate() - messageViewTopToAliasMinimizedConstraint?.deactivate() - messageViewTopToBubbleConstraint?.activate() - - } else if OponentChatCell.shouldMinimizeTopOffset(for: model) { + if OponentChatCell.shouldMinimizeTopOffset(for: model) { messageViewTopToAliasConstraint?.deactivate() messageViewTopToAliasMinimizedConstraint?.activate() - messageViewTopToBubbleConstraint?.deactivate() } else { messageViewTopToAliasConstraint?.activate() messageViewTopToAliasMinimizedConstraint?.deactivate() - messageViewTopToBubbleConstraint?.deactivate() } guard let content = messageContent else { return } @@ -113,7 +103,7 @@ class OponentChatCell: BaseChatCell { override func updateData(_ model: BaseChatCellModel) { super.updateData(model) - if !OponentChatCell.shouldDisplayAlias(for: model), OponentChatCell.shouldShowSender(model) { + if model.type == .sticker, OponentChatCell.shouldShowSender(model) { bubbleLeftToSuperviewConstraint?.deactivate() bubbleLeftToAvatarConstraint?.activate() } else { @@ -122,10 +112,6 @@ class OponentChatCell: BaseChatCell { } } - private static func shouldDisplayAlias(for model: BaseChatCellModel) -> Bool { - return model.type != .sticker - } - private static func shouldMinimizeTopOffset(for model: BaseChatCellModel) -> Bool { guard shouldShowSender(model) else { return false } @@ -149,10 +135,7 @@ class OponentChatCell: BaseChatCell { override class func size(for model: BaseChatCellModel) -> CGSize { var size = super.size(for: model) - if !shouldDisplayAlias(for: model) && shouldShowSender(model) { - size.height -= OponentChatCell.Constraints.fromImageView.height.adjustedByWidth - - } else if shouldMinimizeTopOffset(for: model) { + if shouldMinimizeTopOffset(for: model) { size.height -= OponentChatCell.topInset } -- GitLab From dc460973412d6b1287b10ad68a24a1d9eb3a359e Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Thu, 13 Dec 2018 17:52:59 +0200 Subject: [PATCH 17/41] Fix build issue connected with `TestFairy`. (#1518) --- Nynja.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 23d80b349..7585fe6a5 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -14567,7 +14567,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Fabric/run\" 595b68a8c4deb3533dcdfc24ca73fd3cffd99f3c 27e6d0f170d463c67f2968218ccba54d2f8f44cfa13615771a1dee6f72469dcb\n"; + shellScript = "\"${PODS_ROOT}/Fabric/run\" 595b68a8c4deb3533dcdfc24ca73fd3cffd99f3c 27e6d0f170d463c67f2968218ccba54d2f8f44cfa13615771a1dee6f72469dcb\nsh \"${SRCROOT}/Pods/TestFairy/upload-dsym.sh\" f921f39811d4cb2eced9675f802b7fa30cc3ce58\n"; }; 5EF78D1226FD7A5DAFF4A6A5 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; -- GitLab From 08e20c9477eb020d970f5adceeda361ad6afbcb7 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Thu, 13 Dec 2018 17:24:55 +0200 Subject: [PATCH 18/41] [NY-5252] Remove / change behavior of grayed out Wheel items (#1531) --- Nynja.xcodeproj/project.pbxproj | 64 ++++++++++++------- Nynja/AboutItemsFactory.swift | 20 ------ Nynja/ChatItemsFactory.swift | 26 ++++---- Nynja/ChatsItemsFactory.swift | 14 ++-- Nynja/FavoritesGroupItemsFactory.swift | 22 +++++++ Nynja/FavoritesItemsFactory.swift | 17 ----- Nynja/FavoritesP2pItemsFactory.swift | 22 +++++++ Nynja/FeatureFlags/FeatureFlags.swift | 46 ++++++++++--- Nynja/Generated/ColorsConstants.swift | 2 + Nynja/Generated/LocalizableConstants.swift | 2 - Nynja/GroupChatsItemsFactory.swift | 33 +++++++--- Nynja/HomeItemsFactory.swift | 31 ++++++--- .../CommingSoonWheelItemModel.swift | 51 +++++++++++++++ .../ItemModels/ImageActionItemModel.swift | 2 +- .../Wheel/ItemModels/WheelItemModel.swift | 12 +--- .../Favorites/Entities/FavoritesMode.swift | 13 ++++ .../Favorites/FavoritesProtocols.swift | 2 +- .../Presenter/FavoritesPresenter.swift | 11 +++- .../WireFrame/FavoritesWireframe.swift | 6 +- Nynja/Modules/Main/MainProtocols.swift | 5 +- .../Main/Presenter/MainPresenter.swift | 6 +- .../Main/View/MainNavigationItem.swift | 1 - .../MainViewController+NavigateProtocol.swift | 4 +- .../Modules/Main/View/NavigateProtocol.swift | 3 +- .../Main/WireFrame/MainWireframe.swift | 4 +- .../Profile/WireFrame/ProfileWireframe.swift | 2 +- .../Splash/Presenter/SplashPresenter.swift | 4 -- Nynja/MySelfItemsFactory.swift | 1 - Nynja/OptionsItemsFactory.swift | 57 +++++++++-------- Nynja/P2pChatItemsFactory.swift | 16 ++++- Nynja/P2pChatsItemsFactory.swift | 36 +++++++---- Nynja/Resources/Colors.json | 1 + Nynja/Resources/en.lproj/Localizable.strings | 1 - Nynja/WCBaseItemsFactory.swift | 37 +++++++---- 34 files changed, 381 insertions(+), 193 deletions(-) delete mode 100644 Nynja/AboutItemsFactory.swift create mode 100644 Nynja/FavoritesGroupItemsFactory.swift delete mode 100644 Nynja/FavoritesItemsFactory.swift create mode 100644 Nynja/FavoritesP2pItemsFactory.swift create mode 100644 Nynja/Library/UI/WheelContainer/Wheel/ItemModels/CommingSoonWheelItemModel.swift create mode 100644 Nynja/Modules/Favorites/Entities/FavoritesMode.swift diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 7585fe6a5..7d9a7cbcf 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -579,8 +579,6 @@ 4B1162FF21BFC213003859ED /* AppNotificationsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B71AC4121622A6A00E4583B /* AppNotificationsProvider.swift */; }; 4B11630121BFC277003859ED /* AppNotificationsProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B71AC4421622AA700E4583B /* AppNotificationsProviding.swift */; }; 4B1D7DFA2029BF3400703228 /* HistoryItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7DF92029BF3400703228 /* HistoryItemsFactory.swift */; }; - 4B1D7DFC2029C37900703228 /* FavoritesItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7DFB2029C37900703228 /* FavoritesItemsFactory.swift */; }; - 4B1D7DFE2029C41C00703228 /* AboutItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7DFD2029C41C00703228 /* AboutItemsFactory.swift */; }; 4B1D7E012029C4BE00703228 /* OptionsItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7E002029C4BE00703228 /* OptionsItemsFactory.swift */; }; 4B1D7E032029C80800703228 /* ByContactsItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7E022029C80800703228 /* ByContactsItemsFactory.swift */; }; 4B1D7E052029CF2900703228 /* ShareContactsItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7E042029CF2900703228 /* ShareContactsItemsFactory.swift */; }; @@ -741,12 +739,12 @@ 4BBAEBCE21AE9F790089B703 /* ValidatorFactoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBAEBCD21AE9F790089B703 /* ValidatorFactoryImpl.swift */; }; 4BBAEBD021AE9F9D0089B703 /* ValidatorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBAEBCF21AE9F9D0089B703 /* ValidatorFactory.swift */; }; 4BC8B38D2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC8B38C2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift */; }; - 4BC95A8021C1224100B462AC /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */; }; - 4BC95A8121C1246800B462AC /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */; }; 4BC95A7721C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; 4BC95A7821C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; 4BC95A7921C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; 4BC95A7A21C0077100B462AC /* LocalizableConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */; }; + 4BC95A8021C1224100B462AC /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */; }; + 4BC95A8121C1246800B462AC /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */; }; 4BD53BF4202C8BCA00569C1A /* AVURLAsset+Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E77FBDDC1FFE828400BDB255 /* AVURLAsset+Duration.swift */; }; 4BDC7E61203492CA00BCD381 /* TopSwipable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDC7E60203492CA00BCD381 /* TopSwipable.swift */; }; 4BDC7E63203494C000BCD381 /* ScheduleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDC7E62203494C000BCD381 /* ScheduleButton.swift */; }; @@ -763,6 +761,11 @@ 4BE2C5E72142EB5A00A73DD9 /* NynjaCommunicatorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE2C5E52142EB5A00A73DD9 /* NynjaCommunicatorService.swift */; }; 4BE2C5E82142EB5A00A73DD9 /* NynjaRingingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE2C5E62142EB5A00A73DD9 /* NynjaRingingService.swift */; }; 4BEE89D69CACB85ABEE9046F /* QRCodeGeneratorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFAB7D8D9024C26FA51BF783 /* QRCodeGeneratorPresenter.swift */; }; + 4BEF0F6521C265F40012B6E1 /* CommingSoonWheelItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF0F6421C265F40012B6E1 /* CommingSoonWheelItemModel.swift */; }; + 4BEF0F6821C28FC70012B6E1 /* FavoritesMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF0F6721C28FC70012B6E1 /* FavoritesMode.swift */; }; + 4BEF0F6C21C290A10012B6E1 /* FavoritesP2pItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF0F6B21C290A10012B6E1 /* FavoritesP2pItemsFactory.swift */; }; + 4BEF0F6E21C290B10012B6E1 /* FavoritesGroupItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF0F6D21C290B10012B6E1 /* FavoritesGroupItemsFactory.swift */; }; + 4BEF0F6F21C294A40012B6E1 /* FavoritesMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF0F6721C28FC70012B6E1 /* FavoritesMode.swift */; }; 4BF090B921635B4700DCCA5C /* LogServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090B821635B4700DCCA5C /* LogServiceProtocol.swift */; }; 4BF090BB21635B6600DCCA5C /* LogServiceTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090BA21635B6600DCCA5C /* LogServiceTopic.swift */; }; 4BF090BC21635B8000DCCA5C /* LogServiceTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090BA21635B6600DCCA5C /* LogServiceTopic.swift */; }; @@ -2905,8 +2908,6 @@ 4B0CC2012195B69900E0BA61 /* LinkHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkHandlerDelegate.swift; sourceTree = ""; }; 4B15A544CC681BABD1A631AF /* QRCodeGeneratorInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = QRCodeGeneratorInteractor.swift; sourceTree = ""; }; 4B1D7DF92029BF3400703228 /* HistoryItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryItemsFactory.swift; sourceTree = ""; }; - 4B1D7DFB2029C37900703228 /* FavoritesItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesItemsFactory.swift; sourceTree = ""; }; - 4B1D7DFD2029C41C00703228 /* AboutItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutItemsFactory.swift; sourceTree = ""; }; 4B1D7E002029C4BE00703228 /* OptionsItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionsItemsFactory.swift; sourceTree = ""; }; 4B1D7E022029C80800703228 /* ByContactsItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ByContactsItemsFactory.swift; sourceTree = ""; }; 4B1D7E042029CF2900703228 /* ShareContactsItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareContactsItemsFactory.swift; sourceTree = ""; }; @@ -3044,8 +3045,8 @@ 4BBAEBCD21AE9F790089B703 /* ValidatorFactoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorFactoryImpl.swift; sourceTree = ""; }; 4BBAEBCF21AE9F9D0089B703 /* ValidatorFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorFactory.swift; sourceTree = ""; }; 4BC8B38C2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsProvidingFetchingArgs.swift; sourceTree = ""; }; - 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = ""; }; 4BC95A7621C0077000B462AC /* LocalizableConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableConstants.swift; sourceTree = ""; }; + 4BC95A7F21C1224100B462AC /* FeatureFlags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = ""; }; 4BDC7E60203492CA00BCD381 /* TopSwipable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSwipable.swift; sourceTree = ""; }; 4BDC7E62203494C000BCD381 /* ScheduleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleButton.swift; sourceTree = ""; }; 4BDDAAE8218CA28300F775A7 /* HomeDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataProvider.swift; sourceTree = ""; }; @@ -3060,6 +3061,10 @@ 4BE2C5E12142EB0E00A73DD9 /* AudioManagerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioManagerDelegate.swift; sourceTree = ""; }; 4BE2C5E52142EB5A00A73DD9 /* NynjaCommunicatorService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NynjaCommunicatorService.swift; sourceTree = ""; }; 4BE2C5E62142EB5A00A73DD9 /* NynjaRingingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NynjaRingingService.swift; sourceTree = ""; }; + 4BEF0F6421C265F40012B6E1 /* CommingSoonWheelItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommingSoonWheelItemModel.swift; sourceTree = ""; }; + 4BEF0F6721C28FC70012B6E1 /* FavoritesMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesMode.swift; sourceTree = ""; }; + 4BEF0F6B21C290A10012B6E1 /* FavoritesP2pItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesP2pItemsFactory.swift; sourceTree = ""; }; + 4BEF0F6D21C290B10012B6E1 /* FavoritesGroupItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesGroupItemsFactory.swift; sourceTree = ""; }; 4BF090B821635B4700DCCA5C /* LogServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogServiceProtocol.swift; sourceTree = ""; }; 4BF090BA21635B6600DCCA5C /* LogServiceTopic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogServiceTopic.swift; sourceTree = ""; }; 4BF090C421635E8600DCCA5C /* Message+LinkedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+LinkedId.swift"; sourceTree = ""; }; @@ -6076,8 +6081,6 @@ 3A768E1C1ECD152300108F7C /* Services */ = { isa = PBXGroup; children = ( - 4BC95A7E21C1220D00B462AC /* FeatureFlags */, - 4B2C502521B568FD00FBA9B1 /* MigrationManager */, 851769D420D584CA008ACF6B /* Amazon */, 26ABCA3D21189DA400EA4782 /* Aps.swift */, 4BE2C5CE2142EAC500A73DD9 /* Audio */, @@ -6093,6 +6096,7 @@ 26EA201120BECD6500FBB9CA /* ConversationLanguageSettingService */, 4B7C73E7215A5508007924DB /* Debug */, 26DCB255206924B3001EF0AB /* FeatureFactory.swift */, + 4BC95A7E21C1220D00B462AC /* FeatureFlags */, 26B5F7411FB0FF7B00CEC6AE /* FileManager.swift */, 8509FC832158F7B400734D93 /* Files */, 2625DBF720EFC5DE00E01C05 /* FourCharCode+StringLiteralConvertible.swift */, @@ -6107,6 +6111,7 @@ 26E3229120E4F18100271413 /* MessageParser */, E7EC77A21FD1B9BD00DC8245 /* MessageProcessing */, F11786D220A98D0A007A9A1B /* MessageSendingService */, + 4B2C502521B568FD00FBA9B1 /* MigrationManager */, 3AC321761EEAC4700068F3C8 /* Models */, 3A8045CC1F60C8E200AED866 /* MQTT */, A458FAC020EBA4D70075D55E /* MuteChatService */, @@ -6674,14 +6679,13 @@ children = ( 4B06D3182028A66A003B275B /* Base */, A4494C7C2080F24300223B06 /* Channels */, - 4BFABA332028CD8800299EE7 /* Contacts */, - 4B06D3192028A675003B275B /* ChatsList */, 4B06D31A2028A690003B275B /* Chat */, - 4B1D7DFF2029C4A900703228 /* Options */, + 4B06D3192028A675003B275B /* ChatsList */, + 4BFABA332028CD8800299EE7 /* Contacts */, + B7F5051C2061252100C28FA1 /* DataAndStorageItemsFactory.swift */, 4B06D30B2028A25D003B275B /* HomeItemsFactory.swift */, - 4B1D7DFB2029C37900703228 /* FavoritesItemsFactory.swift */, + 4B1D7DFF2029C4A900703228 /* Options */, 4B1D7E062029D00000703228 /* OtherUserProfileItemsFactory.swift */, - B7F5051C2061252100C28FA1 /* DataAndStorageItemsFactory.swift */, ); name = Factory; sourceTree = ""; @@ -6699,9 +6703,11 @@ isa = PBXGroup; children = ( 4B06D30D2028A349003B275B /* ChatsItemsFactory.swift */, - 4B06D30F2028A472003B275B /* P2pChatsItemsFactory.swift */, - 4B06D3112028A4CF003B275B /* GroupChatsItemsFactory.swift */, 4B1D7E082029D86600703228 /* CreateGroupItemsFactory.swift */, + 4BEF0F6D21C290B10012B6E1 /* FavoritesGroupItemsFactory.swift */, + 4BEF0F6B21C290A10012B6E1 /* FavoritesP2pItemsFactory.swift */, + 4B06D3112028A4CF003B275B /* GroupChatsItemsFactory.swift */, + 4B06D30F2028A472003B275B /* P2pChatsItemsFactory.swift */, ); name = ChatsList; sourceTree = ""; @@ -6751,7 +6757,6 @@ 4B1D7DFF2029C4A900703228 /* Options */ = { isa = PBXGroup; children = ( - 4B1D7DFD2029C41C00703228 /* AboutItemsFactory.swift */, 00E9824B205C1E19008BF03D /* ActiveSessionsItemsFactory.swift */, 85788C4D20443DD2003600C9 /* BuildNumberItemsFactory.swift */, 00E86468204D519600844FF1 /* LanguageSettingsItemsFactory.swift */, @@ -7494,6 +7499,14 @@ path = Services/NynjaCalls; sourceTree = ""; }; + 4BEF0F6621C28F740012B6E1 /* Entities */ = { + isa = PBXGroup; + children = ( + 4BEF0F6721C28FC70012B6E1 /* FavoritesMode.swift */, + ); + path = Entities; + sourceTree = ""; + }; 4BF090B721635B3700DCCA5C /* LogService */ = { isa = PBXGroup; children = ( @@ -7570,6 +7583,7 @@ 51D6048350255DBFEC3969A5 /* Favorites */ = { isa = PBXGroup; children = ( + 4BEF0F6621C28F740012B6E1 /* Entities */, 462440AD41D807CE8957FDD9 /* FavoritesProtocols.swift */, D07E02E41718EC43B68B94D0 /* View */, F53F982DD18212A69B499534 /* Presenter */, @@ -12504,16 +12518,17 @@ E77764B81FBDA9800042541D /* ItemModels */ = { isa = PBXGroup; children = ( - 3AE0A8401F20321A008A04F3 /* WheelItemModel.swift */, - E77764C01FBDA9BC0042541D /* ImageWheelItemModel.swift */, + 26F03C0C20698B0000712CB0 /* ChatWheelItemModel.swift */, + 4BEF0F6421C265F40012B6E1 /* CommingSoonWheelItemModel.swift */, + 26F5C8BD206BD49B003A7FF5 /* DefaultActionItemModel.swift */, 2657BE522012405600F21935 /* ImageActionItemModel.swift */, 2657BE50201233E300F21935 /* ImageFilledItemModel.swift */, E77764BF1FBDA9BC0042541D /* ImageFullWheelItemModel.swift */, + E77764C01FBDA9BC0042541D /* ImageWheelItemModel.swift */, 26610F5A2015476C00609F77 /* LocationFullWheelItemModel.swift */, 8504DEA820693588006722AC /* MediaFullWheelItemModel.swift */, - 26F03C0C20698B0000712CB0 /* ChatWheelItemModel.swift */, - 26F5C8BD206BD49B003A7FF5 /* DefaultActionItemModel.swift */, 8541BD69206CE0340093EF1E /* Placeholders */, + 3AE0A8401F20321A008A04F3 /* WheelItemModel.swift */, ); path = ItemModels; sourceTree = ""; @@ -14982,6 +14997,7 @@ A42CE59A20692EDB000889CC /* writer.swift in Sources */, A42CE60620692EDB000889CC /* History_Spec.swift in Sources */, A42CE61820692EDB000889CC /* Tag_Spec.swift in Sources */, + 4BEF0F6F21C294A40012B6E1 /* FavoritesMode.swift in Sources */, 4BF2C3E12188B27F00E59F6C /* Injectable.swift in Sources */, A42CE5F820692EDB000889CC /* Friend_Spec.swift in Sources */, A42CE61620692EDB000889CC /* Roster_Spec.swift in Sources */, @@ -15196,7 +15212,6 @@ A42D51B6206A361400EEB952 /* iter.swift in Sources */, 9B967098215151760058E98F /* LeaveVoiceMessageViewController.swift in Sources */, 2603139E20A0A4BA009AC66D /* ChatLanguageSettingsPresenter.swift in Sources */, - 4B1D7DFC2029C37900703228 /* FavoritesItemsFactory.swift in Sources */, 26771CC3212ED109006112B5 /* DBConvertMessage.swift in Sources */, 269848CA200E9F1300590D6F /* StarModels.swift in Sources */, 4B058F0D204EAEC3004C7D9F /* RoomDAO.swift in Sources */, @@ -16005,7 +16020,6 @@ 4B2C502E21B56AB700FBA9B1 /* AddAutoColumnToConvertMessage.swift in Sources */, 6D6731101F29E1F4003E8F8F /* BottomCallView.swift in Sources */, 26245F40204EF58E00C8D3DD /* BaseViewProtocol.swift in Sources */, - 4B1D7DFE2029C41C00703228 /* AboutItemsFactory.swift in Sources */, A4688DFC20652DE30013660D /* StorageChange.swift in Sources */, 5683555B8382F7F37FEE1AF5 /* ProfileWireframe.swift in Sources */, A42D51AC206A361400EEB952 /* Auth.swift in Sources */, @@ -16361,6 +16375,7 @@ A45F59AD2058263F00EAA780 /* RosterDAO.swift in Sources */, FEA655DB2167777E00B44029 /* WalletDetailsWireFrame.swift in Sources */, 4C5EEA13EBC6A8398F08DCD1 /* MainWireframe.swift in Sources */, + 4BEF0F6521C265F40012B6E1 /* CommingSoonWheelItemModel.swift in Sources */, A42D52CF206A53AB00EEB952 /* Person_Spec.swift in Sources */, B77C11E82109254800CCB42E /* InterpretationTypeProtocols.swift in Sources */, B77C11E72109254800CCB42E /* InterpretationTypeViewController.swift in Sources */, @@ -16613,6 +16628,7 @@ 54FFFD58388E2B660C1E5A05 /* MapPresenter.swift in Sources */, A42D52D6206A53AB00EEB952 /* serviceTask_Spec.swift in Sources */, 0AB08BA89A51118248FA3233 /* MapInteractor.swift in Sources */, + 4BEF0F6821C28FC70012B6E1 /* FavoritesMode.swift in Sources */, 8540A331211B34B4007F65AF /* MessageCollectionViewDataSource.swift in Sources */, A48C154220EF76EE002DA994 /* LinkExtension.swift in Sources */, A42D52AD206A53AA00EEB952 /* log_Spec.swift in Sources */, @@ -16692,6 +16708,7 @@ 26E7D04A1FCB8973001C69B7 /* Amazon+FileSync.swift in Sources */, E70938411FBEE488006CCDC6 /* TableDefinitionExtension.swift in Sources */, 850D220020D2E7E20018BBA4 /* SelectionFeedbackInteractive.swift in Sources */, + 4BEF0F6C21C290A10012B6E1 /* FavoritesP2pItemsFactory.swift in Sources */, 43711F24FF65C36730467BFF /* EditPhotoViewController.swift in Sources */, A42D519F206A361400EEB952 /* messageEvent.swift in Sources */, F11DF06520BD96D000F3E005 /* GalleryFilterType.swift in Sources */, @@ -16941,6 +16958,7 @@ 8509AC62206A54420089089B /* ResponseResult.swift in Sources */, 926E70F8DA5310B1129B0277 /* MapSearchPresenter.swift in Sources */, A49CC1D620E4AAC600879D41 /* InputBar+DisplayModeConfig.swift in Sources */, + 4BEF0F6E21C290B10012B6E1 /* FavoritesGroupItemsFactory.swift in Sources */, FEA6560F2167797E00B44029 /* WalletFundingNetworkResponse.swift in Sources */, 5ED473EC698E99DC021E553A /* MapSearchInteractor.swift in Sources */, F1607B2A20B2DE6500BDF60A /* CameraQRPreviewWireframe.swift in Sources */, diff --git a/Nynja/AboutItemsFactory.swift b/Nynja/AboutItemsFactory.swift deleted file mode 100644 index 2fcaefddb..000000000 --- a/Nynja/AboutItemsFactory.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// AboutItemsFactory.swift -// Nynja -// -// Created by Volodymyr Hryhoriev on 2/6/18. -// Copyright © 2018 TecSynt Solutions. All rights reserved. -// - -class AboutItemsFactory: OptionsItemsFactory { - - // MARK: - Second lvl - - override var about: ImageActionItemModel { - let item = super.about - // TODO: state - item.state = .highlighted - return item - } - -} diff --git a/Nynja/ChatItemsFactory.swift b/Nynja/ChatItemsFactory.swift index 14cc2957a..6dd2fffef 100644 --- a/Nynja/ChatItemsFactory.swift +++ b/Nynja/ChatItemsFactory.swift @@ -14,15 +14,6 @@ class ChatItemsFactory: WCBaseItemsFactory { }) return item } - - var payment: ImageActionItemModel { - return ImageActionItemModel( - nameImage: "ic_pay", - navItem: .payment, - action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showPayment(indexPath: indexPath) - }) - } var camera: ImageActionItemModel { let item = ImageActionItemModel(nameImage: "ic_camera", navItem: .camera, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in @@ -107,11 +98,18 @@ class ChatItemsFactory: WCBaseItemsFactory { }) } - var videoGroupCall: ImageFilledItemModel { - return ImageFilledItemModel(nameImage: "ic_video", navItem: .videoCall, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.unavailableFunctionality() -// navigateDelegate?.showVideoGroupCall(indexPath: indexPath) - }) + var videoGroupCall: ImageActionItemModel { + var item: ImageActionItemModel = ImageFilledItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icVideo.name, + navItem: .videoCall) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showVideoGroupCall(indexPath: indexPath) + } + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + + return item } // MARK: - Media diff --git a/Nynja/ChatsItemsFactory.swift b/Nynja/ChatsItemsFactory.swift index d75f095c2..73001416e 100644 --- a/Nynja/ChatsItemsFactory.swift +++ b/Nynja/ChatsItemsFactory.swift @@ -12,7 +12,6 @@ class ChatsItemsFactory: WCBaseItemsFactory { override var actions: ImageActionItemModel { let item = super.actions - // TODO: state item.state = .disabled return item } @@ -21,7 +20,11 @@ class ChatsItemsFactory: WCBaseItemsFactory { // MARK: - Second lvl override var secondLevelItems: ItemModels { - return [family, friends, work, all, recent, starred, new] + if FeatureFlags.isChatGroupingEnabled.value { + return [family, friends, work, all, recent, starred, new] + } else { + return [all, recent, starred, new] + } } @@ -51,13 +54,10 @@ class ChatsItemsFactory: WCBaseItemsFactory { var recent : WheelItemModel { return WheelItemModel() - } - var starred : ImageActionItemModel { - return ImageActionItemModel(nameImage: "ic_starred", navItem: .starred, state: .disabled, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showMessages(indexPath: indexPath) - }) + var starred : WheelItemModel { + return WheelItemModel() } var new: WheelItemModel { diff --git a/Nynja/FavoritesGroupItemsFactory.swift b/Nynja/FavoritesGroupItemsFactory.swift new file mode 100644 index 000000000..61149150a --- /dev/null +++ b/Nynja/FavoritesGroupItemsFactory.swift @@ -0,0 +1,22 @@ +// +// FavoritesGroupItemsFactory.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/13/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +final class FavoritesGroupItemsFactory: GroupChatsItemsFactory { + + override var all: ImageActionItemModel { + let item = super.all + item.state = .normal + return item + } + + override var starred: ImageActionItemModel { + let item = super.starred + item.state = .highlighted + return item + } +} diff --git a/Nynja/FavoritesItemsFactory.swift b/Nynja/FavoritesItemsFactory.swift deleted file mode 100644 index 267d4dfca..000000000 --- a/Nynja/FavoritesItemsFactory.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// FavoritesItemsFactory.swift -// Nynja -// -// Created by Volodymyr Hryhoriev on 2/6/18. -// Copyright © 2018 TecSynt Solutions. All rights reserved. -// - -class FavoritesItemsFactory: WCBaseItemsFactory { - - override var home: ImageActionItemModel { - let item = super.home - // TODO: highlight (clarified by Dima designer). - return item - } - -} diff --git a/Nynja/FavoritesP2pItemsFactory.swift b/Nynja/FavoritesP2pItemsFactory.swift new file mode 100644 index 000000000..08794f376 --- /dev/null +++ b/Nynja/FavoritesP2pItemsFactory.swift @@ -0,0 +1,22 @@ +// +// FavoritesP2pItemsFactory.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/13/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +final class FavoritesP2pItemsFactory: P2pChatsItemsFactory { + + override var all: ImageActionItemModel { + let item = super.all + item.state = .normal + return item + } + + override var starred: ImageActionItemModel { + let item = super.starred + item.state = .highlighted + return item + } +} diff --git a/Nynja/FeatureFlags/FeatureFlags.swift b/Nynja/FeatureFlags/FeatureFlags.swift index 1c746d67d..bc6fdf4fb 100644 --- a/Nynja/FeatureFlags/FeatureFlags.swift +++ b/Nynja/FeatureFlags/FeatureFlags.swift @@ -9,19 +9,42 @@ import Foundation enum FeatureFlags { - static let isRepliesCounterEnabled: Flag = Flag(value: false, reason: "Should be removed for 0.8.0 Live. Blocked by 'Threads' feature.") - static let isMarketplaceEnabled: Flag = Flag(value: false, reason: "Should be removed for 0.8.0 Live.") + static let isRepliesCounterEnabled: Flag = Flag(value: false, reasons: [.notReadyForVersion("0.8.0 Live"), .blockedByFeature("Threads")]) + static let isMarketplaceEnabled: Flag = Flag(value: false, reason: .notReadyForVersion("0.8.0 Live")) + static let isChatGroupingEnabled: Flag = Flag(value: false, reason: .notReadyForVersion("0.8.0 Live")) + static let isTransferActionEnabled: Flag = Flag(value: false, reason: .notReadyForVersion("0.8.0 Live")) + static let shouldShowCommingSoon: Flag = Flag(value: true, reason: .notReadyForVersion("0.8.0 Live")) } extension FeatureFlags { struct Flag { let value: T - let reason: String? - - init(value: T, reason: String? = nil) { + let reasons: [Reason] + + init(value: T, reasons: [Reason] = []) { self.value = value - self.reason = reason + self.reasons = reasons + } + + init (value: T, reason: Reason? = nil) { + self.init( + value: value, + reasons: reason.map { [$0] } ?? []) + } + } + + enum Reason: CustomStringConvertible { + case notReadyForVersion(String) + case blockedByFeature(String) + + var description: String { + switch self { + case let .notReadyForVersion(version): + return "Not ready for version: \(version)." + case let .blockedByFeature(feature): + return "Blocked by '\(feature)' feature." + } } } } @@ -30,12 +53,15 @@ extension FeatureFlags.Flag: CustomStringConvertible where T: CustomStringConver var description: String { var text = """ - Feature Flag: - value = \(value) + ##Feature Flag## + value: \(value) + """ - if let reason = reason { - text.append("\nreason = \(reason)") + if !reasons.isEmpty { + text += "reasons: " + reasons + .map { $0.description } + .joined(separator: " ") } return text diff --git a/Nynja/Generated/ColorsConstants.swift b/Nynja/Generated/ColorsConstants.swift index 713effb5a..98d294073 100644 --- a/Nynja/Generated/ColorsConstants.swift +++ b/Nynja/Generated/ColorsConstants.swift @@ -99,6 +99,8 @@ internal extension SGColor { static let violet = #colorLiteral(red: 0.62352943, green: 0.40784314, blue: 0.65882355, alpha: 1.0) /// 0x45484dff (r: 69, g: 72, b: 77, a: 255) static let wheelBackHighlitedGray = #colorLiteral(red: 0.27058825, green: 0.28235295, blue: 0.3019608, alpha: 1.0) + /// 0xffffff80 (r: 255, g: 255, b: 255, a: 128) + static let wheelItemDisabledColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5019608) /// 0x32353bff (r: 50, g: 53, b: 59, a: 255) static let wheelTopLevelSeparatorColor = #colorLiteral(red: 0.19607843, green: 0.20784314, blue: 0.23137255, alpha: 1.0) /// 0xffffffff (r: 255, g: 255, b: 255, a: 255) diff --git a/Nynja/Generated/LocalizableConstants.swift b/Nynja/Generated/LocalizableConstants.swift index ed2c0ac9b..a3c0e3e1a 100644 --- a/Nynja/Generated/LocalizableConstants.swift +++ b/Nynja/Generated/LocalizableConstants.swift @@ -1330,8 +1330,6 @@ internal extension String { static var wheelDataAndStorage: String { return localizable.tr("Localizable", "wheel_data_and_storage") } /// Invite Friends static var wheelInviteFriends: String { return localizable.tr("Localizable", "wheel_invite_friends") } - /// About - static var wheelItemAbout: String { return localizable.tr("Localizable", "wheel_item_about") } /// Actions static var wheelItemActions: String { return localizable.tr("Localizable", "wheel_item_actions") } /// All diff --git a/Nynja/GroupChatsItemsFactory.swift b/Nynja/GroupChatsItemsFactory.swift index 2a8a6e67e..8a354ff97 100644 --- a/Nynja/GroupChatsItemsFactory.swift +++ b/Nynja/GroupChatsItemsFactory.swift @@ -19,23 +19,36 @@ class GroupChatsItemsFactory: ChatsItemsFactory { // MARK: - Items override var all : ImageActionItemModel { - let item = ImageActionItemModel(nameImage: "ic_list", navItem: .all, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showGroupsList(indexPath: indexPath) - }) + let item = ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icList.name, + navItem: .all) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showGroupsList(indexPath: indexPath) + } item.state = .highlighted return item } override var recent : ImageActionItemModel { - let item = ImageActionItemModel(nameImage: "ic_recents", navItem: .recents, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showRecentGroupChats(indexPath: indexPath) - }) - return item + return ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icRecents.name, + navItem: .recents) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showRecentGroupChats(indexPath: indexPath) + } + } + + override var starred: ImageActionItemModel { + return ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icStarred.name, + navItem: .starred) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showFavorites(indexPath: indexPath, mode: .groups) + } } override var new: WheelItemModel { - return ImageActionItemModel(nameImage: "ic_new_group", navItem: .newGroup, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showCreateGroup(indexPath: indexPath) - }) + return ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icNewGroup.name, + navItem: .newGroup) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showCreateGroup(indexPath: indexPath) + } } } diff --git a/Nynja/HomeItemsFactory.swift b/Nynja/HomeItemsFactory.swift index c4c64d1b6..f77cb62f3 100644 --- a/Nynja/HomeItemsFactory.swift +++ b/Nynja/HomeItemsFactory.swift @@ -42,11 +42,18 @@ class HomeItemsFactory: WCBaseItemsFactory { }) } - var videoCall: ImageFilledItemModel { - return ImageFilledItemModel(nameImage: "ic_video", navItem: .videoCall, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.unavailableFunctionality() -// navigateDelegate?.conferenceVideoCall(indexPath: indexPath) - }) + var videoCall: ImageActionItemModel { + var item: ImageActionItemModel = ImageFilledItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icVideo.name, + navItem: .videoCall) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.conferenceVideoCall(indexPath: indexPath) + } + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + + return item } // MARK: - Edit Profile @@ -69,10 +76,16 @@ class HomeItemsFactory: WCBaseItemsFactory { }) } - var phoneNumber: ImageFilledItemModel { - return ImageFilledItemModel(navItem: .phoneNumber, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.unavailableFunctionality() - }) + var phoneNumber: ImageActionItemModel { + var item: ImageActionItemModel = ImageFilledItemModel(navItem: .phoneNumber) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showChangeNumber(indexPath: indexPath) + } + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + + return item } diff --git a/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/CommingSoonWheelItemModel.swift b/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/CommingSoonWheelItemModel.swift new file mode 100644 index 000000000..0fe9c5a93 --- /dev/null +++ b/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/CommingSoonWheelItemModel.swift @@ -0,0 +1,51 @@ +// +// CommingSoonWheelItemModel.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/13/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +final class CommingSoonWheelItemModel: ImageActionItemModel { + + override var backgroundColor: [WheelItemState : UIColor]! { + get { + return item.backgroundColor + } + } + + override var contentColor : [WheelItemState: UIColor]! { + get { + return [.normal : WheelItemState.disabled.defaultContentColor, + .disabled : WheelItemState.disabled.defaultContentColor, + .selected : WheelItemState.selected.defaultContentColor, + .highlighted : WheelItemState.highlighted.defaultContentColor + ] + } + } + + private var item: ImageActionItemModel! + + init(item: ImageActionItemModel, navigateDelegate: NavigateProtocol?) { + self.item = item + + super.init( + nameImage: item.nameImage, + navItem: item.navItem, + isSelectable: false) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.unavailableFunctionality() + } + } + + override init(model: WheelItemModel) { + super.init(model: model) + + if let _model = model as? CommingSoonWheelItemModel { + self.item = _model.item + } + } + + override func cloned() -> CommingSoonWheelItemModel { + return CommingSoonWheelItemModel(model: self) + } +} diff --git a/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/ImageActionItemModel.swift b/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/ImageActionItemModel.swift index aa558f177..9476a2b64 100644 --- a/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/ImageActionItemModel.swift +++ b/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/ImageActionItemModel.swift @@ -15,7 +15,7 @@ class ImageActionItemModel: ImageWheelItemModel { isSelectable: Bool = true, action: ItemAction? = nil) { - super.init(nameImage: nameImage ?? "", navItem: navItem, action: action ) + super.init(nameImage: nameImage ?? "", navItem: navItem, action: action) self.isUserInteractionEnabled = isUserInteractionEnabled self.isSelectable = isSelectable diff --git a/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/WheelItemModel.swift b/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/WheelItemModel.swift index 76aea0930..efaed3598 100644 --- a/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/WheelItemModel.swift +++ b/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/WheelItemModel.swift @@ -17,10 +17,8 @@ enum WheelItemState : Int { case selected case highlighted - var defaultBackgroundColor : UIColor { + var defaultBackgroundColor: UIColor { switch self { - case .normal: - return UIColor.nynja.clear case .selected: return UIColor.nynja.mainRed case .highlighted: @@ -30,10 +28,10 @@ enum WheelItemState : Int { } } - var defaultContentColor : UIColor { + var defaultContentColor: UIColor { switch self { case .disabled: - return UIColor.nynja.contentDisabledGray + return UIColor.nynja.wheelItemDisabledColor default: return UIColor.nynja.white } @@ -41,12 +39,8 @@ enum WheelItemState : Int { var defaultMarkerColor : UIColor { switch self { - case .normal: - return UIColor.nynja.mainRed case .selected: return UIColor.nynja.darkRed - case .highlighted: - return UIColor.nynja.mainRed default: return UIColor.nynja.mainRed } diff --git a/Nynja/Modules/Favorites/Entities/FavoritesMode.swift b/Nynja/Modules/Favorites/Entities/FavoritesMode.swift new file mode 100644 index 000000000..db0145c45 --- /dev/null +++ b/Nynja/Modules/Favorites/Entities/FavoritesMode.swift @@ -0,0 +1,13 @@ +// +// FavoritesMode.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/13/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +enum FavoritesMode { + case chats + case groups + case home +} diff --git a/Nynja/Modules/Favorites/FavoritesProtocols.swift b/Nynja/Modules/Favorites/FavoritesProtocols.swift index e4c98f353..0fa79db38 100644 --- a/Nynja/Modules/Favorites/FavoritesProtocols.swift +++ b/Nynja/Modules/Favorites/FavoritesProtocols.swift @@ -10,7 +10,7 @@ import UIKit protocol FavoritesWireFrameProtocol: class { - func presentFavorites(navigation: UINavigationController, main: MainWireFrame?) + func presentFavorites(navigation: UINavigationController, main: MainWireFrame?, mode: FavoritesMode) /** * Add here your methods for communication PRESENTER -> WIREFRAME diff --git a/Nynja/Modules/Favorites/Presenter/FavoritesPresenter.swift b/Nynja/Modules/Favorites/Presenter/FavoritesPresenter.swift index f1661ea99..947746787 100644 --- a/Nynja/Modules/Favorites/Presenter/FavoritesPresenter.swift +++ b/Nynja/Modules/Favorites/Presenter/FavoritesPresenter.swift @@ -9,11 +9,19 @@ class FavoritesPresenter: BasePresenter, FavoritesPresenterProtocol, FavoritesInteractorOutputProtocol { override var itemsFactory: WCItemsFactory? { - return FavoritesItemsFactory() + switch mode! { + case .chats: + return FavoritesP2pItemsFactory() + case .groups: + return FavoritesGroupItemsFactory() + case .home: + return WCBaseItemsFactory() + } } weak var view: FavoritesViewProtocol! var wireFrame: FavoritesWireFrameProtocol! + var mode: FavoritesMode! var interactor: FavoritesInteractorInputProtocol! { didSet { @@ -46,5 +54,4 @@ class FavoritesPresenter: BasePresenter, FavoritesPresenterProtocol, FavoritesIn func deleteStar(_ star: Star) { interactor.deleteStar(star) } - } diff --git a/Nynja/Modules/Favorites/WireFrame/FavoritesWireframe.swift b/Nynja/Modules/Favorites/WireFrame/FavoritesWireframe.swift index a296e7d17..ad92e0a0f 100644 --- a/Nynja/Modules/Favorites/WireFrame/FavoritesWireframe.swift +++ b/Nynja/Modules/Favorites/WireFrame/FavoritesWireframe.swift @@ -14,7 +14,7 @@ class FavoritesWireFrame: FavoritesWireFrameProtocol { weak var main: MainWireFrame? - func presentFavorites(navigation: UINavigationController, main: MainWireFrame?) { + func presentFavorites(navigation: UINavigationController, main: MainWireFrame?, mode: FavoritesMode) { let view = FavoritesViewController() let presenter = FavoritesPresenter() let interactor = FavoritesInteractor() @@ -24,13 +24,15 @@ class FavoritesWireFrame: FavoritesWireFrameProtocol { // Connecting view.presenter = presenter + presenter.view = view presenter.wireFrame = self presenter.interactor = interactor + presenter.mode = mode + interactor.presenter = presenter navigation.pushViewController(view as UIViewController, animated: false) - //self.navigation?.viewControllers = [view] } func showChat(with contact: Contact, message: Message? = nil) { diff --git a/Nynja/Modules/Main/MainProtocols.swift b/Nynja/Modules/Main/MainProtocols.swift index 45bcb168e..a6d6b0eae 100644 --- a/Nynja/Modules/Main/MainProtocols.swift +++ b/Nynja/Modules/Main/MainProtocols.swift @@ -80,7 +80,6 @@ protocol MainWireFrameProtocol: class { func showImagePreview(with url: URL, target imageView: UIImageView) func openMapView() func showMySelfChat(contact: Contact) - func showFavorites() func showQRGenerator() func showSelectCountry(_ selectCountryDelegate: SelectCountryDelegate) func showAddContactByUserName() @@ -91,6 +90,8 @@ protocol MainWireFrameProtocol: class { func getStarredLocation() -> [LocationType] func getRecentsMedia() -> [Media] func openMarketplace() + + func showFavorites(mode: FavoritesMode) // Group func showAddParticipants() @@ -188,6 +189,8 @@ protocol MainPresenterProtocol: BasePresenterProtocol { func showEditUsername() func showAddContactByUserName() + func showFavorites(mode: FavoritesMode) + func conferenceVoiceCall() func conferenceVideoCall() diff --git a/Nynja/Modules/Main/Presenter/MainPresenter.swift b/Nynja/Modules/Main/Presenter/MainPresenter.swift index 29f6f801d..76ac6c20c 100644 --- a/Nynja/Modules/Main/Presenter/MainPresenter.swift +++ b/Nynja/Modules/Main/Presenter/MainPresenter.swift @@ -51,7 +51,11 @@ final class MainPresenter: BasePresenter, MainPresenterProtocol, MainInteractorO } func showMarketplace() { - self.wireFrame.openMarketplace() + wireFrame.openMarketplace() + } + + func showFavorites(mode: FavoritesMode) { + wireFrame.showFavorites(mode: mode) } func voiceCall() { diff --git a/Nynja/Modules/Main/View/MainNavigationItem.swift b/Nynja/Modules/Main/View/MainNavigationItem.swift index 7b5d4e7f7..ecc1b17bd 100644 --- a/Nynja/Modules/Main/View/MainNavigationItem.swift +++ b/Nynja/Modules/Main/View/MainNavigationItem.swift @@ -96,7 +96,6 @@ enum MainNavigationItem: String { case activeSessions = "wheel_active_sessions" case privacy = "wheel_privacy" case logOut = "wheel_item_logOut" - case about = "wheel_item_about" case deleteAccount = "wheel_item_deleteAccount" // Location subsection diff --git a/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift b/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift index 39327dbaf..2ba26eea9 100644 --- a/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift +++ b/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift @@ -101,11 +101,13 @@ extension MainViewController: NavigateProtocol { closeWheel(indexPath: indexPath) } - func showMessages(indexPath: IndexPath?) { + func showFavorites(indexPath: IndexPath?, mode: FavoritesMode) { + presenter.showFavorites(mode: mode) closeWheel(indexPath: indexPath) } func showGroupCall(indexPath: IndexPath?) { + // TODO: need to implement } func showVoiceGroupCall(indexPath: IndexPath?) { diff --git a/Nynja/Modules/Main/View/NavigateProtocol.swift b/Nynja/Modules/Main/View/NavigateProtocol.swift index 779032165..30d07f898 100644 --- a/Nynja/Modules/Main/View/NavigateProtocol.swift +++ b/Nynja/Modules/Main/View/NavigateProtocol.swift @@ -33,7 +33,8 @@ protocol SecondLevelNavigateProtocol: class { func showCreateGroup(indexPath: IndexPath?) func showGroupOptions(indexPath: IndexPath?) func showChatOptions(indexPath: IndexPath?) - func showMessages(indexPath: IndexPath?) + + func showFavorites(indexPath: IndexPath?, mode: FavoritesMode) // MARK: - Chat Actions func showLocation(indexPath: IndexPath?) diff --git a/Nynja/Modules/Main/WireFrame/MainWireframe.swift b/Nynja/Modules/Main/WireFrame/MainWireframe.swift index e4c29281a..9c7e2bae9 100644 --- a/Nynja/Modules/Main/WireFrame/MainWireframe.swift +++ b/Nynja/Modules/Main/WireFrame/MainWireframe.swift @@ -351,8 +351,8 @@ final class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelega transitionInfo: transitionInfo) } - func showFavorites() { - FavoritesWireFrame().presentFavorites(navigation: contentNavigation, main: self) + func showFavorites(mode: FavoritesMode) { + FavoritesWireFrame().presentFavorites(navigation: contentNavigation, main: self, mode: mode) } func shareContact(contact: Contact) { diff --git a/Nynja/Modules/Profile/WireFrame/ProfileWireframe.swift b/Nynja/Modules/Profile/WireFrame/ProfileWireframe.swift index 3e001cd9d..81858cee2 100644 --- a/Nynja/Modules/Profile/WireFrame/ProfileWireframe.swift +++ b/Nynja/Modules/Profile/WireFrame/ProfileWireframe.swift @@ -93,6 +93,6 @@ class ProfileWireFrame: ProfileWireFrameProtocol { } func showFavorites() { - main?.showFavorites() + main?.showFavorites(mode: .home) } } diff --git a/Nynja/Modules/Splash/Presenter/SplashPresenter.swift b/Nynja/Modules/Splash/Presenter/SplashPresenter.swift index f001fed03..8a75ead4a 100644 --- a/Nynja/Modules/Splash/Presenter/SplashPresenter.swift +++ b/Nynja/Modules/Splash/Presenter/SplashPresenter.swift @@ -7,10 +7,6 @@ // class SplashPresenter: BasePresenter, SplashPresenterProtocol, SplashInteractorOutputProtocol { - - override var itemsFactory: WCItemsFactory? { - return AboutItemsFactory() - } weak var view: SplashViewProtocol! var interactor: SplashInteractorInputProtocol! diff --git a/Nynja/MySelfItemsFactory.swift b/Nynja/MySelfItemsFactory.swift index 0b22bb6d3..accd59888 100644 --- a/Nynja/MySelfItemsFactory.swift +++ b/Nynja/MySelfItemsFactory.swift @@ -12,7 +12,6 @@ class MySelfItemsFactory: ChatBaseFactory { override var mySelf: ImageActionItemModel { let item = super.mySelf - // TODO: state item.isUserInteractionEnabled = false item.state = .highlighted return item diff --git a/Nynja/OptionsItemsFactory.swift b/Nynja/OptionsItemsFactory.swift index 0b8077a49..45143bc4c 100644 --- a/Nynja/OptionsItemsFactory.swift +++ b/Nynja/OptionsItemsFactory.swift @@ -45,10 +45,14 @@ class OptionsItemsFactory: WCBaseItemsFactory { } var wheelPosition: ImageActionItemModel { - let item = ImageActionItemModel(navItem: .wheelPosition, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in -// navigateDelegate?.showWheelPositionPicker(indexPath: indexPath) - navigateDelegate?.unavailableFunctionality() - }) + var item: ImageActionItemModel = ImageActionItemModel(navItem: .wheelPosition) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showWheelPositionPicker(indexPath: indexPath) + } + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + return item } @@ -67,10 +71,14 @@ class OptionsItemsFactory: WCBaseItemsFactory { } var theme: ImageActionItemModel { - let item = ImageActionItemModel(navItem: .theme, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in -// navigateDelegate?.showThemePicker(indexPath: indexPath) - navigateDelegate?.unavailableFunctionality() - }) + var item: ImageActionItemModel = ImageActionItemModel(navItem: .theme) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showThemePicker(indexPath: indexPath) + } + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + return item } @@ -81,14 +89,6 @@ class OptionsItemsFactory: WCBaseItemsFactory { return item } - var about: ImageActionItemModel { - let item = ImageActionItemModel(navItem: .about, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in -// navigateDelegate?.showAbout(indexPath: indexPath) - navigateDelegate?.unavailableFunctionality() - }) - return item - } - var deleteAccount: ImageActionItemModel { let item = ImageActionItemModel(navItem: .deleteAccount, action: { [weak navigateDelegate] (item, indexPath) in #if DEBUG @@ -104,10 +104,14 @@ class OptionsItemsFactory: WCBaseItemsFactory { } var changeNumber: ImageActionItemModel { - let item = ImageActionItemModel(navItem: .phoneNumber, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in -// navigateDelegate?.showChangeNumber(indexPath: indexPath) - navigateDelegate?.unavailableFunctionality() - }) + var item: ImageActionItemModel = ImageActionItemModel(navItem: .phoneNumber) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showChangeNumber(indexPath: indexPath) + } + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + return item } @@ -126,11 +130,14 @@ class OptionsItemsFactory: WCBaseItemsFactory { } var languageSettings: ImageActionItemModel { - let item = ImageActionItemModel(navItem: .language, isSelectable: false, action: { [weak navigateDelegate] (item, indexPath) in -// navigateDelegate?.showLanguageSettings(indexPath: indexPath) - navigateDelegate?.unavailableFunctionality() - }) + var item: ImageActionItemModel = ImageActionItemModel(navItem: .language) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showLanguageSettings(indexPath: indexPath) + } + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + return item } - } diff --git a/Nynja/P2pChatItemsFactory.swift b/Nynja/P2pChatItemsFactory.swift index 94b964727..88443b8de 100644 --- a/Nynja/P2pChatItemsFactory.swift +++ b/Nynja/P2pChatItemsFactory.swift @@ -32,14 +32,24 @@ class P2pChatItemsFactory: ChatBaseFactory { // MARK: - Second lvl - override var payment: ImageActionItemModel { - let item = super.payment + var payment: ImageActionItemModel { + let item = ImageActionItemModel( + nameImage: "ic_pay", + navItem: .payment, + action: { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showPayment(indexPath: indexPath) + }) item.state = isPaymentEnabled ? .normal : .disabled return item } override var secondLevelItems: ItemModels { - let items = [chatOptions, payment, location, contact, call, media, camera] + let items: ItemModels + if FeatureFlags.isTransferActionEnabled.value { + items = [chatOptions, payment, location, contact, call, media, camera] + } else { + items = [chatOptions, location, contact, call, media, camera] + } items.forEach { item in let action = item.action diff --git a/Nynja/P2pChatsItemsFactory.swift b/Nynja/P2pChatsItemsFactory.swift index 0a9d43d9f..3820af565 100644 --- a/Nynja/P2pChatsItemsFactory.swift +++ b/Nynja/P2pChatsItemsFactory.swift @@ -18,26 +18,38 @@ class P2pChatsItemsFactory: ChatsItemsFactory { // MARK: - Items - + override var all : ImageActionItemModel { - let item = ImageActionItemModel(nameImage: "ic_list", navItem: .all, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showP2PList(indexPath: indexPath) - }) + let item = ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icList.name, + navItem: .all) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showP2PList(indexPath: indexPath) + } item.state = .highlighted return item } override var recent : ImageActionItemModel { - let item = ImageActionItemModel(nameImage: "ic_recents", navItem: .recents, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showRecentP2PChats(indexPath: indexPath) - }) - return item + return ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icRecents.name, + navItem: .recents) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showRecentP2PChats(indexPath: indexPath) + } } - override var new: WheelItemModel { - return ImageActionItemModel(nameImage: "ic_new_chat", navItem: .newChat, action: { [weak navigateDelegate] (item, indexPath) in - navigateDelegate?.showByContacts(indexPath: indexPath) - }) + override var starred: ImageActionItemModel { + return ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icStarred.name, + navItem: .starred) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showFavorites(indexPath: indexPath, mode: .chats) + } } + override var new: WheelItemModel { + return ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icNewChat.name, + navItem: .newChat) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showByContacts(indexPath: indexPath) + } + } } diff --git a/Nynja/Resources/Colors.json b/Nynja/Resources/Colors.json index 050637876..6f8e098e9 100644 --- a/Nynja/Resources/Colors.json +++ b/Nynja/Resources/Colors.json @@ -2,6 +2,7 @@ "mainRed": "#c90010", "wheelBackHighlitedGray": "#45484D", "contentDisabledGray": "#505255", + "wheelItemDisabledColor": "#ffffff80", "backgroundColor": "#272a30", "gray": "#666666", "manatee": "#959699", diff --git a/Nynja/Resources/en.lproj/Localizable.strings b/Nynja/Resources/en.lproj/Localizable.strings index 90e05f045..6566d4338 100644 --- a/Nynja/Resources/en.lproj/Localizable.strings +++ b/Nynja/Resources/en.lproj/Localizable.strings @@ -587,7 +587,6 @@ "wheel_item_byContacts"="By Contacts"; "wheel_item_options"="Settings"; "wheel_item_logOut"="Log Out"; -"wheel_item_about"="About"; "wheel_item_deleteAccount"="Delete Account"; "wheel_item_send"="Send Location"; "wheel_item_language"="Language"; diff --git a/Nynja/WCBaseItemsFactory.swift b/Nynja/WCBaseItemsFactory.swift index 1ecfa4174..a04f0c167 100644 --- a/Nynja/WCBaseItemsFactory.swift +++ b/Nynja/WCBaseItemsFactory.swift @@ -55,13 +55,17 @@ class WCBaseItemsFactory: WCItemsFactory { } var search: ImageActionItemModel { - return ImageActionItemModel( + var item: ImageActionItemModel = ImageActionItemModel( nameImage: UIImage.nynja.Wheel.WheelItems.icSearch.name, - navItem: .search, - state: .disabled, - action: { [weak navigateDelegate] (item, indexPath) in + navItem: .search) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showSearch(indexPath: indexPath) - }) + } + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + + return item } var chats: ImageActionItemModel { @@ -110,13 +114,18 @@ class WCBaseItemsFactory: WCItemsFactory { } var calls: ImageActionItemModel { - return ImageActionItemModel( + var item = ImageActionItemModel( nameImage: UIImage.nynja.Wheel.WheelItems.icCalls.name, navItem: .calls, - state: .normal, action: { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showCalls(indexPath: indexPath) }) + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + + return item } var marketplace: ImageActionItemModel { @@ -128,12 +137,16 @@ class WCBaseItemsFactory: WCItemsFactory { } var channels: ImageActionItemModel { - return ImageActionItemModel( + var item: ImageActionItemModel = ImageActionItemModel( nameImage: UIImage.nynja.Wheel.WheelItems.icChannelInactive.name, - navItem: .channels, isSelectable: false) { [weak navigateDelegate] (item, indexPath) in - // navigateDelegate?.showChannels(indexPath: indexPath) - navigateDelegate?.unavailableFunctionality() + navItem: .channels) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.showChannels(indexPath: indexPath) } + + if FeatureFlags.shouldShowCommingSoon.value { + item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) + } + + return item } - } -- GitLab From ccf1d603d379271f152f704f50dabbc02785fa3d Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Thu, 13 Dec 2018 18:07:06 +0200 Subject: [PATCH 19/41] [NY-6158] Update phone number masks for few countries --- Nynja/Resources/countries.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Nynja/Resources/countries.txt b/Nynja/Resources/countries.txt index e2a2b8d1d..19c54c4af 100644 --- a/Nynja/Resources/countries.txt +++ b/Nynja/Resources/countries.txt @@ -40,17 +40,14 @@ 964;IQ;Iraq;XXX XXX XXXX 963;SY;Syria;XXX XXX XXX 962;JO;Jordan;X XXXX XXXX -961;LB;Lebanon +961;LB;Lebanon; XX XXXXXX 960;MV;Maldives;XXX XXXX 886;TW;Taiwan;XXX XXX XXX -883;GO;International Networks -882;GO;International Networks -881;GO;Global Mobile Satellite 880;BD;Bangladesh 856;LA;Laos;XX XX XXX XXX 855;KH;Cambodia 853;MO;Macau;XXXX XXXX -852;HK;Hong Kong;X XXX XXXX +852;HK;Hong Kong;XXXX XXXX 850;KP;North Korea 692;MH;Marshall Islands 691;FM;Micronesia @@ -101,7 +98,6 @@ 387;BA;Bosnia & Herzegovina;XX XXX XXX 386;SI;Slovenia;XX XXX XXX 385;HR;Croatia -383;XK;Kosovo;XXXX XXXX 382;ME;Montenegro 381;RS;Serbia;XX XXX XXXX 380;UA;Ukraine;XX XXX XX XX @@ -226,7 +222,7 @@ 32;BE;Belgium;XXX XX XX XX 31;NL;Netherlands;X XX XX XX XX 30;GR;Greece;XXX XXX XXXX -27;ZA;South Africa;XX XXX XXXX +27;ZA;South Africa;XXX XXXX 20;EG;Egypt;XX XXXX XXXX 7;KZ;Kazakhstan;XXX XXX XX XX 7;RU;Russian Federation;XXX XXX XXXX -- GitLab From 78fb3f8200219526049b854557a69c8b532c64d3 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Thu, 13 Dec 2018 18:13:09 +0200 Subject: [PATCH 20/41] [NY-6154] Change link within text of SMS when Inviting Friends for go-live --- Nynja-Share/Resources/Info.plist | 4 ++-- Nynja/Generated/LocalizableConstants.swift | 2 +- Nynja/Resources/Info.plist | 4 ++-- Nynja/Resources/en.lproj/Localizable.strings | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist index 3da1ee9b5..fc6a27498 100644 --- a/Nynja-Share/Resources/Info.plist +++ b/Nynja-Share/Resources/Info.plist @@ -19,9 +19,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 1.0 + 0.8.0 CFBundleVersion - 0.5.5.1.RC + 0.8.0.RC Config $(Config) ModelsVersion diff --git a/Nynja/Generated/LocalizableConstants.swift b/Nynja/Generated/LocalizableConstants.swift index a3c0e3e1a..6cbf4aeef 100644 --- a/Nynja/Generated/LocalizableConstants.swift +++ b/Nynja/Generated/LocalizableConstants.swift @@ -824,7 +824,7 @@ internal extension String { static var numberOrRegionEmpty: String { return localizable.tr("Localizable", "number_or_region_empty") } /// NYNJA static var nynja: String { return localizable.tr("Localizable", "nynja") } - /// I'm using the new SuperApp! called NYNJA! It is an amazing productivity and communications app for everyone like you've never seen before https://beta.nynja.net/ + /// I'm using the new SuperApp! called NYNJA! It is an amazing productivity and communications app for everyone like you've never seen before https://www.nynja.io/ static var nynjaShareString: String { return localizable.tr("Localizable", "nynja_share_string") } /// NYNJA Support static var nynjaSupport: String { return localizable.tr("Localizable", "nynjaSupport") } diff --git a/Nynja/Resources/Info.plist b/Nynja/Resources/Info.plist index 0ca491858..db4bc4935 100644 --- a/Nynja/Resources/Info.plist +++ b/Nynja/Resources/Info.plist @@ -21,9 +21,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + 0.8.0 CFBundleVersion - 0.5.5.1.RC + 0.8.0.RC ConfServerAddress $(ConfServerAddress) ConfServerPort diff --git a/Nynja/Resources/en.lproj/Localizable.strings b/Nynja/Resources/en.lproj/Localizable.strings index 6566d4338..21693ae8b 100644 --- a/Nynja/Resources/en.lproj/Localizable.strings +++ b/Nynja/Resources/en.lproj/Localizable.strings @@ -692,7 +692,7 @@ "invite_to_nynja" = "INVITE TO NYNJA"; "no_contacts_selected" = "No contacts selected"; "contacts_were_not_fetched" = "Contacts were not fetched"; -"nynja_share_string" = "I'm using the new SuperApp! called NYNJA! It is an amazing productivity and communications app for everyone like you've never seen before https://beta.nynja.net/"; +"nynja_share_string" = "I'm using the new SuperApp! called NYNJA! It is an amazing productivity and communications app for everyone like you've never seen before https://www.nynja.io/"; "sms_functionality_is_not_available" = "send sms functionality is not available on your phone"; //MARK: Security -- GitLab From f30290a6db08c3d0ea154a83092b68b925f0b17f Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Fri, 14 Dec 2018 15:38:49 +0200 Subject: [PATCH 21/41] [NY-5458] Camera Broken header (#1535) --- .../PhotoPreview/View/PhotoPreviewViewController.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nynja/Modules/Flows/CameraFlow/PhotoPreview/View/PhotoPreviewViewController.swift b/Nynja/Modules/Flows/CameraFlow/PhotoPreview/View/PhotoPreviewViewController.swift index edf8dc83d..7634a1f6c 100644 --- a/Nynja/Modules/Flows/CameraFlow/PhotoPreview/View/PhotoPreviewViewController.swift +++ b/Nynja/Modules/Flows/CameraFlow/PhotoPreview/View/PhotoPreviewViewController.swift @@ -165,7 +165,8 @@ extension PhotoPreviewViewController { title: presenter.getTitle(), navigationHandler: presenter, backButtonImage: UIImage.nynja.icBackNavigation.image)) - + headerView.backgroundColor = UIColor.nynja.darkLight + statusBarBackgroundView.backgroundColor = UIColor.nynja.darkLight headerView.layer.zPosition = 1 makeFooterView(isHaveAdditionalFunctionality: presenter.isHaveAdditionalFunctionality) -- GitLab From d9b48033867d17dc06dca4d39f1d56b8a42e9e71 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Fri, 14 Dec 2018 15:17:23 +0200 Subject: [PATCH 22/41] Fix issue with 'Text on the message bubble isn't displayed fully' which was found on `Chat` screen. (#1534) --- .../ChatCells/BaseChatCell/BaseChatCell.swift | 18 ++++++++++-------- .../BaseChatCell/OponentChatCell.swift | 19 +++++-------------- .../Cells/Views/Base/MessageContentView.swift | 10 ++++++++++ 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift index a376d550b..1002bf48b 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/BaseChatCell.swift @@ -25,8 +25,13 @@ class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, Messag private static let avatarHeight = Constraints.fromImageView.height private static let fromLabelHeight: CGFloat = avatarHeight private static let starHeight = Constraints.starView.height + let radius: CGFloat = 6.0 + var corners: UIRectCorner { + return [.topLeft, .bottomLeft, .topRight] + } + // MARK: - Colors var bgColor: UIColor { return UIColor.nynja.selfBubleColor @@ -223,10 +228,7 @@ class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, Messag // MARK: - Life Cycle override func layoutSubviews() { super.layoutSubviews() - - let corners: UIRectCorner = [.topLeft, .bottomLeft,.topRight] bubble.roundCorners(corners, radius: radius) - messageView.roundCorners(corners, radius: radius) } override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) { @@ -236,7 +238,7 @@ class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, Messag } transform = layoutAttributes.isReversed ? CGAffineTransform(rotationAngle: .pi) : .identity } - + override func prepareForReuse() { super.prepareForReuse() model?.resetHandlers() @@ -286,9 +288,9 @@ class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, Messag starImageView.isHidden = model.starID == nil - bubble.snp.remakeConstraints(self.bubbleConstraints()) - bubble.layoutSubviews() - self.layoutSubviews() + bubble.snp.remakeConstraints(bubbleConstraints()) + bubble.layoutIfNeeded() + layoutIfNeeded() } private func baseSetup() { @@ -486,7 +488,7 @@ class BaseChatCell: UICollectionViewCell, MessageBaseImageViewDataSource, Messag } func corners(_ messageContent: MessageContentProtocol) -> CornerRadius { - return ([.topLeft, .bottomLeft, .topRight], radius) + return (corners, radius) } //MARK: Utils diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift index f9e84b9c7..10216a499 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/ChatCells/BaseChatCell/OponentChatCell.swift @@ -26,6 +26,11 @@ class OponentChatCell: BaseChatCell { private static let topInset = OpponentConstraints.messageView.topInset + override var corners: UIRectCorner { + return [.topRight, .bottomRight, .bottomLeft] + } + + // MARK: - Layout enum OpponentConstraints { @@ -142,18 +147,4 @@ class OponentChatCell: BaseChatCell { return size } - - // MARK: Layout - override func layoutSubviews() { - super.layoutSubviews() - let corners: UIRectCorner = [.topRight, .bottomRight, .bottomLeft] - bubble.roundCorners(corners, radius: radius) - messageView.roundCorners(corners, radius: radius) - } - - // MARK: - MessageContentAppearance - override func corners(_ messageContent: MessageContentProtocol) -> CornerRadius { - return ([.bottomRight, .bottomLeft, .topRight], radius) - } - } diff --git a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageContentView.swift b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageContentView.swift index 60ec6e6c1..cd73fb3f8 100644 --- a/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageContentView.swift +++ b/Nynja/Modules/Message/View/Views/CollectionView/Cells/Views/Base/MessageContentView.swift @@ -36,6 +36,16 @@ class MessageContentView: BaseView, BubbleInjectible { self.contentAppearance = contentAppearance } + override func layoutSubviews() { + super.layoutSubviews() + + guard let (corners, radius) = contentAppearance?.corners(self) else { + return + } + + roundCorners(corners, radius: radius) + } + // MARK: - BubbleInjectible -- GitLab From 8a205440921419d13d6cb5a9f547b115cf368e01 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Fri, 14 Dec 2018 18:32:26 +0200 Subject: [PATCH 23/41] [NY-6056] Login > Returning back from background pops up alert again --- .../Auth/Login/Interactor/LoginInteractor.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Nynja/Modules/Auth/Login/Interactor/LoginInteractor.swift b/Nynja/Modules/Auth/Login/Interactor/LoginInteractor.swift index 608c4b93d..7215b961b 100644 --- a/Nynja/Modules/Auth/Login/Interactor/LoginInteractor.swift +++ b/Nynja/Modules/Auth/Login/Interactor/LoginInteractor.swift @@ -12,12 +12,19 @@ class LoginInteractor: BaseInteractor, LoginInteractorInputProtocol, IoHandlerDe private var mqttService: MQTTService! - private var phone: String? + private var phone: String? { + didSet { + if oldValue != phone { + accessDenied = false + } + } + } private var isBadProtocolVersion: Bool = false private var connectionTimerHandler: TimerHandler? + private var accessDenied: Bool = false // MARK: - Configure @@ -51,6 +58,7 @@ class LoginInteractor: BaseInteractor, LoginInteractorInputProtocol, IoHandlerDe } func numberNotAllowed() { + accessDenied = true presenter.numberNotAllowed() } @@ -67,7 +75,7 @@ class LoginInteractor: BaseInteractor, LoginInteractorInputProtocol, IoHandlerDe func mqttServiceDidConnect(_ mqttService: MQTTService) { invalidateConnectionTimer() - if let phone = phone { + if let phone = phone, !accessDenied { login(phone: phone) } } -- GitLab From 878e03133ce6351de82896cc63b400b8f1a4de3d Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Fri, 14 Dec 2018 18:43:50 +0200 Subject: [PATCH 24/41] [NY-5252] Calls level (#1537) --- Nynja.xcodeproj/project.pbxproj | 4 ++ Nynja/CallsItemsFactory.swift | 68 +++++++++++++++++++ Nynja/ChatItemsFactory.swift | 10 +-- Nynja/Generated/LocalizableConstants.swift | 6 ++ Nynja/HomeItemsFactory.swift | 20 ++---- .../CommingSoonWheelItemModel.swift | 12 ++++ .../Main/View/MainNavigationItem.swift | 7 ++ .../MainViewController+NavigateProtocol.swift | 7 +- .../Modules/Main/View/NavigateProtocol.swift | 3 + Nynja/OptionsItemsFactory.swift | 38 ++--------- Nynja/Resources/en.lproj/Localizable.strings | 3 + Nynja/WCBaseItemsFactory.swift | 33 ++------- 12 files changed, 129 insertions(+), 82 deletions(-) create mode 100644 Nynja/CallsItemsFactory.swift diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 7d9a7cbcf..6d687306d 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -668,6 +668,7 @@ 4B752B5E2163A4F900E852B9 /* GApiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E8513A2021E96E007DC792 /* GApiResponse.swift */; }; 4B752B612163A5C900E852B9 /* DescExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B752B602163A5C900E852B9 /* DescExtension.swift */; }; 4B752B652163A64300E852B9 /* Desc+Place.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B752B642163A64300E852B9 /* Desc+Place.swift */; }; + 4B78E30421C3F7BB001B2F0E /* CallsItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B78E30321C3F7BB001B2F0E /* CallsItemsFactory.swift */; }; 4B7B81C62044790700C2EFCF /* TimeZoneLocal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7B81C52044790700C2EFCF /* TimeZoneLocal.swift */; }; 4B7C73F0215A5509007924DB /* SMSCodeProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C73E9215A5508007924DB /* SMSCodeProvider.swift */; }; 4B7C73F1215A5509007924DB /* SMSCodeProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C73EA215A5508007924DB /* SMSCodeProviding.swift */; }; @@ -2983,6 +2984,7 @@ 4B752B502163A04800E852B9 /* Array+BaseChatCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+BaseChatCellModel.swift"; sourceTree = ""; }; 4B752B602163A5C900E852B9 /* DescExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescExtension.swift; sourceTree = ""; }; 4B752B642163A64300E852B9 /* Desc+Place.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Desc+Place.swift"; sourceTree = ""; }; + 4B78E30321C3F7BB001B2F0E /* CallsItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallsItemsFactory.swift; sourceTree = ""; }; 4B7B81C52044790700C2EFCF /* TimeZoneLocal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeZoneLocal.swift; sourceTree = ""; }; 4B7C73E9215A5508007924DB /* SMSCodeProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMSCodeProvider.swift; sourceTree = ""; }; 4B7C73EA215A5508007924DB /* SMSCodeProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMSCodeProviding.swift; sourceTree = ""; }; @@ -6686,6 +6688,7 @@ 4B06D30B2028A25D003B275B /* HomeItemsFactory.swift */, 4B1D7DFF2029C4A900703228 /* Options */, 4B1D7E062029D00000703228 /* OtherUserProfileItemsFactory.swift */, + 4B78E30321C3F7BB001B2F0E /* CallsItemsFactory.swift */, ); name = Factory; sourceTree = ""; @@ -16039,6 +16042,7 @@ E735853D1F6C2705003354B5 /* Geometry.swift in Sources */, 85B0013421272694000C89FE /* MessageInteractor+History.swift in Sources */, 4B030F2F2195BFF300F293B7 /* AuthHandler.swift in Sources */, + 4B78E30421C3F7BB001B2F0E /* CallsItemsFactory.swift in Sources */, F119E67720D27E990043A532 /* ImagePreviewCVCell.swift in Sources */, 260313A720A0A4BA009AC66D /* ChatLanguageSettingsTableDataSource.swift in Sources */, 260313A520A0A4BA009AC66D /* BaseCell.swift in Sources */, diff --git a/Nynja/CallsItemsFactory.swift b/Nynja/CallsItemsFactory.swift new file mode 100644 index 000000000..6b1f90eb4 --- /dev/null +++ b/Nynja/CallsItemsFactory.swift @@ -0,0 +1,68 @@ +// +// CallsItemsFactory.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/14/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +final class CallsItemsFactory: WCBaseItemsFactory { + + // MARK: - First lvl + + override var calls: ImageActionItemModel { + let item = super.calls + item.state = .highlighted + return item + } + + + // MARK: - Second lvl + + override var secondLevelItems: ItemModels { + let voiceCall = self.voiceCall + voiceCall.subitems = [dialNumber, callContact] + + return [callHistory, videoCall, voiceCall] + } + + + // MARK: - Items + + var voiceCall: ImageActionItemModel { + return ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icCalls.name, + navItem: .voiceCall) { [weak navigateDelegate] (item, indexPath) in + navigateDelegate?.expandVoiceCall(indexPath: indexPath) + } + } + + var callContact: ImageActionItemModel { + return ImageFilledItemModel(navItem: .callContact) { [weak navigateDelegate] (item, indexPath) in + // TODO: need to implement + }.asCommingSoon(navigateDelegate: navigateDelegate) + } + + var dialNumber: ImageActionItemModel { + return ImageFilledItemModel(navItem: .dialNumber) { [weak navigateDelegate] (item, indexPath) in + // TODO: need to implement + }.asCommingSoon(navigateDelegate: navigateDelegate) + } + + var videoCall: ImageActionItemModel { + return ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icVideo.name, + navItem: .videoCall) { [weak navigateDelegate] (item, indexPath) in + // TODO: need to implement + }.asCommingSoon(navigateDelegate: navigateDelegate) + } + + var callHistory: ImageActionItemModel { + return ImageActionItemModel( + nameImage: UIImage.nynja.Wheel.WheelItems.icHistory.name, + navItem: .callHistory) { [weak navigateDelegate] (item, indexPath) in + // TODO: need to implement + }.asCommingSoon(navigateDelegate: navigateDelegate) + } +} + diff --git a/Nynja/ChatItemsFactory.swift b/Nynja/ChatItemsFactory.swift index 6dd2fffef..9ae090895 100644 --- a/Nynja/ChatItemsFactory.swift +++ b/Nynja/ChatItemsFactory.swift @@ -99,17 +99,11 @@ class ChatItemsFactory: WCBaseItemsFactory { } var videoGroupCall: ImageActionItemModel { - var item: ImageActionItemModel = ImageFilledItemModel( + return ImageFilledItemModel( nameImage: UIImage.nynja.Wheel.WheelItems.icVideo.name, navItem: .videoCall) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showVideoGroupCall(indexPath: indexPath) - } - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item + }.asCommingSoon(navigateDelegate: navigateDelegate) } // MARK: - Media diff --git a/Nynja/Generated/LocalizableConstants.swift b/Nynja/Generated/LocalizableConstants.swift index 6cbf4aeef..17335aa69 100644 --- a/Nynja/Generated/LocalizableConstants.swift +++ b/Nynja/Generated/LocalizableConstants.swift @@ -1346,6 +1346,10 @@ internal extension String { static var wheelItemByUsername: String { return localizable.tr("Localizable", "wheel_item_byUsername") } /// Call static var wheelItemCall: String { return localizable.tr("Localizable", "wheel_item_call") } + /// Call Сontact + static var wheelItemCallContact: String { return localizable.tr("Localizable", "wheel_item_call_contact") } + /// Call History + static var wheelItemCallHistory: String { return localizable.tr("Localizable", "wheel_item_call_history") } /// Calls static var wheelItemCalls: String { return localizable.tr("Localizable", "wheel_item_calls") } /// Camera @@ -1364,6 +1368,8 @@ internal extension String { static var wheelItemContacts: String { return localizable.tr("Localizable", "wheel_item_contacts") } /// Delete Account static var wheelItemDeleteAccount: String { return localizable.tr("Localizable", "wheel_item_deleteAccount") } + /// Dial Number + static var wheelItemDialNumber: String { return localizable.tr("Localizable", "wheel_item_dial_number") } /// DONE static var wheelItemDone: String { return localizable.tr("Localizable", "wheel_item_done") } /// Edit Profile diff --git a/Nynja/HomeItemsFactory.swift b/Nynja/HomeItemsFactory.swift index f77cb62f3..b9aad501d 100644 --- a/Nynja/HomeItemsFactory.swift +++ b/Nynja/HomeItemsFactory.swift @@ -43,17 +43,11 @@ class HomeItemsFactory: WCBaseItemsFactory { } var videoCall: ImageActionItemModel { - var item: ImageActionItemModel = ImageFilledItemModel( + return ImageFilledItemModel( nameImage: UIImage.nynja.Wheel.WheelItems.icVideo.name, navItem: .videoCall) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.conferenceVideoCall(indexPath: indexPath) - } - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item + }.asCommingSoon(navigateDelegate: navigateDelegate) } // MARK: - Edit Profile @@ -77,15 +71,9 @@ class HomeItemsFactory: WCBaseItemsFactory { } var phoneNumber: ImageActionItemModel { - var item: ImageActionItemModel = ImageFilledItemModel(navItem: .phoneNumber) { [weak navigateDelegate] (item, indexPath) in + return ImageFilledItemModel(navItem: .phoneNumber) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showChangeNumber(indexPath: indexPath) - } - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item + }.asCommingSoon(navigateDelegate: navigateDelegate) } diff --git a/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/CommingSoonWheelItemModel.swift b/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/CommingSoonWheelItemModel.swift index 0fe9c5a93..5633f7232 100644 --- a/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/CommingSoonWheelItemModel.swift +++ b/Nynja/Library/UI/WheelContainer/Wheel/ItemModels/CommingSoonWheelItemModel.swift @@ -49,3 +49,15 @@ final class CommingSoonWheelItemModel: ImageActionItemModel { return CommingSoonWheelItemModel(model: self) } } + + +extension ImageActionItemModel { + + func asCommingSoon(navigateDelegate: NavigateProtocol?) -> ImageActionItemModel { + if FeatureFlags.shouldShowCommingSoon.value { + return CommingSoonWheelItemModel(item: self, navigateDelegate: navigateDelegate) + } + + return self + } +} diff --git a/Nynja/Modules/Main/View/MainNavigationItem.swift b/Nynja/Modules/Main/View/MainNavigationItem.swift index ecc1b17bd..cb92fed3a 100644 --- a/Nynja/Modules/Main/View/MainNavigationItem.swift +++ b/Nynja/Modules/Main/View/MainNavigationItem.swift @@ -47,6 +47,11 @@ enum MainNavigationItem: String { case payment = "wheel_item_transfer" case help = "wheel_item_help" + // Calls section + case callContact = "wheel_item_call_contact" + case dialNumber = "wheel_item_dial_number" + case callHistory = "wheel_item_call_history" + // Chats section case starred = "wheel_item_starred" case recents = "wheel_item_recents" @@ -119,6 +124,8 @@ enum MainNavigationItem: String { anyClass = ContactsItemsFactory.self case .options: anyClass = OptionsItemsFactory.self + case .calls: + anyClass = CallsItemsFactory.self default: anyClass = nil } diff --git a/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift b/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift index 2ba26eea9..f41cbf577 100644 --- a/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift +++ b/Nynja/Modules/Main/View/MainViewController+NavigateProtocol.swift @@ -41,7 +41,7 @@ extension MainViewController: NavigateProtocol { } func showCalls(indexPath: IndexPath?) { - // TODO: will be implemented in future with 'Calls' module. + openNextLevel(indexPath: indexPath) } func showMarketplace(indexPath: IndexPath?) { @@ -336,4 +336,9 @@ extension MainViewController: NavigateProtocol { closeWheel(indexPath: indexPath) } + // MARK: - Calls + + func expandVoiceCall(indexPath: IndexPath?) { + expand(by: indexPath) + } } diff --git a/Nynja/Modules/Main/View/NavigateProtocol.swift b/Nynja/Modules/Main/View/NavigateProtocol.swift index 30d07f898..47047467e 100644 --- a/Nynja/Modules/Main/View/NavigateProtocol.swift +++ b/Nynja/Modules/Main/View/NavigateProtocol.swift @@ -105,6 +105,9 @@ protocol SecondLevelNavigateProtocol: class { func showRecentChannels(indexPath: IndexPath?) func showMyChannels(indexPath: IndexPath?) func showNewChannel(indexPath: IndexPath?) + + // MARK: - Calls + func expandVoiceCall(indexPath: IndexPath?) } protocol ThirdLevelNavigateProtocol: class { diff --git a/Nynja/OptionsItemsFactory.swift b/Nynja/OptionsItemsFactory.swift index 45143bc4c..f927a11bd 100644 --- a/Nynja/OptionsItemsFactory.swift +++ b/Nynja/OptionsItemsFactory.swift @@ -45,15 +45,9 @@ class OptionsItemsFactory: WCBaseItemsFactory { } var wheelPosition: ImageActionItemModel { - var item: ImageActionItemModel = ImageActionItemModel(navItem: .wheelPosition) { [weak navigateDelegate] (item, indexPath) in + return ImageActionItemModel(navItem: .wheelPosition) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showWheelPositionPicker(indexPath: indexPath) - } - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item + }.asCommingSoon(navigateDelegate: navigateDelegate) } var buildNumber: ImageActionItemModel { @@ -71,15 +65,9 @@ class OptionsItemsFactory: WCBaseItemsFactory { } var theme: ImageActionItemModel { - var item: ImageActionItemModel = ImageActionItemModel(navItem: .theme) { [weak navigateDelegate] (item, indexPath) in + return ImageActionItemModel(navItem: .theme) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showThemePicker(indexPath: indexPath) - } - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item + }.asCommingSoon(navigateDelegate: navigateDelegate) } var privacy: ImageActionItemModel { @@ -104,15 +92,9 @@ class OptionsItemsFactory: WCBaseItemsFactory { } var changeNumber: ImageActionItemModel { - var item: ImageActionItemModel = ImageActionItemModel(navItem: .phoneNumber) { [weak navigateDelegate] (item, indexPath) in + return ImageActionItemModel(navItem: .phoneNumber) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showChangeNumber(indexPath: indexPath) } - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item } var dataAndStorage: ImageActionItemModel { @@ -130,14 +112,8 @@ class OptionsItemsFactory: WCBaseItemsFactory { } var languageSettings: ImageActionItemModel { - var item: ImageActionItemModel = ImageActionItemModel(navItem: .language) { [weak navigateDelegate] (item, indexPath) in + return ImageActionItemModel(navItem: .language) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showLanguageSettings(indexPath: indexPath) - } - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item + }.asCommingSoon(navigateDelegate: navigateDelegate) } } diff --git a/Nynja/Resources/en.lproj/Localizable.strings b/Nynja/Resources/en.lproj/Localizable.strings index 21693ae8b..7a70d914e 100644 --- a/Nynja/Resources/en.lproj/Localizable.strings +++ b/Nynja/Resources/en.lproj/Localizable.strings @@ -600,6 +600,9 @@ "wheel_invite_friends"="Invite Friends"; "wheel_item_transfer" = "Transfer"; "wheel_item_help" = "Help & Feedback"; +"wheel_item_call_contact" = "Call Сontact"; +"wheel_item_dial_number" = "Dial Number"; +"wheel_item_call_history" = "Call History"; // MARK: Main "main_undefined"="Undefined"; diff --git a/Nynja/WCBaseItemsFactory.swift b/Nynja/WCBaseItemsFactory.swift index a04f0c167..fdb1e97f2 100644 --- a/Nynja/WCBaseItemsFactory.swift +++ b/Nynja/WCBaseItemsFactory.swift @@ -55,17 +55,11 @@ class WCBaseItemsFactory: WCItemsFactory { } var search: ImageActionItemModel { - var item: ImageActionItemModel = ImageActionItemModel( + return ImageActionItemModel( nameImage: UIImage.nynja.Wheel.WheelItems.icSearch.name, navItem: .search) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showSearch(indexPath: indexPath) - } - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item + }.asCommingSoon(navigateDelegate: navigateDelegate) } var chats: ImageActionItemModel { @@ -114,18 +108,11 @@ class WCBaseItemsFactory: WCItemsFactory { } var calls: ImageActionItemModel { - var item = ImageActionItemModel( + return ImageActionItemModel( nameImage: UIImage.nynja.Wheel.WheelItems.icCalls.name, - navItem: .calls, - action: { [weak navigateDelegate] (item, indexPath) in + navItem: .calls) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showCalls(indexPath: indexPath) - }) - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item + }.asCommingSoon(navigateDelegate: navigateDelegate) } var marketplace: ImageActionItemModel { @@ -137,16 +124,10 @@ class WCBaseItemsFactory: WCItemsFactory { } var channels: ImageActionItemModel { - var item: ImageActionItemModel = ImageActionItemModel( + return ImageActionItemModel( nameImage: UIImage.nynja.Wheel.WheelItems.icChannelInactive.name, navItem: .channels) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showChannels(indexPath: indexPath) - } - - if FeatureFlags.shouldShowCommingSoon.value { - item = CommingSoonWheelItemModel(item: item, navigateDelegate: navigateDelegate) - } - - return item + }.asCommingSoon(navigateDelegate: navigateDelegate) } } -- GitLab From 99d41d7e591b243a748ab276c4f78bf933f95343 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Fri, 14 Dec 2018 20:26:31 +0200 Subject: [PATCH 25/41] [NY-5436] Wheel stay under keyboard on the authorization screen --- .../Modules/Auth/Login/View/LoginViewController.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Nynja/Modules/Auth/Login/View/LoginViewController.swift b/Nynja/Modules/Auth/Login/View/LoginViewController.swift index d0ac878f7..cb3357cca 100644 --- a/Nynja/Modules/Auth/Login/View/LoginViewController.swift +++ b/Nynja/Modules/Auth/Login/View/LoginViewController.swift @@ -19,6 +19,8 @@ class LoginViewController: BaseVC, LoginViewProtocol, LoginWheelContainerViewPro let util = NBPhoneNumberUtil.sharedInstance() var isContainerShown = false + private var keyboardEnabled = false + /// Responsible for items for wheel container var wheelContainerDS: LoginWheelContainerDataSource! @@ -92,9 +94,10 @@ class LoginViewController: BaseVC, LoginViewProtocol, LoginWheelContainerViewPro override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - - showContainer() - isContainerShown = true + if !keyboardEnabled { + showContainer() + isContainerShown = true + } } // MARK: Configure views @@ -141,6 +144,7 @@ class LoginViewController: BaseVC, LoginViewProtocol, LoginWheelContainerViewPro self.loginView.nextButton.snp.updateConstraints({ (make) in make.bottom.equalTo(self.loginView).inset(bottomInset) }) + keyboardEnabled = false } else { if isContainerShown { hideContainer() @@ -150,6 +154,7 @@ class LoginViewController: BaseVC, LoginViewProtocol, LoginWheelContainerViewPro self.loginView.nextButton.snp.updateConstraints({ (make) in make.bottom.equalTo(self.loginView).inset(endFrame.height + bottomInset) }) + keyboardEnabled = true } } -- GitLab From 7060e3988c5410e6cf20d6efd6deca88ce2f8f85 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Sat, 15 Dec 2018 00:37:36 +0200 Subject: [PATCH 26/41] [NY-5449] Contact Profile should be opened when Contact is tapped on Contact History screen --- Nynja/Modules/History/Presenter/HistoryPresenter.swift | 2 +- Nynja/Modules/History/WireFrame/HistoryWireframe.swift | 2 +- Nynja/Modules/Profile/View/ProfileViewController.swift | 4 ++++ .../Profile/View/TableView/ProfileTableViewDelegate.swift | 2 +- .../Profile/View/TableView/ProfileViewSectionDelegate.swift | 1 + 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Nynja/Modules/History/Presenter/HistoryPresenter.swift b/Nynja/Modules/History/Presenter/HistoryPresenter.swift index 9e56816a1..3d6cf8a71 100644 --- a/Nynja/Modules/History/Presenter/HistoryPresenter.swift +++ b/Nynja/Modules/History/Presenter/HistoryPresenter.swift @@ -37,7 +37,7 @@ class HistoryPresenter: BasePresenter, HistoryPresenterProtocol, HistoryInteract } func showChat(contact: Contact) { - guard [.friend, .ban, .banned].contains(contact.originalStatus) else { + guard [.authorization,.friend, .ban, .banned].contains(contact.originalStatus) else { return } wireFrame.showChat(contact: contact) diff --git a/Nynja/Modules/History/WireFrame/HistoryWireframe.swift b/Nynja/Modules/History/WireFrame/HistoryWireframe.swift index e61768b69..3c0218444 100644 --- a/Nynja/Modules/History/WireFrame/HistoryWireframe.swift +++ b/Nynja/Modules/History/WireFrame/HistoryWireframe.swift @@ -34,7 +34,7 @@ class HistoryWireFrame: HistoryWireFrameProtocol { } func showChat(contact: Contact) { - mainWF?.showChat(contact) + mainWF?.showAddContact(contact: contact) } func showInviteFriends() { diff --git a/Nynja/Modules/Profile/View/ProfileViewController.swift b/Nynja/Modules/Profile/View/ProfileViewController.swift index d5459dd4d..33c471146 100644 --- a/Nynja/Modules/Profile/View/ProfileViewController.swift +++ b/Nynja/Modules/Profile/View/ProfileViewController.swift @@ -208,6 +208,10 @@ extension ProfileViewController: ProfileViewSectionDelegate { func contactTapped(_ contact: Contact) { presenter.showChat(with: contact) } + + func historyAvatarTapped(_ contact: Contact) { + presenter.showProfile(contact) + } func scheduledMessageTapped(_ jobId: Int64) { presenter.showScheduledMessage(with: jobId) diff --git a/Nynja/Modules/Profile/View/TableView/ProfileTableViewDelegate.swift b/Nynja/Modules/Profile/View/TableView/ProfileTableViewDelegate.swift index 9f9bb38ca..3feabc3d5 100644 --- a/Nynja/Modules/Profile/View/TableView/ProfileTableViewDelegate.swift +++ b/Nynja/Modules/Profile/View/TableView/ProfileTableViewDelegate.swift @@ -37,7 +37,7 @@ class ProfileTableViewDelegate: NSObject, UITableViewDelegate { if sectionType == .unreadMessages { sectionDelegate?.messageTapped(contact) } else if sectionType == .contactRequests { - sectionDelegate?.contactTapped(contact) + sectionDelegate?.historyAvatarTapped(contact) } } else if let scheduledMessage = model as? ScheduledMessage, sectionModel.profileSection == .scheduledMessages, let id = scheduledMessage.job?.id { sectionDelegate?.scheduledMessageTapped(id) diff --git a/Nynja/Modules/Profile/View/TableView/ProfileViewSectionDelegate.swift b/Nynja/Modules/Profile/View/TableView/ProfileViewSectionDelegate.swift index 686c4097e..95e8d3468 100644 --- a/Nynja/Modules/Profile/View/TableView/ProfileViewSectionDelegate.swift +++ b/Nynja/Modules/Profile/View/TableView/ProfileViewSectionDelegate.swift @@ -12,6 +12,7 @@ protocol ProfileViewSectionDelegate: class { func groupMessageTapped(_ room: Room) func contactTapped(_ contact: Contact) func scheduledMessageTapped(_ jobId: Int64) + func historyAvatarTapped(_ contact: Contact) func showMoreTapped(for profileSection: ProfileSection) func actionTapped(_ action: ProfileAction) -- GitLab From b7357f4723af7afe83f7f36d449a9a33cbb358c1 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Sat, 15 Dec 2018 00:59:10 +0200 Subject: [PATCH 27/41] Vova ATTENTION!!! --- Nynja/OptionsItemsFactory.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nynja/OptionsItemsFactory.swift b/Nynja/OptionsItemsFactory.swift index f927a11bd..31c56be55 100644 --- a/Nynja/OptionsItemsFactory.swift +++ b/Nynja/OptionsItemsFactory.swift @@ -94,7 +94,7 @@ class OptionsItemsFactory: WCBaseItemsFactory { var changeNumber: ImageActionItemModel { return ImageActionItemModel(navItem: .phoneNumber) { [weak navigateDelegate] (item, indexPath) in navigateDelegate?.showChangeNumber(indexPath: indexPath) - } + }.asCommingSoon(navigateDelegate: navigateDelegate) } var dataAndStorage: ImageActionItemModel { -- GitLab From d113d1d4fe38305367671bef8f760172ac32a6e0 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Sat, 15 Dec 2018 02:07:16 +0200 Subject: [PATCH 28/41] [NY-5517] Fix issue with `alert "Please enter at least 1 symbol" should appear in case spaces are entered as Group alias`. --- .../UI/TextInput/Material/Base/MaterialTextInput.swift | 4 ++++ .../MyGroupAlias/Presenter/MyGroupAliasPresenter.swift | 2 +- Nynja/ValidatorFactory/ValidatorFactoryImpl.swift | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Nynja/Library/UI/TextInput/Material/Base/MaterialTextInput.swift b/Nynja/Library/UI/TextInput/Material/Base/MaterialTextInput.swift index 039bda012..2e1b381fb 100644 --- a/Nynja/Library/UI/TextInput/Material/Base/MaterialTextInput.swift +++ b/Nynja/Library/UI/TextInput/Material/Base/MaterialTextInput.swift @@ -148,6 +148,10 @@ extension MaterialTextInput { } func validate(text: String) { + guard !validators.isEmpty else { + return + } + let infos = validators .map { $0.validate(text: text) } .compactMap { $0 } diff --git a/Nynja/Modules/Flows/CreateGroupFlow/MyGroupAlias/Presenter/MyGroupAliasPresenter.swift b/Nynja/Modules/Flows/CreateGroupFlow/MyGroupAlias/Presenter/MyGroupAliasPresenter.swift index 2d7b3b7d8..2911c8a9c 100644 --- a/Nynja/Modules/Flows/CreateGroupFlow/MyGroupAlias/Presenter/MyGroupAliasPresenter.swift +++ b/Nynja/Modules/Flows/CreateGroupFlow/MyGroupAlias/Presenter/MyGroupAliasPresenter.swift @@ -69,7 +69,7 @@ class MyGroupAliasPresenter: BasePresenter, GroupInputPresenterProtocol, Initial // MARK: - MyGroupAliasPresenterProtocol var validators: [MTIValidator] { - return validatorFactory.makeGroupInputValidators(maxLength: 65, emptyWarningMessage: String.localizable.aliasEmptyMessage) + return validatorFactory.makeGroupInputValidators(maxLength: 65, emptyWarningMessage: String.localizable.groupNameEmptyMessage) } func cancel() { diff --git a/Nynja/ValidatorFactory/ValidatorFactoryImpl.swift b/Nynja/ValidatorFactory/ValidatorFactoryImpl.swift index e23ec5356..2a22b9b40 100644 --- a/Nynja/ValidatorFactory/ValidatorFactoryImpl.swift +++ b/Nynja/ValidatorFactory/ValidatorFactoryImpl.swift @@ -13,6 +13,7 @@ class ValidatorFactoryImpl: ValidatorFactory { return [ LengthValidator(length: .max(maxLength, String.localizable.channelMaxLengthWarning)), ClosureValidator { text in + let text = text.trimmed() if text == "" { return InputInfo(text: message, kind: .warning) } -- GitLab From 6805df84e9cd5ff5924cc558a94f9b1ac55a0b5d Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Sat, 15 Dec 2018 15:16:29 +0200 Subject: [PATCH 29/41] 0.8.0 (0.8.1) --- Nynja-Share/Resources/Info.plist | 2 +- Nynja.xcodeproj/project.pbxproj | 24 ++++++++++++++++-------- Nynja/Resources/Info.plist | 2 +- Nynja/Resources/ReleaseConfig.xcconfig | 4 ++-- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist index fc6a27498..f6eb18bfa 100644 --- a/Nynja-Share/Resources/Info.plist +++ b/Nynja-Share/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 0.8.0 CFBundleVersion - 0.8.0.RC + 0.8.1 Config $(Config) ModelsVersion diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 6d687306d..310dd1637 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -17310,6 +17310,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = 1; }; name = LoadDB; }; @@ -17339,7 +17340,7 @@ SKIP_INSTALL = YES; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = LoadDB; }; @@ -17510,7 +17511,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Dev; }; @@ -17542,7 +17543,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -17695,6 +17696,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = 1; }; name = Dev; }; @@ -17726,6 +17728,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -17814,6 +17817,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = 1; }; name = "Release-Debug"; }; @@ -17846,7 +17850,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = "Release-Debug"; }; @@ -17994,6 +17998,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = 1; }; name = Spotify; }; @@ -18025,7 +18030,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Spotify; }; @@ -18261,6 +18266,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = 1; }; name = PrereleaseDebug; }; @@ -18293,7 +18299,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = PrereleaseDebug; }; @@ -18440,6 +18446,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = 1; }; name = DevAutoTests; }; @@ -18471,7 +18478,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = DevAutoTests; }; @@ -18635,6 +18642,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = 1; }; name = Prerelease; }; @@ -18666,7 +18674,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.2; SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Prerelease; }; diff --git a/Nynja/Resources/Info.plist b/Nynja/Resources/Info.plist index db4bc4935..7c918d514 100644 --- a/Nynja/Resources/Info.plist +++ b/Nynja/Resources/Info.plist @@ -23,7 +23,7 @@ CFBundleShortVersionString 0.8.0 CFBundleVersion - 0.8.0.RC + 0.8.1 ConfServerAddress $(ConfServerAddress) ConfServerPort diff --git a/Nynja/Resources/ReleaseConfig.xcconfig b/Nynja/Resources/ReleaseConfig.xcconfig index f65b25f2e..1c5970aa3 100644 --- a/Nynja/Resources/ReleaseConfig.xcconfig +++ b/Nynja/Resources/ReleaseConfig.xcconfig @@ -9,9 +9,9 @@ BundleIdentifier = com.nynja.mobile.communicator ExtensionBundleIdentifier = com.nynja.mobile.communicator.Nynja-Share -ServerURL = im-fallback.nynja.net +ServerURL = im.nynja.net AppName = NYNJA -ServerPort = 8443 +ServerPort = 443 Config = release AppGroup = group.com.nynja.mobile.communicator ModelsVersion = 10 -- GitLab From cce4180675fc23d3ffd4c0daaf8efb0f31d99f03 Mon Sep 17 00:00:00 2001 From: Angel Terziev Date: Mon, 17 Dec 2018 20:00:46 +0200 Subject: [PATCH 30/41] Removed logs in RELEASE (#1540) [NY-6089] Can't receive incoming call after switch from WiFi to LTE --- Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift | 7 +++++-- Podfile | 2 +- Podfile.lock | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift b/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift index 526cd7e54..12ae7e6e5 100644 --- a/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift +++ b/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift @@ -79,8 +79,10 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele override init() { var logDir:String? - let searchPaths = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true) + #if !RELEASE + let searchPaths = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true) + if searchPaths.count > 0 { var path:String = searchPaths[0] @@ -103,7 +105,8 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele } } - + #endif + if let lgDir = logDir { self.nynComm = NynjaCommunicator.start(withLogDir: lgDir, andLogLevel: NYN_LOG_LEVEL.VERBOSE) } else { diff --git a/Podfile b/Podfile index a70f91f1c..ccb23b623 100644 --- a/Podfile +++ b/Podfile @@ -39,7 +39,7 @@ def commonPodsForNynja pod 'MaterialComponents/FlexibleHeader', '= 55.3.0' pod 'JTAppleCalendar', '= 7.1.6' - pod 'NynjaSDK', '= 1.8' + pod 'NynjaSDK', '= 1.8.1' pod 'CryptoSwift', '= 0.13.0' diff --git a/Podfile.lock b/Podfile.lock index 1d2aad740..d6dfcf844 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -62,7 +62,7 @@ PODS: - MQTTClient/Min - SocketRocket - MulticastDelegateSwift (2.1.1) - - NynjaSDK (1.8) + - NynjaSDK (1.8.1) - QRCode (2.0) - SDWebImage (4.4.2): - SDWebImage/Core (= 4.4.2) @@ -95,7 +95,7 @@ DEPENDENCIES: - MaterialComponents/FlexibleHeader (= 55.3.0) - MQTTClient/Websocket (= 0.15.2) - MulticastDelegateSwift (= 2.1.1) - - NynjaSDK (= 1.8) + - NynjaSDK (= 1.8.1) - QRCode (= 2.0) - SDWebImage (= 4.4.2) - SnapKit (= 4.2.0) @@ -172,7 +172,7 @@ SPEC CHECKSUMS: MDFTextAccessibility: 94098925e0853551c5a311ce7c1ecefbe297cdb6 MQTTClient: 902c7bcac1501595f3d0b15178c7205b40331fb0 MulticastDelegateSwift: 93eb077c24f50574b3f8a3f23bf71be6de6e3b41 - NynjaSDK: 3c245c3a1b1e5e650012d2f367260ecf4860e748 + NynjaSDK: 5fdd248089a3bbd8eee950efb84124628344bda8 QRCode: f98a1886c8f37523704a7512a4c0cd45b34c18a4 SDWebImage: 624d6e296c69b244bcede364c72ae0430ac14681 SnapKit: fe8a619752f3f27075cc9a90244d75c6c3f27e2a @@ -181,6 +181,6 @@ SPEC CHECKSUMS: SwiftyJSON: c4bcba26dd9ec7a027fc8eade48e2c911f229e96 TestFairy: 842f8ddc45477b208eb85326b0418047b40f7137 -PODFILE CHECKSUM: 955505f5c9ab80c1ec2042a2c5d6825a0ac12771 +PODFILE CHECKSUM: a26709f4f5bfd33de084ef65033a3017a3958d16 COCOAPODS: 1.5.3 -- GitLab From 8ce640f10136dd035731b8ec1810715808390c08 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Mon, 17 Dec 2018 20:01:36 +0200 Subject: [PATCH 31/41] [NY-274] There is unable to download file on mobile that was sent from web (#1543) --- Nynja/Library/UI/Extensions/DateExtensions.swift | 11 +++++++++++ Nynja/Services/Amazon+FileSync.swift | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Nynja/Library/UI/Extensions/DateExtensions.swift b/Nynja/Library/UI/Extensions/DateExtensions.swift index 01dacd82e..08062571f 100644 --- a/Nynja/Library/UI/Extensions/DateExtensions.swift +++ b/Nynja/Library/UI/Extensions/DateExtensions.swift @@ -72,3 +72,14 @@ extension Date { } } } + +extension Date { + var millisecondsSince1970:Int { + return Int((self.timeIntervalSince1970 * 1000.0).rounded()) + } + + init(milliseconds:Int) { + self = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000) + } +} + diff --git a/Nynja/Services/Amazon+FileSync.swift b/Nynja/Services/Amazon+FileSync.swift index c76edb8c5..73afda494 100644 --- a/Nynja/Services/Amazon+FileSync.swift +++ b/Nynja/Services/Amazon+FileSync.swift @@ -20,7 +20,8 @@ extension AmazonManager: FileDownloader { let downloadRequest = AWSS3TransferManagerDownloadRequest() downloadRequest?.bucket = destination.bucket - downloadRequest?.key = destination.bucketContentPrefix.map { "\($0)/\(encodedFileName)" } ?? encodedFileName + let key = destination.bucketContentPrefix.map { "\($0)/\(encodedFileName)" } ?? encodedFileName + downloadRequest?.key = key.removingPercentEncoding downloadRequest?.downloadingFileURL = downloadingFileURL let requestStartTime = CFAbsoluteTimeGetCurrent() -- GitLab From 56314bb9707fe059cbc4d2e0ca9dee0ce6dbb0c6 Mon Sep 17 00:00:00 2001 From: Anton Poltoratskyi Date: Mon, 17 Dec 2018 20:04:48 +0200 Subject: [PATCH 32/41] [NY-6187] Incorrect transition to the Chat after sending photo / video from camera (#1541) * [NY-6187] Minor refactoring in CameraCoordinator. * [NY-6187] Fixed retain cycles in CameraSettings + CameraVideoPreview. Fixed navigation. * [NY-6187] Remove some of implicit animations * [NY-6187] Disable CALayer animations. --- Nynja/Library/UI/UIViewController+Child.swift | 12 +-- .../Library/UI/View/TimerView/TimerView.swift | 18 +++- .../Flows/CameraFlow/CameraCoordinator.swift | 93 +++++++++++++------ .../CameraVideoPreviewPresenter.swift | 2 +- .../CameraVideoPreviewViewController.swift | 55 +++-------- .../View/Subviews/VideoPreviewView.swift | 4 + .../Interactor/CameraSettingsInteractor.swift | 2 +- 7 files changed, 101 insertions(+), 85 deletions(-) diff --git a/Nynja/Library/UI/UIViewController+Child.swift b/Nynja/Library/UI/UIViewController+Child.swift index 3aa0ea24d..5d22a3d9f 100644 --- a/Nynja/Library/UI/UIViewController+Child.swift +++ b/Nynja/Library/UI/UIViewController+Child.swift @@ -33,6 +33,7 @@ extension UIViewController { } extension UIViewController { + func dismissAll(animated: Bool, rootCompletion: (() -> Void)? = nil) { var vcs: [UIViewController] = [self] @@ -41,7 +42,7 @@ extension UIViewController { } func remove() { - vcs.last?.dismiss(animated: true, completion: { + vcs.last?.dismiss(animated: true) { rootCompletion?() vcs.removeLast() @@ -49,16 +50,9 @@ extension UIViewController { if vcs.last != nil { remove() } - }) + } } remove() } - - - func dismissTwoViews(){ - - self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil) - - } } diff --git a/Nynja/Library/UI/View/TimerView/TimerView.swift b/Nynja/Library/UI/View/TimerView/TimerView.swift index 0b4595cba..5a5346f17 100644 --- a/Nynja/Library/UI/View/TimerView/TimerView.swift +++ b/Nynja/Library/UI/View/TimerView/TimerView.swift @@ -16,15 +16,21 @@ final class TimerView: UIView { //MARK: - Public extension TimerView { - func setTimeValue(seconds: Int) { + func setTimeValue(seconds: Int, animated: Bool = true) { if timerLabel == nil { timerLabel = makeTimerLabel() timerLabel?.alpha = 0 - UIView.animate(withDuration: 0.25) { [weak self] in + let layout = { [weak self] in self?.timerLabel?.alpha = 1 self?.layoutIfNeeded() } + + if animated { + UIView.animate(withDuration: 0.25, animations: layout) + } else { + layout() + } } var secondsStr = "\((seconds % 3600) % 60)" @@ -35,10 +41,16 @@ extension TimerView { minutesStr = appendZeroIfNedeed(time: minutesStr) hoursStr = appendZeroIfNedeed(time: hoursStr) - UIView.animate(withDuration: 0.25) { [weak self] in + let layout = { [weak self] in self?.timerLabel?.text = hoursStr + ":" + minutesStr + ":" + secondsStr self?.layoutIfNeeded() } + + if animated { + UIView.animate(withDuration: 0.25, animations: layout) + } else { + layout() + } } func setFontSize(size: Int) { diff --git a/Nynja/Modules/Flows/CameraFlow/CameraCoordinator.swift b/Nynja/Modules/Flows/CameraFlow/CameraCoordinator.swift index d794fabb4..f60eae879 100644 --- a/Nynja/Modules/Flows/CameraFlow/CameraCoordinator.swift +++ b/Nynja/Modules/Flows/CameraFlow/CameraCoordinator.swift @@ -18,7 +18,7 @@ final class CameraFlowCoordinator: CameraFlowCoordinatorProtocol, SetInjectable private var rootViewController: UIViewController! private var serviceFactory: ServiceFactoryProtocol! - private var mainFlowVC: UIViewController? + private var mainFlowVC: UINavigationController? } // MARK: - Injectable @@ -54,9 +54,12 @@ extension CameraFlowCoordinator { cameraSettingsService: serviceFactory.makeCameraSettingsService(with: .cameraFlow), locationService: serviceFactory.makeLocationService())) - mainFlowVC = view + let navigationController = UINavigationController(rootViewController: view) + navigationController.isNavigationBarHidden = true + + mainFlowVC = navigationController - rootViewController.present(view, animated: true, completion: nil) + rootViewController.present(navigationController, animated: true, completion: nil) } func end() { @@ -69,11 +72,16 @@ extension CameraFlowCoordinator { extension CameraFlowCoordinator { func wireframe(_ wireframe: CameraWireframe, with mainView: UIViewController, didEndWith state: CameraWireframe.State) { switch state { - case .back, .send: mainView.dismiss(animated: true, completion: nil) - case .continueWithImage(let image): self.cameraWireframe(wireframe, with: mainView, didEndWith: image) - case .continueWithVideoUrl(let videoURL): self.cameraWireframe(wireframe, with: mainView, didEndWith: videoURL) - case .settings: cameraWireframeOpenSettings(wireframe, with: mainView) - case .continueWithQRCode(let qrCode): cameraWireframeOpenQRCodePreview(wireframe, with: mainView, text: qrCode) + case .back, .send: + end() + case .continueWithImage(let image): + cameraWireframe(wireframe, with: mainView, didEndWith: image) + case .continueWithVideoUrl(let videoURL): + cameraWireframe(wireframe, with: mainView, didEndWith: videoURL) + case .settings: + cameraWireframeOpenSettings(wireframe, with: mainView) + case .continueWithQRCode(let qrCode): + cameraWireframeOpenQRCodePreview(wireframe, with: mainView, text: qrCode) } } @@ -81,40 +89,53 @@ extension CameraFlowCoordinator { let wireframe = PhotoPreviewWireframe(coordinator: self) let view = wireframe.prepareModule( - parameters: PhotoPreviewWireframe.Parameters( + parameters: .init( image: image, isCanSave: true, isHaveAdditionalFunctionality: true, isHaveCrop: false, - source: .chat), - dependencies: PhotoPreviewWireframe.Dependencies( + source: .chat + ), + dependencies: .init( resourceManager: serviceFactory.makeResourceManager(), - cameraSettingsService: serviceFactory.makeCameraSettingsService(with: .cameraFlow))) - - mainView.present(view, animated: false, completion: nil) + cameraSettingsService: serviceFactory.makeCameraSettingsService(with: .cameraFlow) + ) + ) + + mainFlowVC?.pushViewController(view, animated: true) } private func cameraWireframe(_ cameraWireframe: CameraWireframe, with mainView: UIViewController, didEndWith videoURL: URL) { let wireframe = CameraVideoPreviewWireframe(coordinator: self) let view = wireframe.prepareModule( - parameters: CameraVideoPreviewWireframe.Parameters(videoURL: videoURL, contact: contact, room: room, isCanSave: true), - dependencies: CameraVideoPreviewWireframe.Dependencies( + parameters: .init( + videoURL: videoURL, + contact: contact, + room: room, + isCanSave: true + ), + dependencies: .init( resourceManager: serviceFactory.makeResourceManager(), audioSessionManager: serviceFactory.makeAudioSessionManager(), messageFactory: serviceFactory.makeMessageFactory(), messageSendingService: serviceFactory.makeMessageSendingService(), - cameraSettingsService: serviceFactory.makeCameraSettingsService(with: .cameraFlow))) - - mainView.present(view, animated: false, completion: nil) + cameraSettingsService: serviceFactory.makeCameraSettingsService(with: .cameraFlow) + ) + ) + + mainFlowVC?.pushViewController(view, animated: true) } private func cameraWireframeOpenSettings(_ cameraWireframe: CameraWireframe, with mainView: UIViewController) { let cameraSettingsCoordinator = CameraSettingsFlowCoordinator() cameraSettingsCoordinator.inject( - dependencies: CameraSettingsFlowCoordinator.Dependencies( + dependencies: .init( rootViewController: mainView, - serviceFactory: serviceFactory, sourceFlow: .cameraFlow)) + serviceFactory: serviceFactory, + sourceFlow: .cameraFlow + ) + ) cameraSettingsCoordinator.start() } @@ -123,12 +144,18 @@ extension CameraFlowCoordinator { let qrCodePreviewWireframe = CameraQRPreviewWireframe(coordinator: self) let view = qrCodePreviewWireframe.prepareModule( - parameters: CameraQRPreviewWireframe.Parameters(text: text, contact: contact, room: room), - dependencies: CameraQRPreviewWireframe.Dependencies( + parameters: .init( + text: text, + contact: contact, + room: room + ), + dependencies: .init( messageFactory: serviceFactory.makeMessageFactory(), - messageSendingService: serviceFactory.makeMessageSendingService())) + messageSendingService: serviceFactory.makeMessageSendingService() + ) + ) - mainView.present(view, animated: true, completion: nil) + mainFlowVC?.pushViewController(view, animated: true) } } @@ -137,7 +164,9 @@ extension CameraFlowCoordinator { extension CameraFlowCoordinator { func wireframe(_ wireframe: PhotoPreviewWireframe, with mainView: UIViewController, didEndWithState state: PhotoPreviewWireframe.State) { switch state { - case .back: mainView.dismiss(animated: false, completion: nil) + case .back: + mainFlowVC?.popViewController(animated: true) + case .send(let imageURL): let messageFactory = serviceFactory.makeMessageFactory() let messageSendingService = serviceFactory.makeMessageSendingService() @@ -163,8 +192,10 @@ extension CameraFlowCoordinator { extension CameraFlowCoordinator { func wireframe(_ vwireframe: CameraVideoPreviewWireframe, with mainView: UIViewController, didEndWithState state: CameraVideoPreviewWireframe.State) { switch state { - case .back: mainView.dismiss(animated: false, completion: nil) - case .send: end() + case .back: + mainFlowVC?.popViewController(animated: true) + case .send: + end() } } } @@ -174,8 +205,10 @@ extension CameraFlowCoordinator { extension CameraFlowCoordinator { func wireframe(_ wireframe: CameraQRPreviewWireframe, with mainView: UIViewController, didEndWithState state: CameraQRPreviewWireframe.State) { switch state { - case .back: mainView.dismiss(animated: false, completion: nil) - case .send: end() + case .back: + mainFlowVC?.popViewController(animated: true) + case .send: + end() } } } diff --git a/Nynja/Modules/Flows/CameraFlow/VideoPreview/Presenter/CameraVideoPreviewPresenter.swift b/Nynja/Modules/Flows/CameraFlow/VideoPreview/Presenter/CameraVideoPreviewPresenter.swift index 1e864adbb..440faaabe 100644 --- a/Nynja/Modules/Flows/CameraFlow/VideoPreview/Presenter/CameraVideoPreviewPresenter.swift +++ b/Nynja/Modules/Flows/CameraFlow/VideoPreview/Presenter/CameraVideoPreviewPresenter.swift @@ -11,7 +11,7 @@ import Foundation final class CameraVideoPreviewPresenter: CameraVideoPreviewPresenterProtocol, CameraVideoPreviewOutputInteractorProtocol, SetInjectable { private var interactor: CameraVideoPreviewInputInteractorProtocol! private var wireframe: CameraVideoPreviewWireframe! - private var view: CameraVideoPreviewViewProtocol! + private weak var view: CameraVideoPreviewViewProtocol! } //MARK: - CameraVideoPreviewPresenterProtocol diff --git a/Nynja/Modules/Flows/CameraFlow/VideoPreview/View/CameraVideoPreviewViewController.swift b/Nynja/Modules/Flows/CameraFlow/VideoPreview/View/CameraVideoPreviewViewController.swift index 8325918a1..45a373ec8 100644 --- a/Nynja/Modules/Flows/CameraFlow/VideoPreview/View/CameraVideoPreviewViewController.swift +++ b/Nynja/Modules/Flows/CameraFlow/VideoPreview/View/CameraVideoPreviewViewController.swift @@ -48,13 +48,9 @@ final class CameraVideoPreviewViewController: UIViewController, CameraVideoPrevi private weak var playButton: UIButton! - private weak var screenshot: UIView? - override func viewDidLoad() { super.viewDidLoad() - - UINavigationBar.appearance().tintColor = UIColor.nynja.darkLight - + videoPreviewView = makeVideoPreviewView(on: view) statusBarBackgroundView = UIView.makeStatusBarBackgroundView(on: view) @@ -68,6 +64,9 @@ final class CameraVideoPreviewViewController: UIViewController, CameraVideoPrevi navigationHandler: presenter, backButtonImage: UIImage.nynja.icBackNavigation.image)) + // FIXME: remove it and replace with normal navigation view + ([statusBarBackgroundView, headerView] as [UIView]).forEach { $0.backgroundColor = UIColor.nynja.darkLight } + timerView = makeTimerView(on: headerView) footerView = makeFooterView(on: view) @@ -93,33 +92,7 @@ final class CameraVideoPreviewViewController: UIViewController, CameraVideoPrevi setupNotifications() - timerView.setTimeValue(seconds: presenter.getVideoLenghtInSeconds()) - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - if let screenshot = presentingViewController?.view.snapshotView(afterScreenUpdates: false) { - view.addSubview(screenshot) - self.screenshot = screenshot - - screenshot.snp.makeConstraints { (maker) in - maker.top.left.right.bottom.equalToSuperview() - } - } - } - - override func viewDidAppear(_ animated: Bool) { - super.viewWillDisappear(animated) - - UIView.animate( - withDuration: 0.25, - animations: { [weak self] in - self?.screenshot?.alpha = 0 - self?.setNeedsStatusBarAppearanceUpdate() - }) { [weak self] (result) in - self?.screenshot?.removeFromSuperview() - } + timerView.setTimeValue(seconds: presenter.getVideoLenghtInSeconds(), animated: false) } override func viewDidLayoutSubviews() { @@ -256,7 +229,7 @@ private extension CameraVideoPreviewViewController { func makeTimerView(on view: UIView) -> TimerView { let timerView = TimerView() view.addSubview(timerView) - timerView.setTimeValue(seconds: 0) + timerView.setTimeValue(seconds: 0, animated: false) timerView.snp.makeConstraints { (maker) in maker.bottom.equalTo(view).offset(-TimerViewlayout.bottom) @@ -498,12 +471,12 @@ private extension CameraVideoPreviewViewController { // MARK: - Layout extension CameraVideoPreviewViewController { - struct TimerViewlayout { + enum TimerViewlayout { static let right = 17 static let bottom = 9 } - struct FooterViewLayout { + enum FooterViewLayout { static let collapsedHeight = 100.adjustedByHeight static let expandedHeight = 180.adjustedByHeight @@ -511,7 +484,7 @@ extension CameraVideoPreviewViewController { static let arrowWidth = 44 } - struct SendButtonLayout { + enum SendButtonLayout { static let right = 16.adjustedByWidth static let width = 77.adjustedByHeight static let height = 44.adjustedByHeight @@ -519,28 +492,28 @@ extension CameraVideoPreviewViewController { static let cornerRadius = height / 2 } - struct SendAsFileButtonLayout { + enum SendAsFileButtonLayout { static let bottom = 22 } - struct SaveButtonLayout { + enum SaveButtonLayout { static let left = 16.adjustedByWidth static let height = 44.adjustedByHeight } - struct PlayerButtonLayout { + enum PlayerButtonLayout { static let height = 44.adjustedByHeight static let width = 44.adjustedByWidth } - struct MuteButtonLayout { + enum MuteButtonLayout { static let width = 50.adjustedByHeight static let height = 50.adjustedByHeight static let left = 16.adjustedByWidth static let bottom = 20.adjustedByHeight } - struct ProgressViewLayout { + enum ProgressViewLayout { static let height = 3 } } diff --git a/Nynja/Modules/Flows/CameraFlow/VideoPreview/View/Subviews/VideoPreviewView.swift b/Nynja/Modules/Flows/CameraFlow/VideoPreview/View/Subviews/VideoPreviewView.swift index 2a4904f7d..02b07fc33 100644 --- a/Nynja/Modules/Flows/CameraFlow/VideoPreview/View/Subviews/VideoPreviewView.swift +++ b/Nynja/Modules/Flows/CameraFlow/VideoPreview/View/Subviews/VideoPreviewView.swift @@ -13,6 +13,10 @@ class VideoPreviewView: UIView { override func layoutSublayers(of layer: CALayer) { super.layoutSublayers(of: layer) + + CATransaction.begin() + CATransaction.setDisableActions(true) playerLayer?.frame = bounds + CATransaction.commit() } } diff --git a/Nynja/Modules/Flows/CameraSettingsFlow/CameraSettings/Interactor/CameraSettingsInteractor.swift b/Nynja/Modules/Flows/CameraSettingsFlow/CameraSettings/Interactor/CameraSettingsInteractor.swift index 9de9413ee..8cb3e6699 100644 --- a/Nynja/Modules/Flows/CameraSettingsFlow/CameraSettings/Interactor/CameraSettingsInteractor.swift +++ b/Nynja/Modules/Flows/CameraSettingsFlow/CameraSettings/Interactor/CameraSettingsInteractor.swift @@ -9,7 +9,7 @@ import Foundation final class CameraSettingsInteractor: CameraSettingsInputInteractorProtocol, SetInjectable { - private var presenter: CameraSettingsOutputInteractorProtocol! + private weak var presenter: CameraSettingsOutputInteractorProtocol! private var cameraSettingsService: CameraSettingsServiceProtocol! private var locationService: LocationService! private let appNotificationsProvider = AppNotificationsProvider() -- GitLab From 9ba3f59940bfc2f241ecdbe7599ad49b8aa2ffe1 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Mon, 17 Dec 2018 21:35:40 +0200 Subject: [PATCH 33/41] 0.8.2 --- Nynja-Share/Resources/Info.plist | 2 +- Nynja.xcodeproj/project.pbxproj | 6 ++---- Nynja/Resources/Info.plist | 2 +- .../AutoReconnectConnectionHandler.swift | 10 ++++++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist index f6eb18bfa..d116c1d1b 100644 --- a/Nynja-Share/Resources/Info.plist +++ b/Nynja-Share/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 0.8.0 CFBundleVersion - 0.8.1 + 0.8.2 Config $(Config) ModelsVersion diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 310dd1637..1805772b5 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -2111,7 +2111,6 @@ F105C6BC20A1347E0091786A /* PhotoPreviewWireframeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F105C6B520A1347E0091786A /* PhotoPreviewWireframeProtocol.swift */; }; F105C6BD20A1347E0091786A /* PhotoPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F105C6B720A1347E0091786A /* PhotoPreviewViewController.swift */; }; F105C6BE20A1347E0091786A /* PhotoPreviewInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F105C6B920A1347E0091786A /* PhotoPreviewInteractor.swift */; }; - F10AFE9B20EF8B9B00C7CE83 /* DevAutoTests.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = F10AFE9A20EF8B9A00C7CE83 /* DevAutoTests.xcconfig */; }; F10AFEB420F7B1B000C7CE83 /* WheelPreviewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F10AFEAE20F7B1AF00C7CE83 /* WheelPreviewProtocol.swift */; }; F10AFEB520F7B1B000C7CE83 /* WheelDefaultItemPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = F10AFEAF20F7B1AF00C7CE83 /* WheelDefaultItemPreview.swift */; }; F10AFEB620F7B1B000C7CE83 /* WheelImageFullItemPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = F10AFEB020F7B1AF00C7CE83 /* WheelImageFullItemPreview.swift */; }; @@ -14532,7 +14531,6 @@ A4B544F820EFC0AD00EB7B0F /* StatusCodes.strings in Resources */, 85EBBE052056E8B2009BB269 /* outcoming_message.mp3 in Resources */, E77B9B7C1FDEC6E20035CA12 /* NotoSans-Bold.ttf in Resources */, - F10AFE9B20EF8B9B00C7CE83 /* DevAutoTests.xcconfig in Resources */, 00F7B348202B317000E443E1 /* timezones.json in Resources */, 5BBEF53C212DE09F00F10768 /* ringback.m4a in Resources */, 3A2843291EF9317100EFE21A /* Avenir.ttc in Resources */, @@ -17537,7 +17535,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "2a318f9e-d0ab-41dc-968a-e1cb13de4de5"; - PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_AppstoreExt; + PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_AdhocExt; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -17721,7 +17719,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "1971b115-2b2c-48b6-a20f-3686249e0a88"; - PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_Appstore; + PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_Adhoc; SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/Nynja/Resources/Info.plist b/Nynja/Resources/Info.plist index 7c918d514..3109daba2 100644 --- a/Nynja/Resources/Info.plist +++ b/Nynja/Resources/Info.plist @@ -23,7 +23,7 @@ CFBundleShortVersionString 0.8.0 CFBundleVersion - 0.8.1 + 0.8.2 ConfServerAddress $(ConfServerAddress) ConfServerPort diff --git a/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift b/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift index 831da97a4..42f5e928e 100644 --- a/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift +++ b/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift @@ -7,6 +7,7 @@ // import Foundation +import UIKit final class AutoReconnectConnectionHandler: MQTTConnectionHandler { @@ -27,8 +28,13 @@ final class AutoReconnectConnectionHandler: MQTTConnectionHandler { shouldPerformReconnect() else { return } - - mqttService?.connect() + #if !SHARE_EXTENSION + if UIApplication.shared.applicationState == .active { + mqttService?.connect() + } + #else + mqttService?.connect() + #endif } } } -- GitLab From 2d295624348f6b722eaee05fac52d60bd340c3c5 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Mon, 17 Dec 2018 19:30:28 +0200 Subject: [PATCH 34/41] [NY-6186] Wheel stay on the Group name / My alias screens (#1539) --- .../CreateGroup/WireFrame/CreateGroupWireframe.swift | 8 +++----- Nynja/Modules/Main/MainProtocols.swift | 2 ++ Nynja/Modules/Main/WireFrame/MainWireframe.swift | 12 +++++++++++- .../WireFrame/SettingsGroupWireFrame.swift | 5 ++--- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Nynja/Modules/Flows/CreateGroupFlow/CreateGroup/WireFrame/CreateGroupWireframe.swift b/Nynja/Modules/Flows/CreateGroupFlow/CreateGroup/WireFrame/CreateGroupWireframe.swift index 8fcddd641..1b159fcb1 100644 --- a/Nynja/Modules/Flows/CreateGroupFlow/CreateGroup/WireFrame/CreateGroupWireframe.swift +++ b/Nynja/Modules/Flows/CreateGroupFlow/CreateGroup/WireFrame/CreateGroupWireframe.swift @@ -14,7 +14,7 @@ class CreateGroupWireFrame: CreateGroupWireFrameProtocol { weak var external: CreateGroupExternalProtocol? weak var mainWireFrame: MainWireFrame? - func presentCreateGroup(navigation: UINavigationController, mainWireFrame: MainWireFrame?,contacts: [Contact]) { + func presentCreateGroup(navigation: UINavigationController, mainWireFrame: MainWireFrame?, contacts: [Contact]) { let view = CreateGroupViewController() let presenter = CreateGroupPresenter() let interactor = CreateGroupInteractor() @@ -34,13 +34,11 @@ class CreateGroupWireFrame: CreateGroupWireFrameProtocol { } func changeGroupName(name: String) { - EditGroupNameWireFrame().presentEditGroupName(navigation: navigation!, currentName: name, delegate: external, mode: .create) - self.navigation?.view.layoutIfNeeded() + mainWireFrame?.showGroupName(name, delegate: external, mode: .create) } func changeAlias(alias: String) { - MyGroupAliasWireFrame().presentMyGroupAlias(navigation: navigation!, currentAlias: alias, delegate: external, mode: .create) - self.navigation?.view.layoutIfNeeded() + mainWireFrame?.showGroupAlias(alias, delegate: external, mode: .create) } func updateParticipants(contacts: [Contact]) { diff --git a/Nynja/Modules/Main/MainProtocols.swift b/Nynja/Modules/Main/MainProtocols.swift index a6d6b0eae..4b247bc71 100644 --- a/Nynja/Modules/Main/MainProtocols.swift +++ b/Nynja/Modules/Main/MainProtocols.swift @@ -98,6 +98,8 @@ protocol MainWireFrameProtocol: class { func showGroupsList() func showGroupsOptions() func showChatOptions() + func showGroupName(_ name: String, delegate: GroupNameEditorDelegate?, mode: GroupMode) + func showGroupAlias(_ alias: String, delegate: MyGroupAliasEditorDelegate?, mode: GroupMode) // Channel func showNewChannel() diff --git a/Nynja/Modules/Main/WireFrame/MainWireframe.swift b/Nynja/Modules/Main/WireFrame/MainWireframe.swift index 9c7e2bae9..af3c5c06d 100644 --- a/Nynja/Modules/Main/WireFrame/MainWireframe.swift +++ b/Nynja/Modules/Main/WireFrame/MainWireframe.swift @@ -12,7 +12,7 @@ import SnapKit final class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate { - weak var navigation : UINavigationController? + weak var navigation : UINavigationController! weak var contentNavigation: UINavigationController! weak var view: MainViewController? weak var messageinteractor: MessageInteractor? @@ -566,6 +566,16 @@ final class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelega func showChatOptions() { OtherUserWireFrame().present(navigation: contentNavigation, main: self) } + + func showGroupName(_ name: String, delegate: GroupNameEditorDelegate?, mode: GroupMode) { + EditGroupNameWireFrame().presentEditGroupName(navigation: navigation, currentName: name, delegate: delegate, mode: mode) + navigation.view.layoutIfNeeded() + } + + func showGroupAlias(_ alias: String, delegate: MyGroupAliasEditorDelegate?, mode: GroupMode) { + MyGroupAliasWireFrame().presentMyGroupAlias(navigation: navigation, currentAlias: alias, delegate: delegate, mode: mode) + navigation.view.layoutIfNeeded() + } // MARK: Chats p2p diff --git a/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift b/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift index 3ca3b8c21..df0b060be 100644 --- a/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift +++ b/Nynja/Modules/SettingsGroup/WireFrame/SettingsGroupWireFrame.swift @@ -69,12 +69,11 @@ class SettingsGroupWireFrame: SettingsGroupWireframeProtocol { } func changeGroupName(name: String) { - EditGroupNameWireFrame().presentEditGroupName(navigation: navigation!, currentName: name, delegate: external, mode: .update) - self.navigation?.view.layoutIfNeeded() + main?.showGroupName(name, delegate: external, mode: .update) } func changeAlias(alias: String) { - MyGroupAliasWireFrame().presentMyGroupAlias(navigation: navigation!, currentAlias: alias, delegate: external, mode: .update) + main?.showGroupAlias(alias, delegate: external, mode: .update) } func showRules(room: Room?) { -- GitLab From c2156dd8c78b68124714de200318581f1abbc880 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Mon, 17 Dec 2018 21:46:11 +0200 Subject: [PATCH 35/41] [NY-6138] Fix issue with 'Myself Chat > preview of deleted message is displayed' which was found on the 'Chat' screen. (#1542) --- Nynja/ChatService/ChatService.swift | 40 +++++++++++++++---- .../Interactor/ChatsListInteractor.swift | 3 +- .../Interactor/MessageInteractor.swift | 26 ++++++------ .../MessageSendingService.swift | 1 + 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/Nynja/ChatService/ChatService.swift b/Nynja/ChatService/ChatService.swift index ddd931edd..ec936a274 100644 --- a/Nynja/ChatService/ChatService.swift +++ b/Nynja/ChatService/ChatService.swift @@ -86,15 +86,13 @@ final class ChatService { guard let chatModel = chat ?? fetchChatModel(from: message) else { return false } - - if let shouldUpdate = shouldUpdate, let lastId = chatModel.last_msg?.id, !shouldUpdate(lastId) { + + if let lastId = chatModel.last_msg?.id, let shouldUpdate = shouldUpdate, !shouldUpdate(lastId) { return false } chatModel.last_msg = nil - if let serverId = message.id, let lastMessageId = MessageDAO.fetchMessagePrimaryKey(with: serverId) { - chatModel.lastMessageId = lastMessageId - } + chatModel.lastMessageId = message.msg_id let shouldIncrementUnread = (!message.isOwn || message.isCursor) && !message.isEdited && shouldChangeUnread if shouldIncrementUnread { @@ -175,10 +173,10 @@ final class ChatService { // MARK: - Remove message static func removeMessages(_ messages: [Message]) { - messages.forEach { removeMessage($0, shouldUpdateChat: false) } + messages.forEach { removeMessage($0) } } - static func removeMessage(_ message: Message, shouldUpdateChat: Bool = true) { + static func removeMessage(_ message: Message) { guard MessageDAO.removeMessage(using: message), let id = message.linkedId else { return } @@ -187,9 +185,23 @@ final class ChatService { try? storageService.perform(action: .delete, with: action) } + updateAfterRemove(using: message, id: id) + } + + static func removeLocal(message: Message) { + try? storageService.perform(action: .save, with: message) + + guard let id = message.id else { + return + } + updateAfterRemove(using: message, id: id) + } + + private static func updateAfterRemove(using message: Message, id: MessageServerId) { guard let chat = fetchChatModel(from: message) else { return } + updateUnreadMentionsAfterRemove(for: chat, serverId: id) updateUnreadCounterAfterRemove(for: chat, serverId: id) updateLastMessageAfterRemove(for: chat, message: message) @@ -215,8 +227,9 @@ final class ChatService { updateUnreadCounter(for: chat) } - private static func updateLastMessageAfterRemove(for chat: ChatModel, message: Message) { + static func updateLastMessageAfterRemove(for chat: ChatModel, message: Message) { guard let lastMessage = MessageDAO.fetchLastMessage(feed: message.feed) else { + removeLast(message: message, inChat: chat) return } updateLastMessage(lastMessage, chat: chat, shouldChangeUnread: false, shouldUpdateMentions: false) { lastMessageId in @@ -224,6 +237,17 @@ final class ChatService { } } + private static func removeLast(message: Message, inChat chat: ChatModel) { + chat.last_msg = nil + chat.lastMessageId = nil + chat.unread = 0 + + if let room = chat as? Room { + room.mentions = [] + } + + try? storageService.perform(action: .save, with: chat) + } // MARK: - Clear History diff --git a/Nynja/Modules/ChatsList/Interactor/ChatsListInteractor.swift b/Nynja/Modules/ChatsList/Interactor/ChatsListInteractor.swift index 169875381..6dde34fcd 100644 --- a/Nynja/Modules/ChatsList/Interactor/ChatsListInteractor.swift +++ b/Nynja/Modules/ChatsList/Interactor/ChatsListInteractor.swift @@ -102,8 +102,7 @@ class ChatsListInteractor: BaseInteractor, ChatsListInteractorInputProtocol, Ini } private func updateChatsList(with contact: Contact) -> [Contact] { - guard let phoneId = contact.phone_id, - phoneId != storageService.phoneId else { + guard let phoneId = contact.phone_id else { return chats } diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor.swift b/Nynja/Modules/Message/Interactor/MessageInteractor.swift index 140aa53c7..fde19574b 100644 --- a/Nynja/Modules/Message/Interactor/MessageInteractor.swift +++ b/Nynja/Modules/Message/Interactor/MessageInteractor.swift @@ -849,7 +849,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H message.localStatus = .deleted message.seenby = messageForSave.seenby - try? storageService.perform(action: .save, with: messageForSave) + ChatService.removeLocal(message: messageForSave) if let feed = self.feed, let newLastMessage = MessageDAO.fetchLastMessage(feed: feed) { ChatService.updateLastMessage(newLastMessage, shouldChangeUnread: false) @@ -1078,6 +1078,7 @@ extension MessageInteractor { let sendingLang = conversationLanguageSettingService.outgoingTranslateLanguage try? storageService.perform(action: .save, with: message) + ChatService.updateLastMessage(message) translate(message: message, language: sendingLang) { [weak self] result in guard let self = self else { return } @@ -1092,20 +1093,19 @@ extension MessageInteractor { break } - if let repliedMessage = self.repliedMessage { - self.messageSendingService.sendRepliedMessage(repliedMessage, message: message) - self.declineReply() - } else { - self.messageSendingService.sendMessage(message) - } + self.startSend(message: message) } } else { - if let repliedMessage = repliedMessage { - messageSendingService.sendRepliedMessage(repliedMessage, message: message) - declineReply() - } else { - messageSendingService.sendMessage(message) - } + startSend(message: message) + } + } + + private func startSend(message: Message) { + if let repliedMessage = repliedMessage { + messageSendingService.sendRepliedMessage(repliedMessage, message: message) + declineReply() + } else { + messageSendingService.sendMessage(message) } } diff --git a/Nynja/Services/MessageSendingService/MessageSendingService.swift b/Nynja/Services/MessageSendingService/MessageSendingService.swift index ffa6ba117..efda5a043 100644 --- a/Nynja/Services/MessageSendingService/MessageSendingService.swift +++ b/Nynja/Services/MessageSendingService/MessageSendingService.swift @@ -53,6 +53,7 @@ final class MessageSendingService: MessageSendingServiceProtocol, InitializeInje func sendMessage(_ message: Message) { try? storageService.perform(action: .save, with: message) + ChatService.updateLastMessage(message) processingManager.uploadMessage(message) } -- GitLab From 8fe3b74725391306209b1b3b53bf7cf7af70830a Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Mon, 17 Dec 2018 22:01:12 +0200 Subject: [PATCH 36/41] Another way --- .../AutoReconnectConnectionHandler.swift | 8 +------- Nynja/Services/MQTT/MQTTService.swift | 7 +++++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift b/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift index 42f5e928e..257042379 100644 --- a/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift +++ b/Nynja/Services/MQTT/ConnectionHandlers/AutoReconnectConnectionHandler.swift @@ -28,13 +28,7 @@ final class AutoReconnectConnectionHandler: MQTTConnectionHandler { shouldPerformReconnect() else { return } - #if !SHARE_EXTENSION - if UIApplication.shared.applicationState == .active { - mqttService?.connect() - } - #else - mqttService?.connect() - #endif + mqttService?.connect() } } } diff --git a/Nynja/Services/MQTT/MQTTService.swift b/Nynja/Services/MQTT/MQTTService.swift index db7ab0d16..6d1f2bc33 100644 --- a/Nynja/Services/MQTT/MQTTService.swift +++ b/Nynja/Services/MQTT/MQTTService.swift @@ -57,9 +57,12 @@ final class MQTTService: NSObject, MQTTServiceProtocol, MQTTSessionDelegate { let appGroupConnectionHandler = AppGroupConnectionHandler() handlers.append(appGroupConnectionHandler) - autoReconnectConnectionHandler.shouldPerformReconnect = { [weak appGroupConnectionHandler] in + autoReconnectConnectionHandler.shouldPerformReconnect = { + [weak appGroupConnectionHandler] in let connectionState = appGroupConnectionHandler?.connectionState ?? .connected - return connectionState == .connected + let isConnected = connectionState == .connected + let isAppActive = UIApplication.shared.applicationState == .active + return isConnected && isAppActive } #endif -- GitLab From 9173e7cedd78a07676ae9233d1fb636bd0e427c9 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Tue, 18 Dec 2018 00:23:37 +0200 Subject: [PATCH 37/41] [NY-6188] Incorrect quantity of unread messages on the app icon (#1544) --- Nynja.xcodeproj/project.pbxproj | 4 ++++ Nynja/BadgeNumberService.swift | 5 +++- Nynja/ConversationsProvider.swift | 38 ++++++++++++++++++++++++++++++ Nynja/ConversationsProviding.swift | 1 + Nynja/DB/Models/DBUnreadInfo.swift | 14 +++++++++++ Nynja/DBObserver.swift | 16 ++++++------- 6 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 Nynja/DB/Models/DBUnreadInfo.swift diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 1805772b5..5871845b5 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -793,6 +793,7 @@ 4BF2C3FD218AFF6300E59F6C /* FullNameRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF2C3FB218AFE9D00E59F6C /* FullNameRepresentable.swift */; }; 4BFED75A21A6BE49003CF1B3 /* CLLocation+GPSMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFED75921A6BE49003CF1B3 /* CLLocation+GPSMetadata.swift */; }; 4BFED75D21A6CE38003CF1B3 /* ExtendedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFED75C21A6CE38003CF1B3 /* ExtendedImage.swift */; }; + 4BFF6C8221C84B6A00BB9432 /* DBUnreadInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF6C8121C84B6A00BB9432 /* DBUnreadInfo.swift */; }; 4C5EEA13EBC6A8398F08DCD1 /* MainWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AAB8F770774CE3AE3FD6E1 /* MainWireframe.swift */; }; 4D53FE7454959323B1CCFD96 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270F638DBB2D8FC1BDEB633 /* ProfileViewController.swift */; }; 4DAEBCF361B86B0AD3C98749 /* EditUsernameInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBE3BAC9B7EA418FB463EF04 /* EditUsernameInteractor.swift */; }; @@ -3076,6 +3077,7 @@ 4BF2C3FB218AFE9D00E59F6C /* FullNameRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullNameRepresentable.swift; sourceTree = ""; }; 4BFED75921A6BE49003CF1B3 /* CLLocation+GPSMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CLLocation+GPSMetadata.swift"; sourceTree = ""; }; 4BFED75C21A6CE38003CF1B3 /* ExtendedImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedImage.swift; sourceTree = ""; }; + 4BFF6C8121C84B6A00BB9432 /* DBUnreadInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBUnreadInfo.swift; sourceTree = ""; }; 4CDA2BE900351F21464CE687 /* DateTimePickerInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DateTimePickerInteractor.swift; sourceTree = ""; }; 4D247CBC45C1C1267BBBB289 /* QRCodeReaderInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = QRCodeReaderInteractor.swift; sourceTree = ""; }; 4F7C039B61A0663D43BE5AE5 /* SelectCountryProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SelectCountryProtocols.swift; sourceTree = ""; }; @@ -12264,6 +12266,7 @@ FE58F9B2208F0583004AFDD3 /* DBMessageEditAction.swift */, 26771CC2212ED109006112B5 /* DBConvertMessage.swift */, 8514DE882136A50100718DD8 /* DBStarAction.swift */, + 4BFF6C8121C84B6A00BB9432 /* DBUnreadInfo.swift */, ); path = Models; sourceTree = ""; @@ -16650,6 +16653,7 @@ C99D6CD77325A09D045DB760 /* FavoritesViewController.swift in Sources */, 00E98256205C2740008BF03D /* SessionItemCell.swift in Sources */, 85D77807211D9B980044E72F /* ScrollPosition.swift in Sources */, + 4BFF6C8221C84B6A00BB9432 /* DBUnreadInfo.swift in Sources */, 4B030F362195CD9B00F293B7 /* MQTTService+QueuePool.swift in Sources */, FEA655F22167777E00B44029 /* PaymentWireFrame.swift in Sources */, DF55CCC682DAB5392F2A763D /* FavoritesPresenter.swift in Sources */, diff --git a/Nynja/BadgeNumberService.swift b/Nynja/BadgeNumberService.swift index f86d18afc..639f1f0b7 100644 --- a/Nynja/BadgeNumberService.swift +++ b/Nynja/BadgeNumberService.swift @@ -32,7 +32,10 @@ final class BadgeNumberService: BadgeNumberServiceProtocol, StorageSubscriber { } func initCounters() { - self.badgeNumber = conversationsProvider.fetchUnreadMessagesCount() + conversationsProvider.fetchUnreadsInfo().forEach { info in + counters[info.id] = info.unread + badgeNumber += info.unread + } } deinit { diff --git a/Nynja/ConversationsProvider.swift b/Nynja/ConversationsProvider.swift index f5d011edf..1f60c0170 100644 --- a/Nynja/ConversationsProvider.swift +++ b/Nynja/ConversationsProvider.swift @@ -95,6 +95,44 @@ class ConversationsProvider: ConversationsProviding, InitializeInjectable { where \(unreadColumn) > 0 """ } + + func fetchUnreadsInfo() -> [DBUnreadInfo] { + return dbManager.fetch { db in + return try makeUnreadsInfoRequest().fetchAll(db) + } + } + + private func makeUnreadsInfoRequest() -> AnyTypedRequest { + let idAlias = "id" + + let contactSql = makeUnreadsInfoSQL(for: ContactTable.name, idColumn: Column.phoneId, idAlias: idAlias) + let roomSql = makeUnreadsInfoSQL(for: RoomTable.name, idColumn: Column.id) + + let sql = """ + select \(idAlias), \(Column.unread) + from + (\(contactSql) + union + \(roomSql)) + """ + + return SQLRequest(sql).asRequest(of: DBUnreadInfo.self) + } + + private func makeUnreadsInfoSQL(for tableName: String, + idColumn: Column, + idAlias: String? = nil) -> String { + let unreadColumn = Column.unread.asString(tableName: tableName) + let idColumn = idColumn.asString(tableName: tableName) + + let asIdAlias = idAlias.map { "as '\($0)'" } ?? "" + + return """ + select \(unreadColumn), \(idColumn) \(asIdAlias) + from \(tableName) + where \(unreadColumn) > 0 + """ + } } diff --git a/Nynja/ConversationsProviding.swift b/Nynja/ConversationsProviding.swift index 10bf1bb95..7106e447b 100644 --- a/Nynja/ConversationsProviding.swift +++ b/Nynja/ConversationsProviding.swift @@ -20,4 +20,5 @@ protocol ConversationsProviding { func comparator(lhs: ChatModel, rhs: ChatModel) -> Bool func fetchUnreadMessagesCount() -> Int64 + func fetchUnreadsInfo() -> [DBUnreadInfo] } diff --git a/Nynja/DB/Models/DBUnreadInfo.swift b/Nynja/DB/Models/DBUnreadInfo.swift new file mode 100644 index 000000000..0592cc6c4 --- /dev/null +++ b/Nynja/DB/Models/DBUnreadInfo.swift @@ -0,0 +1,14 @@ +// +// DBUnreadInfo.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 12/17/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import GRDBCipher + +struct DBUnreadInfo: RowConvertible, Codable { + let id: String + let unread: Int64 +} diff --git a/Nynja/DBObserver.swift b/Nynja/DBObserver.swift index 995463616..280fa9d6f 100644 --- a/Nynja/DBObserver.swift +++ b/Nynja/DBObserver.swift @@ -176,17 +176,17 @@ final class DBObserver: StorageObserver, TransactionObserver { } private func handleContactDidCommit(changes: [ChangeInfo]) { - if changes.count == 1, let info = changes.first { - let contact = changedValue(from: info) as? DBContact - notify(with: info.event, entity: contact, type: .contact(nil)) - } else { - notify(with: [], type: .contact(nil)) - } + var storageChanges: [StorageChange] = [] changes.forEach { info in - let contact = changedValue(from: info) as? DBContact - notify(with: info.event, entity: contact, type: .contact(contact?.phoneId)) + let change = storageChange(from: info) + storageChanges.append(change) + + let contact = change.entity as? DBContact + notify(with: [change], type: .contact(contact?.phoneId)) } + + notify(with: storageChanges, type: .contact(nil)) } private func handleRoomDidCommit(changes: [ChangeInfo]) { -- GitLab From db30296b0687673c7ae1bff95ba167619cfd87b1 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Tue, 18 Dec 2018 00:59:16 +0200 Subject: [PATCH 38/41] [NY-5991] Sharing > Chats list is empty if user shares smth to Nynja (#1545) --- .../ForwardSelectorInteractor.swift | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift b/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift index da6bc445b..cc538af19 100644 --- a/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift +++ b/Nynja-Share/UI/ForwardSelector/Interactor/ForwardSelectorInteractor.swift @@ -353,7 +353,10 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P guard message.from == StorageService.sharedInstance.phoneId else { return } - handlerSuccess?() + dispatchAsyncMain { [weak self] in + self?.handlerSuccess?() + } + } @@ -365,7 +368,9 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P return } roster = myRoster - presenter.updateByState() + dispatchAsyncMain { [weak self] in + self?.presenter.updateByState() + } } @@ -374,13 +379,17 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P func didReceiveUpdatedContact(contact: Contact) { if [.ban, .banned].contains(contact.originalStatus) { contacts = contacts?.filter{ $0.content.contact?.phoneId != contact.phoneId } - presenter.updateContactsIfNeeded() + dispatchAsyncMain { [weak self] in + self?.presenter.updateContactsIfNeeded() + } } else if contact.originalStatus == .friend { if !(contacts?.contains(where: { $0.content.contact?.phoneId == contact.phoneId }) ?? false) { let target = makeForwardTarget(with: contact) contacts?.append(target) contacts = contacts?.sorted(by: sortComparator) - presenter.updateContactsIfNeeded() + dispatchAsyncMain { [weak self] in + self?.presenter.updateContactsIfNeeded() + } } } } @@ -389,11 +398,15 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P // MARK: IoHandlerDelegate func sessionNotFound() { - handlerServerSignals?(.sessionNotFound) + dispatchAsyncMain { [weak self] in + self?.handlerServerSignals?(.sessionNotFound) + } } func mismatchUserData() { - handlerServerSignals?(.mismatchUserData) + dispatchAsyncMain { [weak self] in + self?.handlerServerSignals?(.mismatchUserData) + } } -- GitLab From cd070f01ea02f05a39580bc475ee2bb16fb635c2 Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Tue, 18 Dec 2018 03:13:14 +0200 Subject: [PATCH 39/41] Added backswipe on contacts screen --- .../View/ViewController/ContactsViewController.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Nynja/Modules/Contacts/View/ViewController/ContactsViewController.swift b/Nynja/Modules/Contacts/View/ViewController/ContactsViewController.swift index c3422ff12..0324b414a 100644 --- a/Nynja/Modules/Contacts/View/ViewController/ContactsViewController.swift +++ b/Nynja/Modules/Contacts/View/ViewController/ContactsViewController.swift @@ -8,7 +8,7 @@ import UIKit -class ContactsViewController: BaseVC, ContactsViewProtocol, ContactCellDelegate, FastScrollable, KeyboardInteractive { +class ContactsViewController: BaseVC, ContactsViewProtocol, ContactCellDelegate, FastScrollable, KeyboardInteractive, BackSwipable { var presenter: ContactsPresenterProtocol! { didSet { @@ -23,6 +23,10 @@ class ContactsViewController: BaseVC, ContactsViewProtocol, ContactCellDelegate, return dataSource }() + private(set) lazy var swipeBackHelper: SwipeBackHelper = { + return SwipeBackHelper(with: self) + }() + var scrollBar: ScrollBar? var scrollOffset: CGFloat = 0 @@ -108,7 +112,7 @@ class ContactsViewController: BaseVC, ContactsViewProtocol, ContactCellDelegate, // MARK: - BaseVC override func initialize() { super.initialize() - + swipeBackHelper.addGesture() scrollBar = ScrollBar(scrollView: tableView) scrollBar?.verticalInset = ScrollBar.VerticalInset(top: 0, bottom: 0) scrollBar?.shouldShowAction = { [unowned self] in -- GitLab From 67ae53fac16c3d104f31fc0992a344385c2446aa Mon Sep 17 00:00:00 2001 From: Anton Makarov Date: Tue, 18 Dec 2018 04:23:38 +0200 Subject: [PATCH 40/41] 0.8.3 --- Nynja-Share/Resources/Info.plist | 2 +- Nynja.xcodeproj/project.pbxproj | 4 ++-- Nynja/Resources/Info.plist | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist index d116c1d1b..d23398583 100644 --- a/Nynja-Share/Resources/Info.plist +++ b/Nynja-Share/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 0.8.0 CFBundleVersion - 0.8.2 + 0.8.3 Config $(Config) ModelsVersion diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 5871845b5..49a45dfbf 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -17539,7 +17539,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "2a318f9e-d0ab-41dc-968a-e1cb13de4de5"; - PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_AdhocExt; + PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_AppstoreExt; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -17723,7 +17723,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "1971b115-2b2c-48b6-a20f-3686249e0a88"; - PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_Adhoc; + PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_Appstore; SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/Nynja/Resources/Info.plist b/Nynja/Resources/Info.plist index 3109daba2..e548e46a6 100644 --- a/Nynja/Resources/Info.plist +++ b/Nynja/Resources/Info.plist @@ -23,7 +23,7 @@ CFBundleShortVersionString 0.8.0 CFBundleVersion - 0.8.2 + 0.8.3 ConfServerAddress $(ConfServerAddress) ConfServerPort -- GitLab From 74f1954fd46a77339a5a76b8692ca6b32bb32cc9 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryhoriev Date: Tue, 18 Dec 2018 16:22:33 +0200 Subject: [PATCH 41/41] [NY-6185] Fix issue with `Incorrect delivery status for media message after text message sending` which was found on `Chat` screen. (#1546) --- .../MessageInteractor+StorageSubscriber.swift | 6 ++-- .../Interactor/MessageInteractor.swift | 34 +++++++++++++++---- .../Message/Presenter/MessagePresenter.swift | 8 ++--- .../Message/Protocols/MessageProtocols.swift | 6 ++-- Nynja/Modules/Message/View/MessageVC.swift | 17 +++++++--- Nynja/Services/Models/SendModel.swift | 5 +++ .../Models/Message/Message+Files.swift | 2 +- 7 files changed, 55 insertions(+), 23 deletions(-) diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift b/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift index 2d0a2fee0..09e44ecca 100644 --- a/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift +++ b/Nynja/Modules/Message/Interactor/MessageInteractor+StorageSubscriber.swift @@ -212,11 +212,11 @@ extension MessageInteractor { removeProgressIfNeeded(for: message) let config = createMessageConfiguration(message) presenter?.updateMessage(with: config) - } else { + } else if let serverId = message.id { if isMyselfChat { - presenter?.messageRead(localId) + presenter?.messageRead(localId, serverId: serverId) } else { - presenter?.messageSent(localId) + presenter?.messageSent(localId, serverId: serverId) } } } diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor.swift b/Nynja/Modules/Message/Interactor/MessageInteractor.swift index fde19574b..ced1d8a03 100644 --- a/Nynja/Modules/Message/Interactor/MessageInteractor.swift +++ b/Nynja/Modules/Message/Interactor/MessageInteractor.swift @@ -1013,18 +1013,38 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H private func notifyAboutRead(withOld oldReader: MessageServerId?, new newReader: MessageServerId?) { guard let newReader = newReader, oldReader != newReader, - let localId = configuration.messages + let message = message(reader: newReader), + let localId = message.msg_id, + let serverId = message.id else { + return + } + + presenter?.messageRead(localId, serverId: serverId) + } + + private func message(reader: MessageServerId) -> Message? { + func message(isEqual: Bool) -> Message? { + return configuration.messages .last(where: { (message) -> Bool in guard let id = message.id else { return false } - return id <= newReader - })? - .msg_id else { - return + + if isEqual { + return id == reader + } else { + return id <= reader + } + }) + } + + if let message = message(isEqual: true) { + return message + } else if let message = message(isEqual: false) { + return message + } else { + return nil } - - presenter?.messageRead(localId) } private func updateContact(_ contact: Contact) { diff --git a/Nynja/Modules/Message/Presenter/MessagePresenter.swift b/Nynja/Modules/Message/Presenter/MessagePresenter.swift index 8b9aac0ad..372619dfe 100644 --- a/Nynja/Modules/Message/Presenter/MessagePresenter.swift +++ b/Nynja/Modules/Message/Presenter/MessagePresenter.swift @@ -978,16 +978,16 @@ class MessagePresenter: BasePresenter, MessagePresenterProtocol, MessageInteract view.updateHeaderStatus(lastStatus) } - func messageSent(_ localId: String) { - view.updateDeliveryStatus(.sent, messageId: localId) + func messageSent(_ localId: MessageLocalId, serverId: MessageServerId) { + view.updateDeliveryStatus(.sent, localId: localId, serverId: serverId) } func update(sender: MessageSender, inMessagesWithIds messageIds: [String]) { view.update(sender: sender, inMessagesWithIds: messageIds) } - func messageRead(_ localId: String) { - view.updateDeliveryStatus(.read, messageId: localId) + func messageRead(_ localId: MessageLocalId, serverId: MessageServerId) { + view.updateDeliveryStatus(.read, localId: localId, serverId: serverId) } func starMessage(with localId: MessageLocalId, starId: MessageLocalId) { diff --git a/Nynja/Modules/Message/Protocols/MessageProtocols.swift b/Nynja/Modules/Message/Protocols/MessageProtocols.swift index 58b3050bd..3147d52c1 100644 --- a/Nynja/Modules/Message/Protocols/MessageProtocols.swift +++ b/Nynja/Modules/Message/Protocols/MessageProtocols.swift @@ -160,8 +160,8 @@ protocol MessageInteractorOutputProtocol: class, MentionFetchOutputProtocol, Mes func actionStatusChanged(_ status: ActionStatus) func restoreStatus() - func messageSent(_ localId: MessageLocalId) - func messageRead(_ localId: MessageLocalId) + func messageSent(_ localId: MessageLocalId, serverId: MessageServerId) + func messageRead(_ localId: MessageLocalId, serverId: MessageServerId) func update(sender: MessageSender, inMessagesWithIds messageIds: [String]) @@ -303,7 +303,7 @@ protocol MessageViewProtocol: class { func scrollToBottom() func updateHeaderStatus(_ status: String) - func updateDeliveryStatus(_ status: DeliveryStatus, messageId: String) + func updateDeliveryStatus(_ status: DeliveryStatus, localId: MessageLocalId, serverId: MessageServerId) func removeMessage(_ messageId: MessageLocalId, isForAllUsers: Bool) func update(sender: MessageSender, inMessagesWithIds messageIds: [String]) diff --git a/Nynja/Modules/Message/View/MessageVC.swift b/Nynja/Modules/Message/View/MessageVC.swift index 67f03c306..a2ee470f1 100644 --- a/Nynja/Modules/Message/View/MessageVC.swift +++ b/Nynja/Modules/Message/View/MessageVC.swift @@ -1143,11 +1143,14 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw avatarView.isUserInteractionEnabled = !isBlock } - func updateDeliveryStatus(_ status: DeliveryStatus, messageId: String) { - guard let index = messageDS.index(where: { $0.id == messageId }) else { + func updateDeliveryStatus(_ status: DeliveryStatus, localId: MessageLocalId, serverId: MessageServerId) { + guard let index = messageDS.index(where: { $0.id == localId }) else { return } + let model = messageDS.cellModel(at: index) + model.serverID = serverId + if status != .read, model.deliveryStatus != .read { model.deliveryStatus = status } else { @@ -1380,19 +1383,23 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw private func markMesssagesAsRead(from presentationIndex: Int) { for i in (presentationIndex.. Bool { -- GitLab