From e75cedab3795012668ee8508aeed16ba555fc466 Mon Sep 17 00:00:00 2001 From: Nikita Kaberov Date: Thu, 17 Oct 2024 16:58:53 +0300 Subject: [PATCH] Version 3.42.0 --- Cartfile | 2 +- Example/Podfile | 8 +- Example/PrivacyInfo.xcprivacy | 1 - Example/Tests/AbstractRequestLoopTests.swift | 4 +- Example/Tests/ActionRequestLoopTests.swift | 4 +- Example/Tests/DeltaCallbackTests.swift | 57 ++-- Example/Tests/DeltaRequestLoopTests.swift | 8 +- Example/Tests/DeltaResponseTests.swift | 4 +- .../ChatTableViewControllerTests.swift | 6 +- Example/Tests/FAQActionsTests.swift | 6 +- Example/Tests/FAQClientTests.swift | 4 +- Example/Tests/FileUrlCreatorTests.swift | 16 +- Example/Tests/FullUpdateTests.swift | 4 +- Example/Tests/HistoryPollerTests.swift | 7 +- Example/Tests/InternalUtilsTests.swift | 2 +- Example/Tests/KeyboardItemTests.swift | 16 +- .../Tests/LocationSettingsResponseTests.swift | 4 +- Example/Tests/MemoryHistoryStorageTests.swift | 3 +- Example/Tests/MessageFactoriesTests.swift | 3 +- Example/Tests/MessageHolderTests.swift | 49 ++-- Example/Tests/MessageImplTests.swift | 163 ++++++----- Example/Tests/MessageStreamImplTests.swift | 8 +- Example/Tests/MessageTrackerImplTests.swift | 36 ++- .../Tests/Mocks/ActionRequestLoopMock.swift | 4 +- Example/Tests/Mocks/MessageImplMockData.swift | 2 +- .../Tests/RemoteHistoryProviderTests.swift | 7 +- Example/Tests/SQLiteHistoryStorageTests.swift | 13 +- Example/Tests/WebimActionsTests.swift | 7 +- Example/Tests/WebimClientTests.swift | 44 +-- Example/Tests/WebimSessionImplTests.swift | 40 +-- .../CircleProgressIndicator.swift | 125 ++++++++ Example/WebimClientShare/Extensions.swift | 59 ++++ .../WebimClientShare/SendFileManager.swift | 39 +++ .../WMShareProgressViewController.swift | 121 ++++++++ .../WMShareProgressViewController.xib | 84 ++++++ .../cells/ShareProgressTableViewCell.swift | 57 ++++ .../cells/ShareProgressTableViewCell.xib | 88 ++++++ .../WMShareViewController.swift | 98 +++++-- .../WebimMobileSDK.xcodeproj/project.pbxproj | 276 +++++++++++------- Example/WebimMobileSDK/AppDelegate.swift | 2 + .../AppearanceSettings/ImageConstants.swift | 1 + .../Base.lproj/Localizable.strings | 12 +- Example/WebimMobileSDK/Info.plist | 2 +- .../Models/ExternalWidgetBuilder.swift | 50 ++-- .../WebimMobileSDK/Utilities/URLRequest.swift | 16 + .../LaunchScreenController.swift | 3 + .../WMSettingsViewController+Setup.swift | 3 + .../WMSettingsViewController.swift | 1 + .../ar.lproj/WMSettingsViewController.strings | 4 +- .../bg.lproj/WMSettingsViewController.strings | 4 +- .../es.lproj/WMSettingsViewController.strings | 4 +- .../he.lproj/WMSettingsViewController.strings | 4 +- .../pt.lproj/WMSettingsViewController.strings | 4 +- .../WMSettingsViewController.strings | 4 +- .../WMStartViewController.swift | 5 +- .../ar.lproj/WMStartViewController.strings | 2 +- .../bg.lproj/WMStartViewController.strings | 2 +- .../en.lproj/WMStartViewController.strings | 2 +- .../pt.lproj/WMStartViewController.strings | 4 +- .../ru.lproj/WMStartViewController.strings | 2 +- .../uk-UA.lproj/WMStartViewController.strings | 2 +- .../ar.lproj/Localizable.strings | 14 +- .../WebimMobileSDK/bg.lproj/InfoPlist.strings | 10 +- .../bg.lproj/Localizable.strings | 10 + .../WebimMobileSDK/en.lproj/InfoPlist.strings | 10 +- .../en.lproj/Localizable.strings | 14 +- .../WebimMobileSDK/es.lproj/InfoPlist.strings | 10 +- .../es.lproj/Localizable.strings | 10 + .../WebimMobileSDK/he.lproj/InfoPlist.strings | 10 +- .../he.lproj/Localizable.strings | 13 +- .../WebimMobileSDK/pt.lproj/InfoPlist.strings | 10 +- .../pt.lproj/Localizable.strings | 14 +- .../ru.lproj/Localizable.strings | 14 +- .../uk-UA.lproj/InfoPlist.strings | 8 +- .../uk-UA.lproj/Localizable.strings | 10 + LICENSE | 2 +- README.md | 7 +- WebimMobileSDK.podspec | 2 +- .../Backend/AbstractRequestLoop.swift | 17 +- .../Backend/ActionRequestLoop.swift | 10 +- WebimMobileSDK/Backend/DeltaRequestLoop.swift | 20 +- WebimMobileSDK/Backend/FAQActions.swift | 21 +- WebimMobileSDK/Backend/FAQClient.swift | 5 +- WebimMobileSDK/Backend/FAQRequestLoop.swift | 5 +- WebimMobileSDK/Backend/FileUrlCreator.swift | 3 +- .../Backend/Items/AccountConfigItem.swift | 20 ++ .../Backend/Items/HistoryRevisionItem.swift | 9 +- .../Items/Responses/DeltaResponse.swift | 9 +- WebimMobileSDK/Backend/MessageHolder.swift | 37 +++ .../Backend/SQLiteHistoryStorage.swift | 67 ++++- .../Utilities/Extensions/Dictionary.swift | 12 +- .../Backend/Utilities/InternalUtils.swift | 12 +- WebimMobileSDK/Backend/WebimActionsImpl.swift | 66 ++--- WebimMobileSDK/Backend/WebimClient.swift | 15 +- .../Implementation/MessageStreamImpl.swift | 47 +++ .../Implementation/MessageTrackerImpl.swift | 14 + .../WebimServerSideSettings.swift | 1 + .../Implementation/WebimSessionImpl.swift | 3 +- WebimMobileSDK/Message.swift | 6 +- WebimMobileSDK/MessageStream.swift | 50 +++- 100 files changed, 1647 insertions(+), 582 deletions(-) create mode 100644 Example/WebimClientShare/CircleProgressIndicator.swift create mode 100644 Example/WebimClientShare/Extensions.swift create mode 100644 Example/WebimClientShare/SendFileManager.swift create mode 100644 Example/WebimClientShare/WMShareProgressViewController/WMShareProgressViewController.swift create mode 100644 Example/WebimClientShare/WMShareProgressViewController/WMShareProgressViewController.xib create mode 100644 Example/WebimClientShare/WMShareProgressViewController/cells/ShareProgressTableViewCell.swift create mode 100644 Example/WebimClientShare/WMShareProgressViewController/cells/ShareProgressTableViewCell.xib create mode 100644 Example/WebimMobileSDK/Utilities/URLRequest.swift diff --git a/Cartfile b/Cartfile index 590124bb..da08c9bb 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "stephencelis/SQLite.swift" == 0.13.3 +github "stephencelis/SQLite.swift" == 0.15.0 diff --git a/Example/Podfile b/Example/Podfile index 878280cb..cf335ae4 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -1,4 +1,4 @@ -platform :ios, '11.0' +platform :ios, '13.0' workspace 'WebimMobileSDK' use_frameworks! @@ -6,9 +6,9 @@ install! 'cocoapods', :deterministic_uuids => false target 'WebimMobileSDK_Example' do pod 'WebimMobileSDK' - pod 'WebimMobileWidget', '1.2.1' + pod 'WebimMobileWidget', '1.2.4' pod 'WebimKeyboard', '1.0.2' - + pod 'Firebase/Crashlytics' pod 'Firebase/Analytics' @@ -26,7 +26,7 @@ target 'WebimMobileSDK_Example' do installer.pods_project.targets.each do |target| target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' config.build_settings['SWIFT_VERSION'] = '5.0' end diff --git a/Example/PrivacyInfo.xcprivacy b/Example/PrivacyInfo.xcprivacy index 57cd3b04..efd3f654 100644 --- a/Example/PrivacyInfo.xcprivacy +++ b/Example/PrivacyInfo.xcprivacy @@ -23,7 +23,6 @@ NSPrivacyAccessedAPITypes - diff --git a/Example/Tests/AbstractRequestLoopTests.swift b/Example/Tests/AbstractRequestLoopTests.swift index 47f2a8fc..f8ef7c34 100644 --- a/Example/Tests/AbstractRequestLoopTests.swift +++ b/Example/Tests/AbstractRequestLoopTests.swift @@ -32,7 +32,9 @@ class AbstractRequestLoopTests: XCTestCase { // MARK: - Properties private let abstractRequestLoop = AbstractRequestLoopForTests(completionHandlerExecutor: nil, - internalErrorListener: nil) + internalErrorListener: nil, + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) private let webimServerSideSettingsResponse = """ webimApplyServerSideSettings({ diff --git a/Example/Tests/ActionRequestLoopTests.swift b/Example/Tests/ActionRequestLoopTests.swift index a31c1c5d..5407943a 100644 --- a/Example/Tests/ActionRequestLoopTests.swift +++ b/Example/Tests/ActionRequestLoopTests.swift @@ -36,7 +36,9 @@ class ActionRequestLoopTests: XCTestCase { // MARK: - Properties private let actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: SessionDestroyer(userDefaultsKey: ActionRequestLoopTests.userDefaultsKey), queue: DispatchQueue.global()), - internalErrorListener: InternalErrorListenerForTests() as InternalErrorListener) + internalErrorListener: InternalErrorListenerForTests() as InternalErrorListener, + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) // MARK: - Tests func testStart() { diff --git a/Example/Tests/DeltaCallbackTests.swift b/Example/Tests/DeltaCallbackTests.swift index 679c2ebd..159dbc0e 100644 --- a/Example/Tests/DeltaCallbackTests.swift +++ b/Example/Tests/DeltaCallbackTests.swift @@ -54,7 +54,7 @@ class DeltaCallbackTests: XCTestCase { let queue = DispatchQueue.main let execIfNotDestroyedHandlerExecutor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: sessionDestroyer, queue: queue) let listener = InternalErrorListenerForTests() - actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, internalErrorListener: listener) + actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, internalErrorListener: listener, requestHeader: nil, baseURL: MessageImplMockData.serverURLString.rawValue) let currentChatMessageMapper = CurrentChatMessageMapper(withServerURLString: serverURLString) let sendingFactory = SendingFactory(withServerURLString: serverURLString) let operatorFactory = OperatorFactory(withServerURLString: serverURLString) @@ -64,8 +64,7 @@ class DeltaCallbackTests: XCTestCase { let memHistoryMetaInfo = MemoryHistoryMetaInformationStorage() let locationSettingsHolder = LocationSettingsHolder(userDefaultsKey: locationSettingsHolderUserDefaultsKey) - webimActions = WebimActionsImpl(baseURL: serverURLString, - actionRequestLoop: actionRequestLoop) + webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) let messageComposingHandler = MessageComposingHandler(webimActions: webimActions!, queue: queue) let remoteHistoryProvider = RemoteHistoryProvider(webimActions: webimActions!, @@ -77,6 +76,7 @@ class DeltaCallbackTests: XCTestCase { reachedEndOfRemoteHistory: true) messageStream = MessageStreamImpl(serverURLString: serverURLString, + location: "mobile", currentChatMessageFactoriesMapper: currentChatMessageMapper, sendingMessageFactory: sendingFactory, operatorFactory: operatorFactory, @@ -622,31 +622,32 @@ class MessageMapperForTests: MessageMapper { override func map(message: MessageItem) -> MessageImpl? { return MessageImpl(serverURLString: "Some", - clientSideID: "Some", - serverSideID: nil, - keyboard: nil, - keyboardRequest: nil, - operatorID: nil, - quote: nil, - senderAvatarURLString: "Some", - senderName: "Some", - sendStatus: .sent, - sticker: nil, - type: .visitorMessage, - rawData: nil, - data: nil, - text: "Some", - timeInMicrosecond: 12, - historyMessage: false, - internalID: nil, - rawText: nil, - read: false, - messageCanBeEdited: false, - messageCanBeReplied: false, - messageIsEdited: false, - visitorReactionInfo: nil, - visitorCanReact: nil, - visitorChangeReaction: nil) + clientSideID: "Some", + serverSideID: nil, + keyboard: nil, + keyboardRequest: nil, + operatorID: nil, + quote: nil, + senderAvatarURLString: "Some", + senderName: "Some", + sendStatus: .sent, + sticker: nil, + type: .visitorMessage, + rawData: nil, + data: nil, + text: "Some", + timeInMicrosecond: 12, + historyMessage: false, + internalID: nil, + rawText: nil, + read: false, + messageCanBeEdited: false, + messageCanBeReplied: false, + messageIsEdited: false, + visitorReactionInfo: nil, + visitorCanReact: nil, + visitorChangeReaction: nil, + group: nil) } } diff --git a/Example/Tests/DeltaRequestLoopTests.swift b/Example/Tests/DeltaRequestLoopTests.swift index 3b6f166d..beb87356 100644 --- a/Example/Tests/DeltaRequestLoopTests.swift +++ b/Example/Tests/DeltaRequestLoopTests.swift @@ -61,7 +61,8 @@ class DeltaRequestLoopTests: XCTestCase { visitorJSONString: nil, sessionID: nil, prechat: nil, - authorizationData: nil) + authorizationData: nil, + requestHeader: nil) } override func tearDown() { @@ -104,7 +105,7 @@ class DeltaRequestLoopTests: XCTestCase { XCTAssertNil(deltaRequestLoop?.getAuthorizationData()) XCTAssertEqual(deltaRequestLoop!.since, - 0) + "0") XCTAssertTrue(deltaRequestLoop!.initializationRunned) } @@ -113,7 +114,8 @@ class DeltaRequestLoopTests: XCTestCase { // When: DeltaRequestLoop started without AuthorizationData. deltaRequestLoop?.start() - usleep(1_000_000) + deltaRequestLoop?.resume() + usleep(5_000_000) // Then: Initialization should be requested, but delta should not. XCTAssertNil(deltaRequestLoop?.getAuthorizationData()) diff --git a/Example/Tests/DeltaResponseTests.swift b/Example/Tests/DeltaResponseTests.swift index 636ac921..e980eabf 100644 --- a/Example/Tests/DeltaResponseTests.swift +++ b/Example/Tests/DeltaResponseTests.swift @@ -33,7 +33,7 @@ class DeltaResponseTests: XCTestCase { // MARK: - Constants private static let DELTA_RESPONSE_JSON_STRING = """ { - "revision" : 12, + "revision" : "12", "deltaList" : [ { "id" : "80a332f6fced40f290a5e8ace4a6d11c", @@ -76,7 +76,7 @@ class DeltaResponseTests: XCTestCase { let deltaResponseItem = DeltaResponse(jsonDictionary: deltaResponseDictionary) XCTAssertEqual(deltaResponseItem.getRevision(), - 12) + "12") XCTAssertNil(deltaResponseItem.getFullUpdate()) XCTAssertEqual(deltaResponseItem.getDeltaList()!.count, 3) diff --git a/Example/Tests/ExampleTests/ChatTableViewControllerTests.swift b/Example/Tests/ExampleTests/ChatTableViewControllerTests.swift index c118214d..60955d54 100644 --- a/Example/Tests/ExampleTests/ChatTableViewControllerTests.swift +++ b/Example/Tests/ExampleTests/ChatTableViewControllerTests.swift @@ -28,11 +28,12 @@ import UIKit import XCTest @testable import WebimMobileSDK @testable import WebimMobileSDK_Example +@testable import WebimMobileWidget class ChatViewControllerTests: XCTestCase { // MARK: - Properties - var chatTableViewController = ChatViewController.loadViewControllerFromXib() + var chatTableViewController = ChatViewController() // MARK: - Methods override func setUp() { @@ -68,7 +69,8 @@ class ChatViewControllerTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) messages.append(message as Message) } diff --git a/Example/Tests/FAQActionsTests.swift b/Example/Tests/FAQActionsTests.swift index 905e2c98..02789da2 100644 --- a/Example/Tests/FAQActionsTests.swift +++ b/Example/Tests/FAQActionsTests.swift @@ -33,7 +33,7 @@ class FAQActionsTests: XCTestCase { let faqDestroyer = FAQDestroyer() let queue = DispatchQueue.global() - let baseURL = "https://wmtest6.webim.ru/" + let baseURL = "https://wmtest6.webim.ru" var completionHandlerExecutor: ExecIfNotDestroyedFAQHandlerExecutor! var faqRequestLoopMock: FAQRequestLoopMock! var sut: FAQActions! @@ -42,8 +42,8 @@ class FAQActionsTests: XCTestCase { override func setUp() { super.setUp() completionHandlerExecutor = ExecIfNotDestroyedFAQHandlerExecutor(faqDestroyer: faqDestroyer, queue: queue) - faqRequestLoopMock = FAQRequestLoopMock(completionHandlerExecutor: completionHandlerExecutor) - sut = FAQActions(baseURL: baseURL, faqRequestLoop: faqRequestLoopMock) + faqRequestLoopMock = FAQRequestLoopMock(completionHandlerExecutor: completionHandlerExecutor, baseURL: baseURL) + sut = FAQActions(faqRequestLoop: faqRequestLoopMock) } override func tearDown() { diff --git a/Example/Tests/FAQClientTests.swift b/Example/Tests/FAQClientTests.swift index c20634c8..013e24f6 100644 --- a/Example/Tests/FAQClientTests.swift +++ b/Example/Tests/FAQClientTests.swift @@ -45,8 +45,8 @@ class FAQClientTests: XCTestCase { let queue = DispatchQueue.global() let completionHandlerExecutor = ExecIfNotDestroyedFAQHandlerExecutor(faqDestroyer: destroyer, queue: queue) baseURL = "https://wmtest6.webim.ru/" - faqRequestLoop = FAQRequestLoopMock(completionHandlerExecutor: completionHandlerExecutor) - faqActions = FAQActions(baseURL: baseURL, faqRequestLoop: faqRequestLoop) + faqRequestLoop = FAQRequestLoopMock(completionHandlerExecutor: completionHandlerExecutor, baseURL: baseURL) + faqActions = FAQActions(faqRequestLoop: faqRequestLoop) sut = FAQClient(withFAQRequestLoop: faqRequestLoop, diff --git a/Example/Tests/FileUrlCreatorTests.swift b/Example/Tests/FileUrlCreatorTests.swift index 7ab48bbc..39e68d21 100644 --- a/Example/Tests/FileUrlCreatorTests.swift +++ b/Example/Tests/FileUrlCreatorTests.swift @@ -53,7 +53,7 @@ class FileUrlCreatorTests: XCTestCase { globalQueue = DispatchQueue.global() execIfNotDestroyedHandlerExecutor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: sessionDestroyer, queue: globalQueue) internalErrorListener = InternalErrorListenerForTests() - actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, internalErrorListener: internalErrorListener) + actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, internalErrorListener: internalErrorListener, requestHeader: nil, baseURL: MessageImplMockData.serverURLString.rawValue) authData = AuthorizationData(pageID: expectedPageID, authorizationToken: expectedAuthorizationToken) deltaRequestLoop = DeltaRequestLoop( @@ -77,14 +77,13 @@ class FileUrlCreatorTests: XCTestCase { visitorJSONString: nil, sessionID: nil, prechat: nil, - authorizationData: authData) + authorizationData: authData, + requestHeader: nil) webimClient = WebimClient( withActionRequestLoop: actionRequestLoop, deltaRequestLoop: deltaRequestLoop, - webimActions: WebimActionsImpl( - baseURL: urlString, - actionRequestLoop: actionRequestLoop)) + webimActions: WebimActionsImpl(actionRequestLoop: actionRequestLoop)) } override func tearDown() { @@ -157,14 +156,13 @@ class FileUrlCreatorTests: XCTestCase { visitorJSONString: nil, sessionID: nil, prechat: nil, - authorizationData: nil) + authorizationData: nil, + requestHeader: nil) webimClient = WebimClient( withActionRequestLoop: actionRequestLoop, deltaRequestLoop: deltaRequestLoop, - webimActions: WebimActionsImpl( - baseURL: urlString, - actionRequestLoop: actionRequestLoop)) + webimActions: WebimActionsImpl(actionRequestLoop: actionRequestLoop)) let sut = FileUrlCreator(webimClient: webimClient, serverURL: urlString) diff --git a/Example/Tests/FullUpdateTests.swift b/Example/Tests/FullUpdateTests.swift index 55d62c2e..20d2865c 100644 --- a/Example/Tests/FullUpdateTests.swift +++ b/Example/Tests/FullUpdateTests.swift @@ -142,7 +142,7 @@ class FullUpdateTests: XCTestCase { ], "offline" : false, "visitorMessageDraft" : null, - "id" : "5123124", + "id" : 5123124, "unreadByVisitorSinceTs" : null, "operatorIdToRate" : { }, "creationTs" : 1519040829.056129, @@ -198,7 +198,7 @@ class FullUpdateTests: XCTestCase { func testInitChat() { let chatItemJson = """ { - "id" : "5123124", + "id" : 5123124, "clientSideId" : "0134e9d90e0eb95884d880860382c8ab" } diff --git a/Example/Tests/HistoryPollerTests.swift b/Example/Tests/HistoryPollerTests.swift index d981cbd2..65979acf 100644 --- a/Example/Tests/HistoryPollerTests.swift +++ b/Example/Tests/HistoryPollerTests.swift @@ -49,11 +49,12 @@ class HistoryPollerTests: XCTestCase { let executor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: sessionDestroyer, queue: queue) let internalErrorListener = InternalErrorListenerForTests() actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: executor, - internalErrorListener: internalErrorListener) + internalErrorListener: internalErrorListener, + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) let accessChecker = AccessChecker(thread: Thread.current, sessionDestroyer: sessionDestroyer) - let webimActions = WebimActionsImpl(baseURL: serverURLString, - actionRequestLoop: actionRequestLoop) + let webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) let remoteHistoryProvider = RemoteHistoryProviderMock(withWebimActions: webimActions, historyMessageMapper: HistoryMessageMapper(withServerURLString: MessageImplMockData.serverURLString.rawValue), historyMetaInformation: MemoryHistoryMetaInformationStorage(), diff --git a/Example/Tests/InternalUtilsTests.swift b/Example/Tests/InternalUtilsTests.swift index e7e3a9ed..05086bc8 100644 --- a/Example/Tests/InternalUtilsTests.swift +++ b/Example/Tests/InternalUtilsTests.swift @@ -57,7 +57,7 @@ class InternalUtilsTests: XCTestCase { func test_GetCurrentTimeInMicrosecond() { let expectedTime = Int64(Date().timeIntervalSince1970 * 1_000_000) - XCTAssertEqual(InternalUtils.getCurrentTimeInMicrosecond(), expectedTime, accuracy: 10) + XCTAssertEqual(InternalUtils.getCurrentTimeInMicrosecond(), expectedTime, accuracy: 100) } func test_ParseRemoteNotification_EmptyValue() { diff --git a/Example/Tests/KeyboardItemTests.swift b/Example/Tests/KeyboardItemTests.swift index 69ca4ee0..68c25387 100644 --- a/Example/Tests/KeyboardItemTests.swift +++ b/Example/Tests/KeyboardItemTests.swift @@ -473,7 +473,7 @@ class ConfigurationItemTests: XCTestCase { let sut = ConfigurationItem(jsonDictionary: convertToDict(nullIsActiveJson)) - XCTAssertNil(sut) + XCTAssertNil(sut.isActive()) } func test_Init_DataLink() { @@ -497,8 +497,8 @@ class ConfigurationItemTests: XCTestCase { let sut = ConfigurationItem(jsonDictionary: convertToDict(nullDataJson)) - XCTAssertEqual(sut?.getData(), expectedData) - XCTAssertEqual(sut?.getButtonType(), expectedType) + XCTAssertEqual(sut.getData(), expectedData) + XCTAssertEqual(sut.getButtonType(), expectedType) } func test_Init_DataNullLinkValue() { @@ -512,7 +512,7 @@ class ConfigurationItemTests: XCTestCase { let sut = ConfigurationItem(jsonDictionary: convertToDict(nullIsDataJson)) - XCTAssertNil(sut) + XCTAssertNil(sut.getButtonType()) } func test_Init_DataNullTextValue() { @@ -526,7 +526,7 @@ class ConfigurationItemTests: XCTestCase { let sut = ConfigurationItem(jsonDictionary: convertToDict(nullDataJson)) - XCTAssertNil(sut) + XCTAssertNil(sut.getButtonType()) } func test_Init_StateShowing() { @@ -547,7 +547,7 @@ class ConfigurationItemTests: XCTestCase { let sut = ConfigurationItem(jsonDictionary: convertToDict(showingSelectedStateJson)) - XCTAssertEqual(sut?.getState(), expectedValue) + XCTAssertEqual(sut.getState(), expectedValue) } func test_Init_StateWrongValue() { @@ -562,7 +562,7 @@ class ConfigurationItemTests: XCTestCase { let sut = ConfigurationItem(jsonDictionary: convertToDict(wrongStateJson)) - XCTAssertEqual(sut?.getState(), expectedValue) + XCTAssertEqual(sut.getState(), expectedValue) } func test_Init_StateNullValue() { @@ -576,7 +576,7 @@ class ConfigurationItemTests: XCTestCase { let sut = ConfigurationItem(jsonDictionary: convertToDict(nullStateJson)) - XCTAssertNil(sut) + XCTAssertNil(sut.getState()) } } diff --git a/Example/Tests/LocationSettingsResponseTests.swift b/Example/Tests/LocationSettingsResponseTests.swift index 3ed99e2c..a5e6920e 100644 --- a/Example/Tests/LocationSettingsResponseTests.swift +++ b/Example/Tests/LocationSettingsResponseTests.swift @@ -48,14 +48,14 @@ class LocationSettingsResponseTests: XCTestCase { } """ - let sut = LocationSettingsResponse(jsonDictionary: convertToDict(defaultLocationSettingsResponseJson)) + let sut = ServerSettingsResponse(jsonDictionary: convertToDict(defaultLocationSettingsResponseJson)) XCTAssertEqual(sut.getLocationSettings()[firstKey] as? Int, 12) XCTAssertEqual(sut.getLocationSettings()[secondKey] as? String, "someValue") } func test_Init_LocationSettingNullValue() { - let sut = LocationSettingsResponse(jsonDictionary: [:]) + let sut = ServerSettingsResponse(jsonDictionary: [:]) XCTAssertNotNil(sut) XCTAssertTrue(sut.getLocationSettings().isEmpty) diff --git a/Example/Tests/MemoryHistoryStorageTests.swift b/Example/Tests/MemoryHistoryStorageTests.swift index 81eeea20..18fb42dc 100644 --- a/Example/Tests/MemoryHistoryStorageTests.swift +++ b/Example/Tests/MemoryHistoryStorageTests.swift @@ -63,7 +63,8 @@ class MemoryHistoryStorageTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil)) + visitorChangeReaction: nil, + group: nil)) } return messages diff --git a/Example/Tests/MessageFactoriesTests.swift b/Example/Tests/MessageFactoriesTests.swift index b695255c..97be2207 100644 --- a/Example/Tests/MessageFactoriesTests.swift +++ b/Example/Tests/MessageFactoriesTests.swift @@ -167,7 +167,8 @@ class SendingFactoryTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) let messageToSend = sendingFactory.createTextMessageToSendWithQuoteWith( id: "someMessageID", diff --git a/Example/Tests/MessageHolderTests.swift b/Example/Tests/MessageHolderTests.swift index 4c66f70a..70cc1c15 100644 --- a/Example/Tests/MessageHolderTests.swift +++ b/Example/Tests/MessageHolderTests.swift @@ -71,8 +71,9 @@ class MessageHolderTests: XCTestCase { messageCanBeReplied: false, messageIsEdited: false, visitorReactionInfo: nil, - visitorCanReact: nil, - visitorChangeReaction: nil)) + visitorCanReact: false, + visitorChangeReaction: false, + group: nil)) } messagesCount = messagesCount + numberOfMessages @@ -108,7 +109,8 @@ class MessageHolderTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil)) + visitorChangeReaction: nil, + group: nil)) } messagesCount = messagesCount + numberOfMessages @@ -144,7 +146,8 @@ class MessageHolderTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: message.getVisitorReaction(), visitorCanReact: message.canVisitorReact(), - visitorChangeReaction: message.canVisitorChangeReaction()) + visitorChangeReaction: message.canVisitorChangeReaction(), + group: nil) result.append(newMessage) } @@ -178,7 +181,8 @@ class MessageHolderTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) } private func newEdited(currentChatMessage: MessageImpl) -> MessageImpl { @@ -206,7 +210,8 @@ class MessageHolderTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) } private func newEdited(historyMessage: MessageImpl) -> MessageImpl { @@ -234,7 +239,8 @@ class MessageHolderTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) } private func newMessageHolder(withHistory history: [MessageImpl] = [MessageImpl]()) -> MessageHolder { @@ -244,9 +250,10 @@ class MessageHolderTests: XCTestCase { let execIfNotDestroyedHandlerExecutor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: sessionDestroyer, queue: DispatchQueue.global(qos: .userInteractive)) let actionRequestLoop = ActionRequestLoop(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, - internalErrorListener: InternalErrorListenerForTests()) - let webimActions = WebimActionsImpl(baseURL: MessageImplMockData.serverURLString.rawValue, - actionRequestLoop: actionRequestLoop) + internalErrorListener: InternalErrorListenerForTests(), + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) + let webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) let remoteHistoryProvider = RemoteHistoryProviderMock(withWebimActions: webimActions, historyMessageMapper: HistoryMessageMapper(withServerURLString: MessageImplMockData.serverURLString.rawValue), historyMetaInformation: MemoryHistoryMetaInformationStorage(), @@ -266,9 +273,10 @@ class MessageHolderTests: XCTestCase { let execIfNotDestroyedHandlerExecutor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: sessionDestroyer, queue: DispatchQueue.global(qos: .userInteractive)) let actionRequestLoop = ActionRequestLoop(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, - internalErrorListener: InternalErrorListenerForTests()) - let webimActions = WebimActionsImpl(baseURL: MessageImplMockData.serverURLString.rawValue, - actionRequestLoop: actionRequestLoop) + internalErrorListener: InternalErrorListenerForTests(), + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) + let webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) let remoteHistoryProvider = RemoteHistoryProviderMock(withWebimActions: webimActions, historyMessageMapper: HistoryMessageMapper(withServerURLString: MessageImplMockData.serverURLString.rawValue), historyMetaInformation: MemoryHistoryMetaInformationStorage(), @@ -843,7 +851,8 @@ class MessageHolderTests: XCTestCase { completionHandlerMessages = messages as? [MessageImpl] } // Then: Completion handler should be called on last history part message. - XCTAssertEqual(completionHandlerMessages!, [currentChat[0]]) + XCTAssertEqual(completionHandlerMessages!.count, 1) + XCTAssertEqual(completionHandlerMessages![0].getID(), currentChat[0].getID()) } func testMergeChat() throws { @@ -908,8 +917,8 @@ class MessageHolderTests: XCTestCase { // MARK: Model set up let messageHolder = newMessageHolder() let messageTracker = try messageHolder.newMessageTracker(withMessageListener: self) - let firstChat = ChatItem(id: "1") - let secondChat = ChatItem(id: "2") + let firstChat = ChatItem(id: 1) + let secondChat = ChatItem(id: 2) let messages = generateCurrentChat(ofCount: 10) // MARK: Test 1 @@ -975,8 +984,8 @@ class MessageHolderTests: XCTestCase { let history2 = generateHistoryFrom(currentChat: messages) let messageHolder = newMessageHolder(withHistory: (history1 + history2)) let messageTracker = try messageHolder.newMessageTracker(withMessageListener: self) - let firstChat = ChatItem(id: "1") - let secondChat = ChatItem(id: "2") + let firstChat = ChatItem(id: 1) + let secondChat = ChatItem(id: 2) // MARK: Test 1 // When: Requesting messages. @@ -1076,8 +1085,8 @@ class MessageHolderTests: XCTestCase { let messageHolder = newMessageHolder(withHistory: (history1 + history2), localHistory: Array(history2[0 ... 1])) let messageTracker = try messageHolder.newMessageTracker(withMessageListener: self) - let firstChat = ChatItem(id: "1") - let secondChat = ChatItem(id: "2") + let firstChat = ChatItem(id: 1) + let secondChat = ChatItem(id: 2) // MARK: Test 1 // When: Requesting messages. diff --git a/Example/Tests/MessageImplTests.swift b/Example/Tests/MessageImplTests.swift index e6167be0..64643dc7 100644 --- a/Example/Tests/MessageImplTests.swift +++ b/Example/Tests/MessageImplTests.swift @@ -288,7 +288,8 @@ class MessageImplTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) let expectedString = """ MessageImpl { serverURLString = http://demo.webim.ru, @@ -338,7 +339,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertNil(message.getSenderAvatarFullURL()) } @@ -369,7 +371,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertEqual(message.getSendStatus(), MessageSendStatus.sent) @@ -401,34 +404,36 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) let message1 = MessageImpl(serverURLString: "http://demo.webim.ru", - clientSideID: "id1", - serverSideID: nil, - keyboard: nil, - keyboardRequest: nil, - operatorID: nil, - quote: nil, - senderAvatarURLString: nil, - senderName: "Name", - sendStatus: .sent, - sticker: nil, - type: .visitorMessage, - rawData: nil, - data: nil, - text: "Text", - timeInMicrosecond: 0, - historyMessage: false, - internalID: nil, - rawText: nil, - read: false, - messageCanBeEdited: false, - messageCanBeReplied: false, - messageIsEdited: false, - visitorReactionInfo: nil, - visitorCanReact: nil, - visitorChangeReaction: nil) + clientSideID: "id1", + serverSideID: nil, + keyboard: nil, + keyboardRequest: nil, + operatorID: nil, + quote: nil, + senderAvatarURLString: nil, + senderName: "Name", + sendStatus: .sent, + sticker: nil, + type: .visitorMessage, + rawData: nil, + data: nil, + text: "Text", + timeInMicrosecond: 0, + historyMessage: false, + internalID: nil, + rawText: nil, + read: false, + messageCanBeEdited: false, + messageCanBeReplied: false, + messageIsEdited: false, + visitorReactionInfo: nil, + visitorCanReact: nil, + visitorChangeReaction: nil, + group: nil) let message2 = MessageImpl(serverURLString: "http://demo.webim.ru", clientSideID: "id", serverSideID: nil, @@ -454,7 +459,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) let message3 = MessageImpl(serverURLString: "http://demo.webim.ru", clientSideID: "id", serverSideID: nil, @@ -480,7 +486,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) let message4 = MessageImpl(serverURLString: "http://demo.webim.ru", clientSideID: "id", serverSideID: nil, @@ -506,7 +513,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) let message5 = MessageImpl(serverURLString: "http://demo.webim.ru", clientSideID: "id", serverSideID: nil, @@ -532,7 +540,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) let message6 = MessageImpl(serverURLString: "http://demo.webim.ru", clientSideID: "id", serverSideID: nil, @@ -558,7 +567,8 @@ MessageImpl { messageIsEdited: true, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertFalse(message.isEqual(to: message1)) XCTAssertFalse(message.isEqual(to: message2)) @@ -596,7 +606,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertNoThrow(try message.getSource().assertIsCurrentChat()) } @@ -627,7 +638,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertThrowsError(try message.getSource().assertIsHistory()) } @@ -658,7 +670,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertNil(message.getHistoryID()) } @@ -690,7 +703,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertEqual(currentChatID, message.getCurrentChatID()) @@ -724,7 +738,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertEqual(URL(string: (baseURLString + avatarURLString)), message.getSenderAvatarFullURL()) @@ -758,7 +773,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertTrue(message.disableBotButtons()) } @@ -791,7 +807,8 @@ MessageImpl { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) XCTAssertFalse(message.disableBotButtons()) } @@ -1226,7 +1243,9 @@ fileprivate func fillMessageAttachmentsProperties() { execIfNotDestroyedHandlerExecutor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: SessionDestroyer(userDefaultsKey: userDefaultsKey), queue: DispatchQueue.main) actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, - internalErrorListener: internalErrorListener) + internalErrorListener: internalErrorListener, + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) deltaRequestLoop = DeltaRequestLoop(deltaCallback: DeltaCallback(currentChatMessageMapper: CurrentChatMessageMapper(withServerURLString: SERVER_URL_STRING), historyMessageMapper: HistoryMessageMapper(withServerURLString: SERVER_URL_STRING), userDefaultsKey: userDefaultsKey), @@ -1246,12 +1265,12 @@ fileprivate func fillMessageAttachmentsProperties() { visitorJSONString: nil, sessionID: nil, prechat: nil, - authorizationData: authorizationData) + authorizationData: authorizationData, + requestHeader: nil) webimClient = WebimClient(withActionRequestLoop: actionRequestLoop, deltaRequestLoop: deltaRequestLoop, - webimActions: WebimActionsImpl(baseURL: SERVER_URL_STRING, - actionRequestLoop: actionRequestLoop)) + webimActions: WebimActionsImpl(actionRequestLoop: actionRequestLoop)) fileUrlCreator = FileUrlCreator(webimClient: webimClient, serverURL: SERVER_URL_STRING) imageInfo = ImageInfoImpl(withThumbURLString: "https://demo.webim.ru/thumb.jpg", fileUrlCreator: fileUrlCreator, @@ -1313,31 +1332,33 @@ let defaultMessage = MessageImpl(serverURLString: "https://demo.webim.ru", messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: true, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) let defaultMessage_2 = MessageImpl(serverURLString: "https://demo.webim.ru", - clientSideID: "id", - serverSideID: nil, - keyboard: nil, - keyboardRequest: nil, - operatorID: nil, - quote: nil, - senderAvatarURLString: nil, - senderName: "Name", - sendStatus: .sent, - sticker: nil, - type: .visitorMessage, - rawData: nil, - data: nil, - text: "Text", - timeInMicrosecond: 0, - historyMessage: true, - internalID: "internalId", - rawText: nil, - read: false, - messageCanBeEdited: true, - messageCanBeReplied: true, - messageIsEdited: false, - visitorReactionInfo: nil, - visitorCanReact: false, - visitorChangeReaction: false) + clientSideID: "id", + serverSideID: nil, + keyboard: nil, + keyboardRequest: nil, + operatorID: nil, + quote: nil, + senderAvatarURLString: nil, + senderName: "Name", + sendStatus: .sent, + sticker: nil, + type: .visitorMessage, + rawData: nil, + data: nil, + text: "Text", + timeInMicrosecond: 0, + historyMessage: true, + internalID: "internalId", + rawText: nil, + read: false, + messageCanBeEdited: true, + messageCanBeReplied: true, + messageIsEdited: false, + visitorReactionInfo: nil, + visitorCanReact: false, + visitorChangeReaction: false, + group: nil) diff --git a/Example/Tests/MessageStreamImplTests.swift b/Example/Tests/MessageStreamImplTests.swift index 872a4e57..75fea5f6 100644 --- a/Example/Tests/MessageStreamImplTests.swift +++ b/Example/Tests/MessageStreamImplTests.swift @@ -49,7 +49,7 @@ class MessageStreamImplTests: XCTestCase { let queue = DispatchQueue.main let execIfNotDestroyedHandlerExecutor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: sessionDestroyer, queue: queue) let listener = InternalErrorListenerForTests() - actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, internalErrorListener: listener) + actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, internalErrorListener: listener, requestHeader: nil, baseURL: MessageImplMockData.serverURLString.rawValue) let currentChatMessageMapper = CurrentChatMessageMapper(withServerURLString: serverURLString) let sendingFactory = SendingFactory(withServerURLString: serverURLString) let operatorFactory = OperatorFactory(withServerURLString: serverURLString) @@ -59,8 +59,7 @@ class MessageStreamImplTests: XCTestCase { let memHistoryMetaInfo = MemoryHistoryMetaInformationStorage() let locationSettingsHolder = LocationSettingsHolder(userDefaultsKey: locationSettingsHolderUserDefaultsKey) - webimActions = WebimActionsImpl(baseURL: serverURLString, - actionRequestLoop: actionRequestLoop) + webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) let messageComposingHandler = MessageComposingHandler(webimActions: webimActions!, queue: queue) let remoteHistoryProvider = RemoteHistoryProvider(webimActions: webimActions!, @@ -72,6 +71,7 @@ class MessageStreamImplTests: XCTestCase { reachedEndOfRemoteHistory: true) messageStream = MessageStreamImpl(serverURLString: serverURLString, + location: "mobile", currentChatMessageFactoriesMapper: currentChatMessageMapper, sendingMessageFactory: sendingFactory, operatorFactory: operatorFactory, @@ -827,7 +827,7 @@ class MessageStreamImplTests: XCTestCase { try messageStream?.sendDialogTo(emailAddress: emailAdress, completionHandler: nil) - XCTAssertFalse(actionRequestLoop.enqueueCalled) + XCTAssertTrue(actionRequestLoop.enqueueCalled) } func testSetPrechatFields() throws { diff --git a/Example/Tests/MessageTrackerImplTests.swift b/Example/Tests/MessageTrackerImplTests.swift index 80bcd7f5..9c337cb4 100644 --- a/Example/Tests/MessageTrackerImplTests.swift +++ b/Example/Tests/MessageTrackerImplTests.swift @@ -74,7 +74,8 @@ class MessageTrackerImplTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil)) + visitorChangeReaction: nil, + group: nil)) } messagesCount = messagesCount + numberOfMessages @@ -110,7 +111,8 @@ class MessageTrackerImplTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil)) + visitorChangeReaction: nil, + group: nil)) } messagesCount = messagesCount + numberOfMessages @@ -146,7 +148,8 @@ class MessageTrackerImplTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: message.getVisitorReaction(), visitorCanReact: message.canVisitorReact(), - visitorChangeReaction: message.canVisitorChangeReaction()) + visitorChangeReaction: message.canVisitorChangeReaction(), + group: nil) result.append(newMessage) } @@ -180,7 +183,8 @@ class MessageTrackerImplTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) } private func newEdited(currentChatMessage: MessageImpl) -> MessageImpl { @@ -208,7 +212,8 @@ class MessageTrackerImplTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) } private func newEdited(historyMessage: MessageImpl) -> MessageImpl { @@ -236,7 +241,8 @@ class MessageTrackerImplTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil) + visitorChangeReaction: nil, + group: nil) } private func newMessageHolder(withHistory history: [MessageImpl] = [MessageImpl]()) -> MessageHolder { @@ -246,9 +252,10 @@ class MessageTrackerImplTests: XCTestCase { let execIfNotDestroyedHandlerExecutor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: sessionDestroyer, queue: DispatchQueue.global(qos: .userInteractive)) let actionRequestLoop = ActionRequestLoop(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, - internalErrorListener: InternalErrorListenerForTests()) - let webimActions = WebimActionsImpl(baseURL: MessageImplMockData.serverURLString.rawValue, - actionRequestLoop: actionRequestLoop) + internalErrorListener: InternalErrorListenerForTests(), + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) + let webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) let remoteHistoryProvider = RemoteHistoryProviderMock(withWebimActions: webimActions, historyMessageMapper: HistoryMessageMapper(withServerURLString: MessageImplMockData.serverURLString.rawValue), historyMetaInformation: MemoryHistoryMetaInformationStorage(), @@ -268,9 +275,10 @@ class MessageTrackerImplTests: XCTestCase { let execIfNotDestroyedHandlerExecutor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: sessionDestroyer, queue: DispatchQueue.global(qos: .userInteractive)) let actionRequestLoop = ActionRequestLoop(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, - internalErrorListener: InternalErrorListenerForTests()) - let webimActions = WebimActionsImpl(baseURL: MessageImplMockData.serverURLString.rawValue, - actionRequestLoop: actionRequestLoop) + internalErrorListener: InternalErrorListenerForTests(), + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) + let webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) let remoteHistoryProvider = RemoteHistoryProviderMock(withWebimActions: webimActions, historyMessageMapper: HistoryMessageMapper(withServerURLString: MessageImplMockData.serverURLString.rawValue), historyMetaInformation: MemoryHistoryMetaInformationStorage(), @@ -520,7 +528,7 @@ class MessageTrackerImplTests: XCTestCase { func test_ResetTo_MessageTrackerDestroyed() throws { webimLogger.reset() - let expectedLog = "MessageTracker object was destroyed. Unable to perform a request to reset to a message." + let expectedLog = "MessageTracker object is destroyed. Unable to perform request to get new messages." try sut.destroy() try sut.resetTo(message: defaultMessage) @@ -530,7 +538,7 @@ class MessageTrackerImplTests: XCTestCase { func test_ResetTo_MessageLoading() throws { webimLogger.reset() - let expectedLog = "Messages is loading. Unable to perform a simultaneous request to reset to a message." + let expectedLog = "Messages are already loading. Unable to perform a second request to get new messages." sut.set(messagesLoading: true) try sut.resetTo(message: defaultMessage) diff --git a/Example/Tests/Mocks/ActionRequestLoopMock.swift b/Example/Tests/Mocks/ActionRequestLoopMock.swift index 5b93c86a..68138dff 100644 --- a/Example/Tests/Mocks/ActionRequestLoopMock.swift +++ b/Example/Tests/Mocks/ActionRequestLoopMock.swift @@ -34,10 +34,10 @@ final class ActionRequestLoopForTests: ActionRequestLoop { var enqueueCalled = false // MARK: - Methods - override func enqueue(request: WebimRequest) { + override func enqueue(request: WebimRequest, withAuthData: Bool) { webimRequest = request enqueueCalled = true - super.enqueue(request: request) + super.enqueue(request: request, withAuthData: withAuthData) } } diff --git a/Example/Tests/Mocks/MessageImplMockData.swift b/Example/Tests/Mocks/MessageImplMockData.swift index 9640caf4..85c7bc43 100644 --- a/Example/Tests/Mocks/MessageImplMockData.swift +++ b/Example/Tests/Mocks/MessageImplMockData.swift @@ -27,7 +27,7 @@ import Foundation enum MessageImplMockData: String { - case serverURLString = "https://demo.webim.ru/" + case serverURLString = "https://demo.webim.ru" case operatorID = "operatorID" case avatarURLString = "image.jpg" case senderName = "Sender Name" diff --git a/Example/Tests/RemoteHistoryProviderTests.swift b/Example/Tests/RemoteHistoryProviderTests.swift index 95f63e48..5c881d44 100644 --- a/Example/Tests/RemoteHistoryProviderTests.swift +++ b/Example/Tests/RemoteHistoryProviderTests.swift @@ -41,9 +41,10 @@ class RemoteHistoryProviderTests: XCTestCase { let execIfNotDestroyedHandlerExecutor = ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: sessionDestroyer, queue: DispatchQueue.main) actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, - internalErrorListener: InternalErrorListenerForTests()) - let webimActions = WebimActionsImpl(baseURL: MessageImplMockData.serverURLString.rawValue, - actionRequestLoop: actionRequestLoop) + internalErrorListener: InternalErrorListenerForTests(), + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) + let webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) let messageMapper = MessageMapper(withServerURLString: MessageImplMockData.serverURLString.rawValue) let historyMetaInformationStorage = MemoryHistoryMetaInformationStorage() diff --git a/Example/Tests/SQLiteHistoryStorageTests.swift b/Example/Tests/SQLiteHistoryStorageTests.swift index 8df98917..d9b2661f 100644 --- a/Example/Tests/SQLiteHistoryStorageTests.swift +++ b/Example/Tests/SQLiteHistoryStorageTests.swift @@ -48,7 +48,9 @@ class SQLiteHistoryStorageTests: XCTestCase { queue: queue) let internalErrorListener = InternalErrorListenerForTests() let actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: exeIfNotDestroyedHandlerExecutor, - internalErrorListener: internalErrorListener) + internalErrorListener: internalErrorListener, + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) sqLiteHistoryStorage = SQLiteHistoryStorage(dbName: SQLiteHistoryStorageTests.DB_NAME, serverURL: SQLiteHistoryStorageTests.DB_NAME, fileUrlCreator: FileUrlCreator(webimClient: WebimClient(withActionRequestLoop: actionRequestLoop, @@ -69,9 +71,9 @@ class SQLiteHistoryStorageTests: XCTestCase { visitorJSONString: nil, sessionID: nil, prechat: nil, - authorizationData: nil), - webimActions: WebimActionsImpl(baseURL: SQLiteHistoryStorageTests.SERVER_URL_STRING, - actionRequestLoop: actionRequestLoop)), serverURL: SQLiteHistoryStorageTests.SERVER_URL_STRING), + authorizationData: nil, + requestHeader: nil), + webimActions: WebimActionsImpl(actionRequestLoop: actionRequestLoop)), serverURL: SQLiteHistoryStorageTests.SERVER_URL_STRING), reachedHistoryEnd: true, queue: queue, readBeforeTimestamp: -1) @@ -123,7 +125,8 @@ class SQLiteHistoryStorageTests: XCTestCase { messageIsEdited: false, visitorReactionInfo: nil, visitorCanReact: nil, - visitorChangeReaction: nil)) + visitorChangeReaction: nil, + group: nil)) } return messages diff --git a/Example/Tests/WebimActionsTests.swift b/Example/Tests/WebimActionsTests.swift index ed65157b..afedefce 100644 --- a/Example/Tests/WebimActionsTests.swift +++ b/Example/Tests/WebimActionsTests.swift @@ -36,7 +36,9 @@ class WebimActionsTests: XCTestCase { // MARK: - Properties private let actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: ExecIfNotDestroyedHandlerExecutor(sessionDestroyer: SessionDestroyer(userDefaultsKey: WebimActionsTests.userDefaultsKey), queue: DispatchQueue.global()), - internalErrorListener: InternalErrorListenerForTests() as InternalErrorListener) + internalErrorListener: InternalErrorListenerForTests() as InternalErrorListener, + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) private var webimActions: WebimActions? // MARK: - Methods @@ -44,8 +46,7 @@ class WebimActionsTests: XCTestCase { override func setUp() { super.setUp() - webimActions = WebimActionsImpl(baseURL: "https://demo.webim.ru", - actionRequestLoop: actionRequestLoop) + webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) } override func tearDown() { diff --git a/Example/Tests/WebimClientTests.swift b/Example/Tests/WebimClientTests.swift index 4484db1a..0d4b7356 100644 --- a/Example/Tests/WebimClientTests.swift +++ b/Example/Tests/WebimClientTests.swift @@ -52,30 +52,32 @@ class WebimClientTests: XCTestCase { userDefaultsKey: WebimClientTests.userDefaultsKey) actionRequestLoop = ActionRequestLoopForTests(completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, - internalErrorListener: internalErrorListener) + internalErrorListener: internalErrorListener, + requestHeader: nil, + baseURL: MessageImplMockData.serverURLString.rawValue) deltaRequestLoop = DeltaRequestLoop(deltaCallback: deltaCallback, - completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, - sessionParametersListener: nil, - internalErrorListener: internalErrorListener, - baseURL: urlString, - title: "title", - location: "location", - appVersion: nil, - visitorFieldsJSONString: nil, - providedAuthenticationTokenStateListener: nil, - providedAuthenticationToken: nil, - deviceID: "id", - deviceToken: nil, - remoteNotificationSystem: nil, - visitorJSONString: nil, - sessionID: nil, - prechat: nil, - authorizationData: nil) - - webimActions = WebimActionsImpl(baseURL: urlString, - actionRequestLoop: actionRequestLoop) + completionHandlerExecutor: execIfNotDestroyedHandlerExecutor, + sessionParametersListener: nil, + internalErrorListener: internalErrorListener, + baseURL: urlString, + title: "title", + location: "location", + appVersion: nil, + visitorFieldsJSONString: nil, + providedAuthenticationTokenStateListener: nil, + providedAuthenticationToken: nil, + deviceID: "id", + deviceToken: nil, + remoteNotificationSystem: nil, + visitorJSONString: nil, + sessionID: nil, + prechat: nil, + authorizationData: nil, + requestHeader: nil) + + webimActions = WebimActionsImpl(actionRequestLoop: actionRequestLoop) webimClient = WebimClient(withActionRequestLoop: actionRequestLoop, deltaRequestLoop: deltaRequestLoop, diff --git a/Example/Tests/WebimSessionImplTests.swift b/Example/Tests/WebimSessionImplTests.swift index 7d55c590..cd253985 100644 --- a/Example/Tests/WebimSessionImplTests.swift +++ b/Example/Tests/WebimSessionImplTests.swift @@ -102,7 +102,7 @@ class WebimSessionImplTests: XCTestCase { "crc" : "ffadeb6aa3c788200824e311b9aa44cb" } """ - let expectedLog = "Clear visitor data in WebimSessionImpl - clearVisitorDataFor" + let expectedLog = "Clear visitor data in WebimSessionImpl - clearVisitorDataFor(userDefaultsKey:)" let providedVisitorFields = ProvidedVisitorFields(withJSONString: visitorJson) let sut = produceWebimSession(providedFields: providedVisitorFields) let expectation = XCTestExpectation() @@ -149,24 +149,26 @@ class WebimSessionImplTests: XCTestCase { private func produceWebimSession(providedFields: ProvidedVisitorFields? = nil) -> WebimSessionImpl { WebimSessionImpl.newInstanceWith(accountName: "accountName", - location: "location", - appVersion: "appVersion", - visitorFields: providedFields, - providedAuthorizationTokenStateListener: nil, - providedAuthorizationToken: "providedAuthorizationToken", - pageTitle: "pageTitle", - fatalErrorHandler: nil, - notFatalErrorHandler: nil, - deviceToken: "deviceToken", - remoteNotificationSystem: .apns, - isLocalHistoryStoragingEnabled: true, - isVisitorDataClearingEnabled: true, - webimLogger: webimLogger, - verbosityLevel: .verbose, - availableLogTypes: [.manualCall,.messageHistory,.networkRequest,.undefined], - prechat: nil, - multivisitorSection: "multivisitorSection", - onlineStatusRequestFrequencyInMillis: 12376123) + location: "location", + appVersion: "appVersion", + visitorFields: providedFields, + providedAuthorizationTokenStateListener: nil, + providedAuthorizationToken: "providedAuthorizationToken", + pageTitle: "pageTitle", + fatalErrorHandler: nil, + notFatalErrorHandler: nil, + deviceToken: "deviceToken", + remoteNotificationSystem: .apns, + isLocalHistoryStoragingEnabled: true, + isVisitorDataClearingEnabled: true, + webimLogger: webimLogger, + verbosityLevel: .verbose, + availableLogTypes: [.manualCall,.messageHistory,.networkRequest,.undefined], + webimAlert: nil, + prechat: nil, + multivisitorSection: "multivisitorSection", + onlineStatusRequestFrequencyInMillis: 12376123, + requestHeader: nil) } } diff --git a/Example/WebimClientShare/CircleProgressIndicator.swift b/Example/WebimClientShare/CircleProgressIndicator.swift new file mode 100644 index 00000000..29d0ab34 --- /dev/null +++ b/Example/WebimClientShare/CircleProgressIndicator.swift @@ -0,0 +1,125 @@ +// +// File.swift +// WebimClientShare +// +// Created by Anna Frolova on 11.03.2024. +// Copyright © 2024 Webim. All rights reserved. +// + +import Foundation +import UIKit + +class CircleProgressIndicator: UIView { + + // MARK: - Properties + var lineWidth: CGFloat = 2 { + didSet { + circleLayer.lineWidth = lineWidth + setNeedsLayout() + } + } + + var strokeColor: CGColor = UIColor.red.cgColor { + didSet { + circleLayer.strokeColor = strokeColor + setNeedsLayout() + } + } + + // MARK: - Private properties + private var startValue: Float = 0 + private let backgrondCircleLayer = CAShapeLayer() + private let circleLayer = CAShapeLayer() + private let rotationAnimation: CAAnimation = { + let animation = CABasicAnimation(keyPath: "transform.rotation.z") + animation.fromValue = 0 + animation.toValue = Double.pi * 2 + animation.duration = 4 + animation.repeatCount = .infinity + + return animation + }() + + // MARK: - Methods + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setup() + } + + override func layoutSubviews() { + super.layoutSubviews() + + let center = CGPoint(x: bounds.midX, y: bounds.midY) + let radius = min(bounds.width, bounds.height) / 2 - circleLayer.lineWidth / 2 + + let startAngle = CGFloat(-Double.pi / 2) // -90° + let endAngle = startAngle + CGFloat(Double.pi * 2) + let path = UIBezierPath( + arcCenter: CGPoint.zero, + radius: radius, + startAngle: startAngle, + endAngle: endAngle, + clockwise: true + ) + + backgrondCircleLayer.position = center + backgrondCircleLayer.path = path.cgPath + + circleLayer.position = center + circleLayer.path = path.cgPath + } + + func enableRotationAnimation() { + circleLayer.add(rotationAnimation, forKey: "rotation") + } + + func setProgressWithAnimation(duration: TimeInterval, value: Float) { + let animation = CABasicAnimation(keyPath: "strokeEnd") + animation.duration = duration + animation.fromValue = startValue + startValue = value + animation.toValue = value + animation.timingFunction = CAMediaTimingFunction(name: .linear) + circleLayer.strokeEnd = CGFloat(value) + + circleLayer.add(animation, forKey: "strokeEnd") + } + + func updateImageDownloadProgress(_ progress: Float) { + if self.isHidden { + self.isHidden = false + self.enableRotationAnimation() + } + self.setProgressWithAnimation( + duration: 0.1, + value: progress + ) + } + + func setDefaultSetup() { + self.lineWidth = 1 + self.strokeColor = WMCircleProgressIndicatorCyan.cgColor + self.isUserInteractionEnabled = false + self.isHidden = true + self.translatesAutoresizingMaskIntoConstraints = false + } + + // MARK: - Private methods + private func setup() { + backgrondCircleLayer.lineWidth = lineWidth + backgrondCircleLayer.fillColor = nil + backgrondCircleLayer.strokeColor = WMCircleProgressIndicatorLightGrey.cgColor + layer.addSublayer(backgrondCircleLayer) + + circleLayer.lineWidth = lineWidth + circleLayer.fillColor = nil + circleLayer.strokeColor = strokeColor + layer.addSublayer(circleLayer) + } + +} diff --git a/Example/WebimClientShare/Extensions.swift b/Example/WebimClientShare/Extensions.swift new file mode 100644 index 00000000..400087aa --- /dev/null +++ b/Example/WebimClientShare/Extensions.swift @@ -0,0 +1,59 @@ +// +// Extensions.swift +// WebimClientShare +// +// Created by Anna Frolova on 22.07.2024. +// Copyright © 2024 Webim. All rights reserved. +// + +import UIKit + +private var associatedObjectHandle: UInt8 = 0 + +extension UITableView { + + var registeredCellsSet: Set { + get { + let set = objc_getAssociatedObject(self, &associatedObjectHandle) as? Set + if let set = set { + return set + } else { + self.registeredCellsSet = Set() + return self.registeredCellsSet + } + } + set { + objc_setAssociatedObject(self, + &associatedObjectHandle, + newValue, + objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + func identifierRegistered(_ identifier: String) -> Bool { + return self.registeredCellsSet.contains(identifier) + } + + public func dequeueReusableCellWithType(_ type: T.Type) -> T { + let identifier = "\(type)" + + if !self.identifierRegistered(identifier) { + self.registerCellWithType(type) + } + + guard let cell = self.dequeueReusableCell(withIdentifier: identifier) as? T else { + let logMessage = "Cast dequeueReusableCell with identifier \(identifier) to \(type) failure in DialogController.\(#function)" + // WMLogsManager.log(logMessage) + fatalError(logMessage) + } + return cell + } + + func registerCellWithType(_ type: T.Type) { + let identifier = "\(type)" + self.registeredCellsSet.insert(identifier) + if Bundle.main.path(forResource: identifier, ofType: "nib") != nil { + self.register(UINib(nibName: identifier, bundle: nil), forCellReuseIdentifier: identifier) + } + } +} diff --git a/Example/WebimClientShare/SendFileManager.swift b/Example/WebimClientShare/SendFileManager.swift new file mode 100644 index 00000000..5ef96761 --- /dev/null +++ b/Example/WebimClientShare/SendFileManager.swift @@ -0,0 +1,39 @@ +// +// SendFileManager.swift +// WebimClientShare +// +// Created by Anna Frolova on 11.03.2024. +// Copyright © 2024 Webim. All rights reserved. +// + +import Foundation +import WebimMobileSDK +import UIKit + +struct SendingFile { + var fileName: String + var fileID: String + var totalBytes: Int64 + var totalBytesSent: Int64 + var state: MessageSendStatus + + var progress: Float { + Float(totalBytesSent) / Float(totalBytes) + } + + init( + fileName: String, + fileID: String, + totalBytes: Int64 = -1, + totalBytesSent: Int64 = -1, + state: MessageSendStatus = .sending, + id: String = "" + ) { + self.fileName = fileName + self.fileID = fileID + self.totalBytesSent = totalBytesSent + self.totalBytes = totalBytes + self.state = state + } + +} diff --git a/Example/WebimClientShare/WMShareProgressViewController/WMShareProgressViewController.swift b/Example/WebimClientShare/WMShareProgressViewController/WMShareProgressViewController.swift new file mode 100644 index 00000000..93a9a5a2 --- /dev/null +++ b/Example/WebimClientShare/WMShareProgressViewController/WMShareProgressViewController.swift @@ -0,0 +1,121 @@ +// +// WMShareProgressViewController.swift +// WebimClientShare +// +// Created by Anna Frolova on 11.03.2024. +// Copyright © 2024 Webim. All rights reserved. +// + +import Foundation +import UIKit +import WebimMobileSDK + +class WMShareProgressViewController: UIViewController { + + @IBOutlet var tableView: UITableView! + + var sendingFiles = [SendingFile]() + private lazy var byteCountFormatter = byteFormatter() + + override func viewDidLoad() { + super.viewDidLoad() + setupTableViewIndicatorInset() + } + + @IBAction func cancelButtonPressed(_ sender: Any) { + closeExtension() + } + + func startProgress(for sendingFile: SendingFile, with progress: Int? = 0) { + + if !sendingFileExist(sendingFile) { + sendingFiles.append(sendingFile) + let indexForInsert = IndexPath(row: sendingFiles.count - 1, section: 0) + tableView.insertRows(at: [indexForInsert], with: .bottom) + } else { + let index = indexPathForSendingFile(sendingFile) + tableView.reloadRows(at: [index], with: .none) + } + } + + func stateChanged(for sendingFile: SendingFile, with state: MessageSendStatus) { + let index = indexForSendingFile(sendingFile) + let indexPath = indexPathForSendingFile(sendingFile) + sendingFiles[index].state = state + tableView.reloadRows(at: [indexPath], with: .none) + if sendingFiles.last?.state == .sent { + closeExtension() + } + } + + private func closeExtension() { + extensionContext?.completeRequest( + returningItems: nil, + completionHandler: nil + ) + } + + private func indexForSendingFile(_ sendingFile: SendingFile) -> Int { + return sendingFiles.firstIndex(where: { $0.fileID == sendingFile.fileID}) ?? 0 + } + + private func indexPathForSendingFile(_ sendingFile: SendingFile) -> IndexPath { + let index = indexForSendingFile(sendingFile) + return IndexPath(row: index, section: 0) + } + + private func sendingFileExist(_ sendingFile: SendingFile) -> Bool { + sendingFiles.first(where: { $0.fileID == sendingFile.fileID}) != nil + } + + private func generateProgressInfo(for sendingFile: SendingFile) -> String { + let sizeSent = byteCountFormatter.string(fromByteCount: sendingFile.totalBytesSent) + let size = byteCountFormatter.string(fromByteCount: sendingFile.totalBytes) + return "\(sizeSent) / \(size)" + } + + private func registerCellIfNeeded() { + let identifier = "\(ShareProgressTableViewCell.self)" + + if !tableView.identifierRegistered(identifier) { + tableView.registerCellWithType(ShareProgressTableViewCell.self) + } + } + + private func byteFormatter() -> ByteCountFormatter { + let formatter = ByteCountFormatter() + formatter.allowedUnits = .useAll + formatter.countStyle = .file + formatter.includesUnit = true + formatter.isAdaptive = true + return formatter + } + + private func setupTableViewIndicatorInset() { + tableView.scrollIndicatorInsets = UIEdgeInsets( + top: 10, + left: 0, + bottom: 10, + right: 20 + ) + } +} + +extension WMShareProgressViewController: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return sendingFiles.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + registerCellIfNeeded() + let cell = tableView.dequeueReusableCellWithType(ShareProgressTableViewCell.self) + let currentFile = sendingFiles[indexPath.row] + cell.setupIndicator() + cell.setFileName(currentFile.fileName) + cell.setState(currentFile.state) +// cell.setProgress(currentFile.progress) +// cell.setProgressInfo(generateProgressInfo(for: currentFile)) + return cell + } +} diff --git a/Example/WebimClientShare/WMShareProgressViewController/WMShareProgressViewController.xib b/Example/WebimClientShare/WMShareProgressViewController/WMShareProgressViewController.xib new file mode 100644 index 00000000..016176c5 --- /dev/null +++ b/Example/WebimClientShare/WMShareProgressViewController/WMShareProgressViewController.xib @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/WebimClientShare/WMShareProgressViewController/cells/ShareProgressTableViewCell.swift b/Example/WebimClientShare/WMShareProgressViewController/cells/ShareProgressTableViewCell.swift new file mode 100644 index 00000000..9ef623b7 --- /dev/null +++ b/Example/WebimClientShare/WMShareProgressViewController/cells/ShareProgressTableViewCell.swift @@ -0,0 +1,57 @@ +// +// ShareProgressTableViewCell.swift +// WebimClientShare +// +// Created by Anna Frolova on 11.03.2024. +// Copyright © 2024 Webim. All rights reserved. +// + +import UIKit +import WebimMobileSDK + +class ShareProgressTableViewCell: UITableViewCell { + @IBOutlet private var progressIndicator: CircleProgressIndicator! + @IBOutlet private var fileNameLabel: UILabel! + @IBOutlet private var progressInfoLabel: UILabel! + @IBOutlet private var fileStateImageView: UIImageView! + + func setProgress(_ progress: Float) { + progressIndicator.setProgressWithAnimation(duration: 0.5, value: progress) + } + + func setFileName(_ fileName: String) { + fileNameLabel.text = fileName + } + + func setProgressInfo(_ progressInfo: String) { + progressInfoLabel.text = progressInfo + } + + func setState(_ state: MessageSendStatus) { + switch state { + case .sending: + progressIndicator.isHidden = false + fileStateImageView.isHidden = true + fileNameLabel.textColor = quoteBodyLabelColourVisitor + fileStateImageView.image = nil + progressIndicator.enableRotationAnimation() + progressIndicator?.setProgressWithAnimation( + duration: 0.01, + value: 0.5 + ) + case .sent: + progressIndicator.isHidden = true + fileStateImageView.isHidden = false + fileNameLabel.textColor = webimCyan + fileStateImageView.image = #imageLiteral(resourceName: "FileDownloadSuccessOperator") + default: + progressIndicator.isHidden = true + fileStateImageView.isHidden = false + fileNameLabel.textColor = wmCoral + fileStateImageView.image = UIImage(named: "FileDownloadError")! + } + } + func setupIndicator() { + progressIndicator?.setDefaultSetup() + } +} diff --git a/Example/WebimClientShare/WMShareProgressViewController/cells/ShareProgressTableViewCell.xib b/Example/WebimClientShare/WMShareProgressViewController/cells/ShareProgressTableViewCell.xib new file mode 100644 index 00000000..560ce582 --- /dev/null +++ b/Example/WebimClientShare/WMShareProgressViewController/cells/ShareProgressTableViewCell.xib @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/WebimClientShare/WMShareViewController.swift b/Example/WebimClientShare/WMShareViewController.swift index a5574335..f75d455e 100644 --- a/Example/WebimClientShare/WMShareViewController.swift +++ b/Example/WebimClientShare/WMShareViewController.swift @@ -38,51 +38,39 @@ class WMShareViewController: UIViewController, SendFileCompletionHandler { private var alertController: UIAlertController! var sendFileError: SendFileError? = nil let saveView = WMSaveView.loadXibView() + lazy var shareProgressViewController = WMShareProgressViewController.loadViewControllerFromXib() + var sendedFilesCount = 0 + var countOfAttachments = 0 override func viewDidLoad() { super.viewDidLoad() WMKeychainWrapper.standard.setAppGroupName(userDefaults: UserDefaults(suiteName: "group.WebimClient.Share") ?? UserDefaults.standard, keychainAccessGroup: Bundle.main.infoDictionary!["keychainAppIdentifier"] as! String) - WebimServiceController.currentSessionShare.createSession() - - alertController = UIAlertController(title: "Отправить файл".localized, - message: "Вы уверены что хотите отправить файл?", - preferredStyle: .alert) - - let okAction = UIAlertAction(title: "OK".localized, - style: .default, - handler: { _ in self.getFilesExtensionContext() }) - - alertController.addAction(okAction) - self.present(alertController, animated: true) + WebimServiceController.currentSession.setMessageTracker(withMessageListener: self) + getFilesExtensionContext() } + func getFilesExtensionContext() { guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem], inputItems.isNotEmpty() else { close() return } - - DispatchQueue.main.async { - self.view.addSubview(self.saveView) - self.saveView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true - self.saveView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true - } - self.saveView.animateActivity() + inputItems.forEach { item in - if let attachments = item.attachments, - !attachments.isEmpty { - attachments.forEach { attachment in - if attachment.isImage { - handleImageAttachment(attachment) - } else if attachment.isFile { - handleFileAttachment(attachment) - } - } + item.attachments?.forEach { attachment in + handleAttachment(attachment) + countOfAttachments += 1 } } - checkErrorAfterSend() + + showShareProgressView() + } + + private func showShareProgressView() { + shareProgressViewController.modalPresentationStyle = .overCurrentContext + present(shareProgressViewController, animated: true) } func showDialog( @@ -117,6 +105,7 @@ class WMShareViewController: UIViewController, SendFileCompletionHandler { self.present(alertController, animated: true) } + func checkErrorAfterSend() { guard sendFileError != nil else { self.saveView.stopAnimateActivity() @@ -164,6 +153,16 @@ extension WMShareViewController { extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) } + func handleAttachment(_ attachment: NSItemProvider) { + if attachment.isImage { + handleImageAttachment(attachment) + } else if attachment.isFile { + handleFileAttachment(attachment, (kUTTypeFileURL as String)) + } else if let type = attachment.isVideo { + handleFileAttachment(attachment, type) + } + } + func handleImageAttachment(_ attachment: NSItemProvider) { attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { [weak self] item, error in guard let self = self else { return } @@ -208,8 +207,8 @@ extension WMShareViewController { } } - func handleFileAttachment(_ attachment: NSItemProvider) { - attachment.loadItem(forTypeIdentifier: kUTTypeFileURL as String, options: nil) { [weak self] item, error in + func handleFileAttachment(_ attachment: NSItemProvider, _ typeIdentifier: String) { + attachment.loadItem(forTypeIdentifier: typeIdentifier, options: nil) { [weak self] item, error in guard let self = self else { return } guard error == nil else { self.close() @@ -245,10 +244,22 @@ extension NSItemProvider { var isImage: Bool { return hasItemConformingToTypeIdentifier(kUTTypeImage as String) } - + var isFile: Bool { return hasItemConformingToTypeIdentifier(kUTTypeFileURL as String) } + + var isVideo: String? { + if hasItemConformingToTypeIdentifier(kUTTypeVideo as String) { + return (kUTTypeVideo as String) + } else if hasItemConformingToTypeIdentifier(kUTTypeMPEG as String) { + return (kUTTypeMPEG as String) + } else if hasItemConformingToTypeIdentifier(kUTTypeMPEG4 as String) { + return (kUTTypeMPEG4 as String) + } else { + return "" + } + } } extension Array { @@ -256,3 +267,26 @@ extension Array { return !isEmpty } } + +extension WMShareViewController: MessageListener { + func removed(message: any WebimMobileSDK.Message) { + } + + func removedAllMessages() { + } + + func added(message newMessage: Message, + after previousMessage: Message?) { + if let fileData = newMessage.getData()?.getAttachment()?.getFileInfo() { + self.shareProgressViewController.startProgress(for: SendingFile(fileName: fileData.getFileName(), fileID: newMessage.getID())) + } + + } + + func changed(message oldVersion: Message, + to newVersion: Message) { + if let fileData = newVersion.getData()?.getAttachment()?.getFileInfo() { + self.shareProgressViewController.stateChanged(for: SendingFile(fileName: fileData.getFileName(), fileID: oldVersion.getID()), with: newVersion.getSendStatus()) + } + } +} diff --git a/Example/WebimMobileSDK.xcodeproj/project.pbxproj b/Example/WebimMobileSDK.xcodeproj/project.pbxproj index f7780a57..a4c5f1bb 100644 --- a/Example/WebimMobileSDK.xcodeproj/project.pbxproj +++ b/Example/WebimMobileSDK.xcodeproj/project.pbxproj @@ -24,13 +24,10 @@ 129360672614C20C008F8EF1 /* AdditionalKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129360662614C20C008F8EF1 /* AdditionalKeys.swift */; }; 1295C6AF2602BB5200F7BE80 /* WebimServiceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1295C6AE2602BB5200F7BE80 /* WebimServiceController.swift */; }; 12ADB880278D753900EC2B1F /* WMSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12ADB882278D753900EC2B1F /* WMSettingsViewController.xib */; }; - 4C653D2CDA2BAEB07F2CC844 /* Pods_WebimMobileSDK_Example_WebimClientShare.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E59887F0998D10578881C93 /* Pods_WebimMobileSDK_Example_WebimClientShare.framework */; }; 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 607FACD81AFB9204008FA782 /* WMStartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* WMStartViewController.swift */; }; 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 607FACEC1AFB9204008FA782 /* MessageHolderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* MessageHolderTests.swift */; }; - 6F580BCF9267AE6E9BC87088 /* Pods_WebimMobileSDK_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D8FB54DCF97367628EE1991 /* Pods_WebimMobileSDK_Tests.framework */; }; - 7A9ED53C368D1607C1A4933A /* Pods_WebimMobileSDK_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 789A2E6B871777C866D5F778 /* Pods_WebimMobileSDK_Example.framework */; }; 906CBADB28D9DC7100CE86DF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 906CBADD28D9DC7100CE86DF /* InfoPlist.strings */; }; 906CBB2028DA471300CE86DF /* WMLogsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 906CBB2228DA471300CE86DF /* WMLogsViewController.xib */; }; 909D76CB28F54F380018E54C /* WMShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 909D76CA28F54F380018E54C /* WMShareViewController.swift */; }; @@ -40,10 +37,23 @@ 909D76DA28F55BEA0018E54C /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBFB67E2018CB0F00D9E5F6 /* String.swift */; }; 909D76DB28F5698F0018E54C /* WebimService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE591A421FC2F31E004C95EE /* WebimService.swift */; }; 909D76DD28F56B000018E54C /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE88BCD21FCD88EA00A4FA2E /* Settings.swift */; }; + 90A432562B9F515300628253 /* ShareProgressTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A432552B9F515300628253 /* ShareProgressTableViewCell.swift */; }; + 90A4325A2B9F522600628253 /* CircleProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A432592B9F522600628253 /* CircleProgressIndicator.swift */; }; + 90A4325C2B9F527500628253 /* WMShareProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A4325B2B9F527500628253 /* WMShareProgressViewController.swift */; }; + 90A4325E2B9F529D00628253 /* WMShareProgressViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90A4325D2B9F529D00628253 /* WMShareProgressViewController.xib */; }; + 90A432602B9F547500628253 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; + 90A432622B9F54B100628253 /* SendFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A432612B9F54B100628253 /* SendFileManager.swift */; }; + 90A432632BA05BD900628253 /* ColourConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCC52592356360F00D39F8A /* ColourConstants.swift */; }; + 90A432642BA0674E00628253 /* SendFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A432612B9F54B100628253 /* SendFileManager.swift */; }; + 90A432662BA0678800628253 /* URLRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A432652BA0678800628253 /* URLRequest.swift */; }; + 90A432672BA0679D00628253 /* URLRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A432652BA0678800628253 /* URLRequest.swift */; }; + 90A432682BA06C1700628253 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD848902A73E2A0005CDE5A /* UIViewController.swift */; }; 90BC0495279A040B0081A75F /* WMStartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90BC0497279A040B0081A75F /* WMStartViewController.xib */; }; 90BC04C2279A51C60081A75F /* logo_webim.png in Resources */ = {isa = PBXBuildFile; fileRef = 12A0D8D525FF467B003AF10E /* logo_webim.png */; }; 90C5059229B493E200FAD33D /* WMSaveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C5059129B493E200FAD33D /* WMSaveView.swift */; }; 90C5059629B497F300FAD33D /* WMSaveView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90C5059529B497F300FAD33D /* WMSaveView.xib */; }; + 90F56C9C2C4ED0F300357A6A /* ShareProgressTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90A432572B9F51A400628253 /* ShareProgressTableViewCell.xib */; }; + 90F56C9E2C4ED1E600357A6A /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90F56C9D2C4ED1E600357A6A /* Extensions.swift */; }; BE17D41E285B7DCD00DB606D /* WMLogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE17D41C285B7DCD00DB606D /* WMLogsViewController.swift */; }; BE24022928B4BA4700697F21 /* FAQActionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE24022828B4BA4700697F21 /* FAQActionsTests.swift */; }; BE24022B28B4BE2100697F21 /* FAQRequestLoopMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE24022A28B4BE2100697F21 /* FAQRequestLoopMock.swift */; }; @@ -150,6 +160,9 @@ DDC7924623573CFF00AF424F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DDC7924823573CFF00AF424F /* LaunchScreen.storyboard */; }; DDCC52582356333400D39F8A /* ImageConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCC52572356333400D39F8A /* ImageConstants.swift */; }; DDCC525A2356360F00D39F8A /* ColourConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCC52592356360F00D39F8A /* ColourConstants.swift */; }; + DDCF0821AB2A6131C8917358 /* Pods_WebimMobileSDK_Example_WebimClientShare.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CA97A57A2CABAE3333F729F /* Pods_WebimMobileSDK_Example_WebimClientShare.framework */; }; + F22E2E5217BCBA11F9A8D6BD /* Pods_WebimMobileSDK_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1F92C486FA5262A337DD867 /* Pods_WebimMobileSDK_Tests.framework */; }; + F787C9DCFD38DB35FDF830B1 /* Pods_WebimMobileSDK_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6173D665D0C02E615C30F0DD /* Pods_WebimMobileSDK_Example.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -193,6 +206,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 08EF987CCABD336CB294220E /* Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Example-WebimClientShare/Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig"; sourceTree = ""; }; 11C0FDF12BC9415600A771EE /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 11EFE7D2231AC33500B64EFA /* FAQ methods documentation.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "FAQ methods documentation.md"; sourceTree = ""; }; 122DD62027918C0700093609 /* UINavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationController.swift; sourceTree = ""; }; @@ -216,11 +230,7 @@ 12ADB87B278D749D00EC2B1F /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; 12ADB881278D753900EC2B1F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/WMSettingsViewController.xib; sourceTree = ""; }; 12ADB886278D756B00EC2B1F /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/WMSettingsViewController.strings; sourceTree = ""; }; - 18EFB50E2E06DDBD48B29C40 /* Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Example-WebimClientShare/Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig"; sourceTree = ""; }; - 475CA95610ABD040CAEA2E09 /* Pods-WebimMobileSDK_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Example.debug.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Example/Pods-WebimMobileSDK_Example.debug.xcconfig"; sourceTree = ""; }; - 4E59887F0998D10578881C93 /* Pods_WebimMobileSDK_Example_WebimClientShare.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WebimMobileSDK_Example_WebimClientShare.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 55CD1AEA73D029FB130A9E40 /* Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Example-WebimClientShare/Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig"; sourceTree = ""; }; - 5D8FB54DCF97367628EE1991 /* Pods_WebimMobileSDK_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WebimMobileSDK_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3CA97A57A2CABAE3333F729F /* Pods_WebimMobileSDK_Example_WebimClientShare.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WebimMobileSDK_Example_WebimClientShare.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD01AFB9204008FA782 /* WebimMobileSDK_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebimMobileSDK_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -229,7 +239,8 @@ 607FACE51AFB9204008FA782 /* WebimMobileSDK_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WebimMobileSDK_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACEB1AFB9204008FA782 /* MessageHolderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageHolderTests.swift; sourceTree = ""; }; - 789A2E6B871777C866D5F778 /* Pods_WebimMobileSDK_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WebimMobileSDK_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6173D665D0C02E615C30F0DD /* Pods_WebimMobileSDK_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WebimMobileSDK_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 832A61587957BD696F7D9267 /* Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Example-WebimClientShare/Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig"; sourceTree = ""; }; 906CBADC28D9DC7100CE86DF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 906CBADE28D9DC7E00CE86DF /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; 906CBB2128DA471300CE86DF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/WMLogsViewController.xib; sourceTree = ""; }; @@ -240,6 +251,13 @@ 909D76CA28F54F380018E54C /* WMShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMShareViewController.swift; sourceTree = ""; }; 909D76CF28F54F380018E54C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 909D76D728F54FBD0018E54C /* WebimClientShare.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WebimClientShare.entitlements; sourceTree = ""; }; + 90A432552B9F515300628253 /* ShareProgressTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareProgressTableViewCell.swift; sourceTree = ""; }; + 90A432572B9F51A400628253 /* ShareProgressTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShareProgressTableViewCell.xib; sourceTree = ""; }; + 90A432592B9F522600628253 /* CircleProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleProgressIndicator.swift; sourceTree = ""; }; + 90A4325B2B9F527500628253 /* WMShareProgressViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMShareProgressViewController.swift; sourceTree = ""; }; + 90A4325D2B9F529D00628253 /* WMShareProgressViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WMShareProgressViewController.xib; sourceTree = ""; }; + 90A432612B9F54B100628253 /* SendFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendFileManager.swift; sourceTree = ""; }; + 90A432652BA0678800628253 /* URLRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequest.swift; sourceTree = ""; }; 90BC0496279A040B0081A75F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/WMStartViewController.xib; sourceTree = ""; }; 90BC0499279A045F0081A75F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/WMStartViewController.strings; sourceTree = ""; }; 90BC049B279A045F0081A75F /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/WMStartViewController.strings; sourceTree = ""; }; @@ -287,8 +305,9 @@ 90DC110D2AF3D41C00438D98 /* pt */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = pt; path = pt.lproj/LaunchScreenController.storyboard; sourceTree = ""; }; 90DC110E2AF3D41C00438D98 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/InfoPlist.strings; sourceTree = ""; }; 90DC110F2AF3D41C00438D98 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = ""; }; + 90F56C9D2C4ED1E600357A6A /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; + A89133BECF3D2A05742A161C /* Pods-WebimMobileSDK_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Tests.debug.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Tests/Pods-WebimMobileSDK_Tests.debug.xcconfig"; sourceTree = ""; }; AB52E1C1E0F009A386DC1C6C /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; - B245B23919CFD976FED79B92 /* Pods-WebimMobileSDK_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Tests.release.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Tests/Pods-WebimMobileSDK_Tests.release.xcconfig"; sourceTree = ""; }; BE17D41C285B7DCD00DB606D /* WMLogsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMLogsViewController.swift; sourceTree = ""; }; BE24022828B4BA4700697F21 /* FAQActionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAQActionsTests.swift; sourceTree = ""; }; BE24022A28B4BE2100697F21 /* FAQRequestLoopMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAQRequestLoopMock.swift; sourceTree = ""; }; @@ -320,7 +339,6 @@ BEFFCF6E28C0CA0C00B886AC /* WebimClientBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebimClientBuilderTests.swift; sourceTree = ""; }; BEFFCF7028C0D75D00B886AC /* WebimNotFatalErrorImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebimNotFatalErrorImpl.swift; sourceTree = ""; }; BEFFCF7228C0D88C00B886AC /* WebimRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebimRequestTests.swift; sourceTree = ""; }; - C3C87E09DE04468C69E37BC8 /* Pods-WebimMobileSDK_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Tests.debug.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Tests/Pods-WebimMobileSDK_Tests.debug.xcconfig"; sourceTree = ""; }; C4CD65CA48F80B5A4AD826B3 /* WebimMobileSDK.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = WebimMobileSDK.podspec; path = ../WebimMobileSDK.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; CC1F8E9E2A13A77E002494E1 /* DemoVisitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoVisitor.swift; sourceTree = ""; }; CC529D2E29EFF57300295C4A /* WMVisitorFieldsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMVisitorFieldsParser.swift; sourceTree = ""; }; @@ -403,14 +421,17 @@ CED72594200E20E500CD1623 /* ChatTableViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTableViewControllerTests.swift; sourceTree = ""; }; CED72597200E5B1C00CD1623 /* WebimInternalLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebimInternalLoggerTests.swift; sourceTree = ""; }; CEE382C12035D544006C809E /* WebimTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebimTests.swift; sourceTree = ""; }; + D1F92C486FA5262A337DD867 /* Pods_WebimMobileSDK_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WebimMobileSDK_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DD71AD04234DAFDC00A32FB6 /* UIAlertHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIAlertHandler.swift; sourceTree = ""; }; DD8A8D2C232A3E200011D275 /* LaunchScreenController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchScreenController.swift; sourceTree = ""; }; DDC7924B23573D8700AF424F /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/LaunchScreen.storyboard; sourceTree = ""; }; DDC7924C23573D9300AF424F /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/LaunchScreenController.storyboard; sourceTree = ""; }; DDCC52572356333400D39F8A /* ImageConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageConstants.swift; sourceTree = ""; }; DDCC52592356360F00D39F8A /* ColourConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColourConstants.swift; sourceTree = ""; }; + E7677A9313FBC0C91D073A75 /* Pods-WebimMobileSDK_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Tests.release.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Tests/Pods-WebimMobileSDK_Tests.release.xcconfig"; sourceTree = ""; }; EB8C258A78F3730770936414 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; - EC1676D646E1C2549F3DAEED /* Pods-WebimMobileSDK_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Example.release.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Example/Pods-WebimMobileSDK_Example.release.xcconfig"; sourceTree = ""; }; + F6C4511557995D99EA2FB9F8 /* Pods-WebimMobileSDK_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Example.release.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Example/Pods-WebimMobileSDK_Example.release.xcconfig"; sourceTree = ""; }; + F977A2214FA33837D47DAE8F /* Pods-WebimMobileSDK_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebimMobileSDK_Example.debug.xcconfig"; path = "Target Support Files/Pods-WebimMobileSDK_Example/Pods-WebimMobileSDK_Example.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -425,7 +446,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7A9ED53C368D1607C1A4933A /* Pods_WebimMobileSDK_Example.framework in Frameworks */, + F787C9DCFD38DB35FDF830B1 /* Pods_WebimMobileSDK_Example.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -433,7 +454,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6F580BCF9267AE6E9BC87088 /* Pods_WebimMobileSDK_Tests.framework in Frameworks */, + F22E2E5217BCBA11F9A8D6BD /* Pods_WebimMobileSDK_Tests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -441,7 +462,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4C653D2CDA2BAEB07F2CC844 /* Pods_WebimMobileSDK_Example_WebimClientShare.framework in Frameworks */, + DDCF0821AB2A6131C8917358 /* Pods_WebimMobileSDK_Example_WebimClientShare.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -515,25 +536,15 @@ name = "Recovered References"; sourceTree = ""; }; - 1F8789149F87012EDC40B6F9 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 789A2E6B871777C866D5F778 /* Pods_WebimMobileSDK_Example.framework */, - 4E59887F0998D10578881C93 /* Pods_WebimMobileSDK_Example_WebimClientShare.framework */, - 5D8FB54DCF97367628EE1991 /* Pods_WebimMobileSDK_Tests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 3C3DB6CF3A0FDD8E0CD44FD9 /* Pods */ = { isa = PBXGroup; children = ( - 475CA95610ABD040CAEA2E09 /* Pods-WebimMobileSDK_Example.debug.xcconfig */, - EC1676D646E1C2549F3DAEED /* Pods-WebimMobileSDK_Example.release.xcconfig */, - 18EFB50E2E06DDBD48B29C40 /* Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig */, - 55CD1AEA73D029FB130A9E40 /* Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig */, - C3C87E09DE04468C69E37BC8 /* Pods-WebimMobileSDK_Tests.debug.xcconfig */, - B245B23919CFD976FED79B92 /* Pods-WebimMobileSDK_Tests.release.xcconfig */, + F977A2214FA33837D47DAE8F /* Pods-WebimMobileSDK_Example.debug.xcconfig */, + F6C4511557995D99EA2FB9F8 /* Pods-WebimMobileSDK_Example.release.xcconfig */, + 08EF987CCABD336CB294220E /* Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig */, + 832A61587957BD696F7D9267 /* Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig */, + A89133BECF3D2A05742A161C /* Pods-WebimMobileSDK_Tests.debug.xcconfig */, + E7677A9313FBC0C91D073A75 /* Pods-WebimMobileSDK_Tests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -553,7 +564,7 @@ 607FACD11AFB9204008FA782 /* Products */, 3C3DB6CF3A0FDD8E0CD44FD9 /* Pods */, 12A45C2626023BBD004B077F /* Recovered References */, - 1F8789149F87012EDC40B6F9 /* Frameworks */, + DBF187A1B4EC4E3E80CF1178 /* Frameworks */, ); sourceTree = ""; }; @@ -695,15 +706,38 @@ 909D76C928F54F380018E54C /* WebimClientShare */ = { isa = PBXGroup; children = ( + 90A432532B9F50FF00628253 /* WMShareProgressViewController */, 909D76D728F54FBD0018E54C /* WebimClientShare.entitlements */, 909D76CA28F54F380018E54C /* WMShareViewController.swift */, 909D76CF28F54F380018E54C /* Info.plist */, 90C5059129B493E200FAD33D /* WMSaveView.swift */, 90C5059529B497F300FAD33D /* WMSaveView.xib */, + 90A432592B9F522600628253 /* CircleProgressIndicator.swift */, + 90A432612B9F54B100628253 /* SendFileManager.swift */, + 90F56C9D2C4ED1E600357A6A /* Extensions.swift */, ); path = WebimClientShare; sourceTree = ""; }; + 90A432532B9F50FF00628253 /* WMShareProgressViewController */ = { + isa = PBXGroup; + children = ( + 90A432542B9F511100628253 /* cells */, + 90A4325B2B9F527500628253 /* WMShareProgressViewController.swift */, + 90A4325D2B9F529D00628253 /* WMShareProgressViewController.xib */, + ); + path = WMShareProgressViewController; + sourceTree = ""; + }; + 90A432542B9F511100628253 /* cells */ = { + isa = PBXGroup; + children = ( + 90A432552B9F515300628253 /* ShareProgressTableViewCell.swift */, + 90A432572B9F51A400628253 /* ShareProgressTableViewCell.xib */, + ); + path = cells; + sourceTree = ""; + }; 90BC04832796F4BE0081A75F /* WMStartViewController */ = { isa = PBXGroup; children = ( @@ -775,6 +809,7 @@ CE45EDDF1F95F95C00F56319 /* MimeType.swift */, 127D2AEB26E6018100FD60DF /* WMTestManager.swift */, CCF69DAD2A14C9B400E1FBA8 /* NetworkUtils.swift */, + 90A432652BA0678800628253 /* URLRequest.swift */, ); path = Utilities; sourceTree = ""; @@ -847,6 +882,16 @@ path = Mocks; sourceTree = ""; }; + DBF187A1B4EC4E3E80CF1178 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6173D665D0C02E615C30F0DD /* Pods_WebimMobileSDK_Example.framework */, + 3CA97A57A2CABAE3333F729F /* Pods_WebimMobileSDK_Example_WebimClientShare.framework */, + D1F92C486FA5262A337DD867 /* Pods_WebimMobileSDK_Tests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -871,15 +916,15 @@ isa = PBXNativeTarget; buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "WebimMobileSDK_Example" */; buildPhases = ( - AB5DFAB4D24167C30421C8C8 /* [CP] Check Pods Manifest.lock */, + 34E8460D11FB0EC00615B957 /* [CP] Check Pods Manifest.lock */, 8CC584B88D331C1B48D729CB /* [Amimono] Create filelist per architecture */, 607FACCC1AFB9204008FA782 /* Sources */, 607FACCD1AFB9204008FA782 /* Frameworks */, 607FACCE1AFB9204008FA782 /* Resources */, 909D76D328F54F380018E54C /* Embed Foundation Extensions */, 129C626425E83D60009D4328 /* ShellScript */, + BE8775A141F6564CD64C643A /* [CP] Embed Pods Frameworks */, 90A3704C2A1BDE9D000CC592 /* Upload DSYM */, - 34074CB2931BA5C35040A396 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -895,7 +940,7 @@ isa = PBXNativeTarget; buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "WebimMobileSDK_Tests" */; buildPhases = ( - A4514B3867DF216993FEB5E9 /* [CP] Check Pods Manifest.lock */, + 5AA293043EE48503F61D82A7 /* [CP] Check Pods Manifest.lock */, 607FACE11AFB9204008FA782 /* Sources */, 607FACE21AFB9204008FA782 /* Frameworks */, 607FACE31AFB9204008FA782 /* Resources */, @@ -914,7 +959,7 @@ isa = PBXNativeTarget; buildConfigurationList = 909D76D628F54F380018E54C /* Build configuration list for PBXNativeTarget "WebimClientShare" */; buildPhases = ( - 40A0E2AF8EEA238A9F10393B /* [CP] Check Pods Manifest.lock */, + A22AF9CB0950E4D3D2C5A478 /* [CP] Check Pods Manifest.lock */, 909D76C428F54F380018E54C /* Sources */, 909D76C528F54F380018E54C /* Frameworks */, 909D76C628F54F380018E54C /* Resources */, @@ -1028,7 +1073,10 @@ buildActionMask = 2147483647; files = ( 909D76D928F55BC20018E54C /* Localizable.strings in Resources */, + 90A432602B9F547500628253 /* Images.xcassets in Resources */, 90C5059629B497F300FAD33D /* WMSaveView.xib in Resources */, + 90F56C9C2C4ED0F300357A6A /* ShareProgressTableViewCell.xib in Resources */, + 90A4325E2B9F529D00628253 /* WMShareProgressViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1052,63 +1100,29 @@ shellPath = /bin/sh; shellScript = "if which swiftlint >/dev/null; then\n swiftlint\n#else\n #echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; - 34074CB2931BA5C35040A396 /* [CP] Embed Pods Frameworks */ = { + 34E8460D11FB0EC00615B957 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-WebimMobileSDK_Example/Pods-WebimMobileSDK_Example-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Cosmos/Cosmos.framework", - "${BUILT_PRODUCTS_DIR}/FLAnimatedImage/FLAnimatedImage.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseRemoteConfigInterop/FirebaseRemoteConfigInterop.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseSessions/FirebaseSessions.framework", - "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/Nuke/Nuke.framework", - "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", - "${BUILT_PRODUCTS_DIR}/PromisesSwift/Promises.framework", - "${BUILT_PRODUCTS_DIR}/SQLite.swift/SQLite.framework", - "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework", - "${BUILT_PRODUCTS_DIR}/WebimKeyboard/WebimKeyboard.framework", - "${BUILT_PRODUCTS_DIR}/WebimMobileSDK/WebimMobileSDK.framework", - "${BUILT_PRODUCTS_DIR}/WebimMobileWidget/WebimMobileWidget.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cosmos.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FLAnimatedImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreExtension.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCrashlytics.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseRemoteConfigInterop.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseSessions.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nuke.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Promises.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SQLite.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebimKeyboard.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebimMobileSDK.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebimMobileWidget.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + "$(DERIVED_FILE_DIR)/Pods-WebimMobileSDK_Example-WebimClientShare-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-WebimMobileSDK_Example/Pods-WebimMobileSDK_Example-frameworks.sh\"\n"; + 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; }; - 40A0E2AF8EEA238A9F10393B /* [CP] Check Pods Manifest.lock */ = { + 5AA293043EE48503F61D82A7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1123,7 +1137,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-WebimMobileSDK_Example-WebimClientShare-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-WebimMobileSDK_Tests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1162,7 +1176,7 @@ shellPath = /bin/sh; shellScript = "# Type a script or drag a script file from your workspace to insert its path.\necho \"Symbols Directory\"\necho \"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\"\n\n\"${PODS_ROOT}/FirebaseCrashlytics/upload-symbols\" -gsp \"${PROJECT_DIR}/WebimMobileSDK/GoogleService-Info.plist\" -p ios \"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\"\n"; }; - A4514B3867DF216993FEB5E9 /* [CP] Check Pods Manifest.lock */ = { + A22AF9CB0950E4D3D2C5A478 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1177,33 +1191,65 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-WebimMobileSDK_Tests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-WebimMobileSDK_Example-WebimClientShare-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; 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; }; - AB5DFAB4D24167C30421C8C8 /* [CP] Check Pods Manifest.lock */ = { + BE8775A141F6564CD64C643A /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-WebimMobileSDK_Example/Pods-WebimMobileSDK_Example-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Cosmos/Cosmos.framework", + "${BUILT_PRODUCTS_DIR}/FLAnimatedImage/FLAnimatedImage.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseSessions/FirebaseSessions.framework", + "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/Nuke/Nuke.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", + "${BUILT_PRODUCTS_DIR}/PromisesSwift/Promises.framework", + "${BUILT_PRODUCTS_DIR}/SQLite.swift/SQLite.framework", + "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework", + "${BUILT_PRODUCTS_DIR}/WebimKeyboard/WebimKeyboard.framework", + "${BUILT_PRODUCTS_DIR}/WebimMobileSDK/WebimMobileSDK.framework", + "${BUILT_PRODUCTS_DIR}/WebimMobileWidget/WebimMobileWidget.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-WebimMobileSDK_Example-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cosmos.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FLAnimatedImage.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreExtension.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCrashlytics.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseSessions.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nuke.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Promises.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SQLite.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebimKeyboard.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebimMobileSDK.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebimMobileWidget.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - 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"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-WebimMobileSDK_Example/Pods-WebimMobileSDK_Example-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -1237,6 +1283,7 @@ CC1F8E9D2A13A229002494E1 /* WMVisitorFieldsParser.swift in Sources */, 127D2AEC26E6018100FD60DF /* WMTestManager.swift in Sources */, CE45EDE01F95F9F700F56319 /* MimeType.swift in Sources */, + 90A432642BA0674E00628253 /* SendFileManager.swift in Sources */, CCEEB4F129F66A4D000923DF /* SelectVisitorTableViewCell.swift in Sources */, CCEEB4F429F66ECF000923DF /* WMSettingsViewController+SelectUser.swift in Sources */, CCD848932A73E2A0005CDE5A /* UIView.swift in Sources */, @@ -1254,6 +1301,7 @@ BE868AAD29B1CFAB00B77BDD /* ExternalWidgetBuilder.swift in Sources */, CC9BD1AA29F7989600DAF0CC /* UserDefaultsManager.swift in Sources */, CC9BD1A829F797C900DAF0CC /* WMVisitorFieldsManager.swift in Sources */, + 90A432662BA0678800628253 /* URLRequest.swift in Sources */, CCF69DAE2A14C9B400E1FBA8 /* NetworkUtils.swift in Sources */, 1239B89B277F189200D0CC2E /* WMSettingsViewController+UITableView.swift in Sources */, 122DD62127918C0700093609 /* UINavigationController.swift in Sources */, @@ -1350,13 +1398,21 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 90A432682BA06C1700628253 /* UIViewController.swift in Sources */, 909D76DD28F56B000018E54C /* Settings.swift in Sources */, 909D76DA28F55BEA0018E54C /* String.swift in Sources */, + 90A432632BA05BD900628253 /* ColourConstants.swift in Sources */, 909D76CB28F54F380018E54C /* WMShareViewController.swift in Sources */, 909D76DB28F5698F0018E54C /* WebimService.swift in Sources */, + 90A432622B9F54B100628253 /* SendFileManager.swift in Sources */, + 90A432562B9F515300628253 /* ShareProgressTableViewCell.swift in Sources */, 90C5059229B493E200FAD33D /* WMSaveView.swift in Sources */, + 90A432672BA0679D00628253 /* URLRequest.swift in Sources */, + 90F56C9E2C4ED1E600357A6A /* Extensions.swift in Sources */, 909D76D828F552820018E54C /* MimeType.swift in Sources */, + 90A4325A2B9F522600628253 /* CircleProgressIndicator.swift in Sources */, CC5E2B022A8A377E001EEC27 /* WebimServiceController.swift in Sources */, + 90A4325C2B9F527500628253 /* WMShareProgressViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1501,7 +1557,7 @@ DEVELOPMENT_TEAM = 574GE9X9L7; ENABLE_HARDENED_RUNTIME = YES; GCC_C_LANGUAGE_STANDARD = gnu11; - MACOSX_DEPLOYMENT_TARGET = 11.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1525,7 +1581,7 @@ DEVELOPMENT_TEAM = 574GE9X9L7; ENABLE_HARDENED_RUNTIME = YES; GCC_C_LANGUAGE_STANDARD = gnu11; - MACOSX_DEPLOYMENT_TARGET = 11.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -1582,7 +1638,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -1633,7 +1689,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -1644,7 +1700,7 @@ }; 607FACF01AFB9204008FA782 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 475CA95610ABD040CAEA2E09 /* Pods-WebimMobileSDK_Example.debug.xcconfig */; + baseConfigurationReference = F977A2214FA33837D47DAE8F /* Pods-WebimMobileSDK_Example.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -1652,13 +1708,13 @@ CODE_SIGN_ENTITLEMENTS = WebimClientLibrary_Example.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 34; DEVELOPMENT_TEAM = 574GE9X9L7; FRAMEWORK_SEARCH_PATHS = " $(inherited)"; INFOPLIST_FILE = WebimMobileSDK/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 3.41.0; + MARKETING_VERSION = 4.0.6; MODULE_NAME = ExampleApp; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -Xfrontend -warn-long-expression-type-checking=400 -Xfrontend -warn-long-function-bodies=400 -Onone"; PRODUCT_BUNDLE_IDENTIFIER = "ru.webim.Webim-Client"; @@ -1673,7 +1729,7 @@ }; 607FACF11AFB9204008FA782 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = EC1676D646E1C2549F3DAEED /* Pods-WebimMobileSDK_Example.release.xcconfig */; + baseConfigurationReference = F6C4511557995D99EA2FB9F8 /* Pods-WebimMobileSDK_Example.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -1681,13 +1737,13 @@ CODE_SIGN_ENTITLEMENTS = WebimClientLibrary_Example.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 34; DEVELOPMENT_TEAM = 574GE9X9L7; FRAMEWORK_SEARCH_PATHS = " $(inherited)"; INFOPLIST_FILE = WebimMobileSDK/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 3.41.0; + MARKETING_VERSION = 4.0.6; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "ru.webim.Webim-Client"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1701,7 +1757,7 @@ }; 607FACF31AFB9204008FA782 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C3C87E09DE04468C69E37BC8 /* Pods-WebimMobileSDK_Tests.debug.xcconfig */; + baseConfigurationReference = A89133BECF3D2A05742A161C /* Pods-WebimMobileSDK_Tests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -1723,7 +1779,7 @@ }; 607FACF41AFB9204008FA782 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B245B23919CFD976FED79B92 /* Pods-WebimMobileSDK_Tests.release.xcconfig */; + baseConfigurationReference = E7677A9313FBC0C91D073A75 /* Pods-WebimMobileSDK_Tests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; DEVELOPMENT_TEAM = ""; @@ -1740,7 +1796,7 @@ }; 909D76D428F54F380018E54C /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 18EFB50E2E06DDBD48B29C40 /* Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig */; + baseConfigurationReference = 08EF987CCABD336CB294220E /* Pods-WebimMobileSDK_Example-WebimClientShare.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1785,7 +1841,7 @@ }; 909D76D528F54F380018E54C /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 55CD1AEA73D029FB130A9E40 /* Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig */; + baseConfigurationReference = 832A61587957BD696F7D9267 /* Pods-WebimMobileSDK_Example-WebimClientShare.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/Example/WebimMobileSDK/AppDelegate.swift b/Example/WebimMobileSDK/AppDelegate.swift index aa49674f..bd592e02 100644 --- a/Example/WebimMobileSDK/AppDelegate.swift +++ b/Example/WebimMobileSDK/AppDelegate.swift @@ -36,6 +36,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? static var shared: AppDelegate! lazy var isApplicationConnected: Bool = false + var hasRemoteNotification = false private var notificationUserInfo: [AnyHashable: Any]? @@ -138,6 +139,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + hasRemoteNotification = true if let notificationUserInfo = notificationUserInfo { openChatFromNotification(notificationUserInfo) } diff --git a/Example/WebimMobileSDK/AppearanceSettings/ImageConstants.swift b/Example/WebimMobileSDK/AppearanceSettings/ImageConstants.swift index 9c2cda5d..ca4ac1f5 100644 --- a/Example/WebimMobileSDK/AppearanceSettings/ImageConstants.swift +++ b/Example/WebimMobileSDK/AppearanceSettings/ImageConstants.swift @@ -34,6 +34,7 @@ let loadingPlaceholderImage = UIImage(named: "ImagePlaceholder") let navigationBarTitleImageViewImage = #imageLiteral(resourceName: "LogoWebimNavigationBar_dark") let scrollButtonImage = #imageLiteral(resourceName: "SendMessageButton").flipImage(.vertically) let textInputButtonImage = #imageLiteral(resourceName: "SendMessageButton") +let textInputInactiveButtonImage = #imageLiteral(resourceName: "sendButton") let networkErrorView = UIImage(named: "ConnectionImage")! let uploadFileImage = UIImage(named: "FileUploadButtonVisitor")! let readyFileImage = UIImage(named: "FileDownloadSuccess")! diff --git a/Example/WebimMobileSDK/Base.lproj/Localizable.strings b/Example/WebimMobileSDK/Base.lproj/Localizable.strings index 6427c600..0ab54ae7 100644 --- a/Example/WebimMobileSDK/Base.lproj/Localizable.strings +++ b/Example/WebimMobileSDK/Base.lproj/Localizable.strings @@ -37,10 +37,20 @@ //StringConstants.swift "Reply" = "Reply"; //WMStartViewController.xib -"Welcome to the WebimClientLibrary app!" = "Welcome to the Webim Chat App!"; +"Welcome to the WebimClientLibrary app!" = "Welcome to the Webim SDK App!"; //WMThanksAlertView.xib "Your feedback will help us become better." = "Your feedback will help us become better."; //WebimService.swift "Your visitor account is in the black list." = "Your visitor account is in the black list."; //WMSettingsViewController.swift "Parse demo visitor error." = "Parse demo visitor error."; +//ChatViewController+CompletionHandler.swift,UIAlertHandler.swift +"Session creation failed" = "Session creation failed"; +//ChatViewController+Listener.swift +"typing" = "typing"; +//StringConstants.swift +"Edit" = "Edit"; +"Enter message" = "Enter message"; +"Please rate the overall impression of the consultation" = "Please rate the overall impression of the consultation."; +"Rate Operator" = "Rate Agent"; +"Send" = "Send"; diff --git a/Example/WebimMobileSDK/Info.plist b/Example/WebimMobileSDK/Info.plist index a6bba332..babdad37 100644 --- a/Example/WebimMobileSDK/Info.plist +++ b/Example/WebimMobileSDK/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - Webim Chat + Webim SDK App CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/Example/WebimMobileSDK/Models/ExternalWidgetBuilder.swift b/Example/WebimMobileSDK/Models/ExternalWidgetBuilder.swift index efcd5815..e5918177 100644 --- a/Example/WebimMobileSDK/Models/ExternalWidgetBuilder.swift +++ b/Example/WebimMobileSDK/Models/ExternalWidgetBuilder.swift @@ -72,7 +72,7 @@ class ExternalWidgetBuilder { let chatControllerConfig = chatViewControllerConfig(openFromNotification) let imageControllerConfig = imageViewControllerConfig() let fileControllerConfig = fileViewControllerConfig() - + let widget = WMWidgetBuilder() .set(sessionBuilder: sessionBuilder) .set(chatViewControllerConfig: chatControllerConfig) @@ -153,11 +153,16 @@ class ExternalWidgetBuilder { .font : UIFont.systemFont(ofSize: 16, weight: .regular), .foregroundColor : visitorMessageTextColour ] + let visitorConfigCorners: CACornerMask = !WMLocaleManager.isRightOrientationLocale() ? + [.layerMinXMinYCorner, + .layerMaxXMinYCorner, + .layerMinXMaxYCorner] : + [.layerMinXMinYCorner, + .layerMaxXMinYCorner, + .layerMaxXMaxYCorner] let textCellConfig = WMTextCellConfigBuilder() .set(backgroundColor: visitorMessageBubbleColour) - .set(roundCorners: [.layerMinXMinYCorner, - .layerMaxXMinYCorner, - .layerMinXMaxYCorner]) + .set(roundCorners: visitorConfigCorners) .set(cornerRadius: 10) .set(subtitleAttributes: textCellSubtitleAttributes) .set(strokeWidth: 0) @@ -194,9 +199,7 @@ class ExternalWidgetBuilder { ] let fileCellConfig = WMFileCellConfigBuilder() .set(backgroundColor: visitorMessageBubbleColour) - .set(roundCorners: [.layerMinXMinYCorner, - .layerMaxXMinYCorner, - .layerMinXMaxYCorner]) + .set(roundCorners: visitorConfigCorners) .set(cornerRadius: 10) .set(titleAttributes: fileCellTitleAttributes) .set(subtitleAttributes: fileCellSubtitleAttributes) @@ -240,11 +243,16 @@ class ExternalWidgetBuilder { .font : UIFont.systemFont(ofSize: 16, weight: .regular), .foregroundColor : operatorMessageTextColour ] + let operatorConfigCorners: CACornerMask = !WMLocaleManager.isRightOrientationLocale() ? + [.layerMinXMinYCorner, + .layerMaxXMinYCorner, + .layerMaxXMaxYCorner] : + [.layerMinXMinYCorner, + .layerMaxXMinYCorner, + .layerMinXMaxYCorner] let textCellConfig = WMTextCellConfigBuilder() .set(backgroundColor: operatorMessageBubbleColour) - .set(roundCorners: [.layerMinXMinYCorner, - .layerMaxXMinYCorner, - .layerMaxXMaxYCorner]) + .set(roundCorners: operatorConfigCorners) .set(cornerRadius: 10) .set(titleAttributes: cellTitleAttributes) .set(subtitleAttributes: textCellSubtitleAttributes) @@ -273,9 +281,7 @@ class ExternalWidgetBuilder { ] let fileCellConfig = WMFileCellConfigBuilder() .set(backgroundColor: operatorMessageBubbleColour) - .set(roundCorners: [.layerMinXMinYCorner, - .layerMaxXMinYCorner, - .layerMaxXMaxYCorner]) + .set(roundCorners: operatorConfigCorners) .set(cornerRadius: 10) .set(titleAttributes: cellTitleAttributes) .set(subtitleAttributes: fileCellSubtitleAttribute) @@ -299,7 +305,7 @@ class ExternalWidgetBuilder { return operatorCellsConfig } - + private func chatViewControllerConfig(_ openFromNotification: Bool = false) -> WMViewControllerConfig { let visitorCellsConfig = buildVisitorCellsConfig() let operatorCellsConfig = buildOperatorCellsConfig() @@ -326,6 +332,7 @@ class ExternalWidgetBuilder { .build() let toolBarConfig = WMToolbarConfigBuilder() .set(sendButtonImage: textInputButtonImage) + .set(inactiveSendButtonImage: textInputInactiveButtonImage) .set(addAttachmentImage: fileButtonImage) .set(placeholderText: "Enter message".localized) .set(textViewFont: .systemFont(ofSize: 16, weight: .regular)) @@ -396,6 +403,7 @@ class ExternalWidgetBuilder { .set(buttonTitle: rateOperatorButtonTitle) .set(buttonColor: rateOperatorButtonColour) .set(buttonCornerRadius: 8) + .set(changeRateEnabled: true) .build() let chatNavigationBarConfig = WMChatNavigationBarConfigBuilder() .set(backgroundColorOnlineState: navigationBarBarTintColour) @@ -404,9 +412,9 @@ class ExternalWidgetBuilder { .set(textColorOfflineState: navigationBarTintColour) .set(logoImage: navigationBarTitleImageViewImage) .set(canShowTypingIndicator: true) - .set(typingLabelText: "typing") + .set(typingLabelText: "typing".localized) .build() - + let chatConfig = WMChatViewControllerConfigBuilder() .set(showScrollButtonView: true) .set(scrollButtonImage: scrollButtonImage) @@ -428,7 +436,7 @@ class ExternalWidgetBuilder { .build() return chatConfig } - + private func imageViewControllerConfig() -> WMViewControllerConfig { let navigationConfig = WMImageNavigationBarConfigBuilder() .set(backgroundColorOnlineState: .clear) @@ -437,7 +445,7 @@ class ExternalWidgetBuilder { .set(textColorOfflineState: .white) .set(rightBarButtonImage: saveImageButton) .build() - + let imageViewControllerConfig = WMImageViewControllerConfigBuilder() .set(saveViewColor: webimCyan) .set(backgroundColor: .black) @@ -445,7 +453,7 @@ class ExternalWidgetBuilder { .build() return imageViewControllerConfig } - + private func fileViewControllerConfig() -> WMViewControllerConfig { let navigationConfig = WMFileNavigationBarConfigBuilder() .set(backgroundColorOnlineState: navigationBarBarTintColour) @@ -454,7 +462,7 @@ class ExternalWidgetBuilder { .set(textColorOfflineState: navigationBarTintColour) .set(rightBarButtonImage: fileShare) .build() - + let fileViewControllerConfig = WMFileViewControllerConfigBuilder() .set(backgroundColor: .clear) .set(loadingLabelText: loadingFileTitle) @@ -486,7 +494,7 @@ extension ExternalWidgetBuilder { .set(strokeColor: .clear) return popupCellConfig } - + private func defaultCosmosSettings() -> CosmosSettings { var settings = CosmosSettings() settings.fillMode = .full diff --git a/Example/WebimMobileSDK/Utilities/URLRequest.swift b/Example/WebimMobileSDK/Utilities/URLRequest.swift new file mode 100644 index 00000000..a5cc7a46 --- /dev/null +++ b/Example/WebimMobileSDK/Utilities/URLRequest.swift @@ -0,0 +1,16 @@ +// +// URLRequest.swift +// WebimMobileSDK_Example +// +// Created by Anna Frolova on 12.03.2024. +// Copyright © 2024 Webim. All rights reserved. +// + +import Foundation +extension URLRequest { + var isSendFileRequest: Bool { + guard let url = url else { return false } + let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) + return (urlComponents?.queryItems?.contains { $0.value == "upload_file" } == true) + } +} diff --git a/Example/WebimMobileSDK/ViewControllers/LaunchScreenController.swift b/Example/WebimMobileSDK/ViewControllers/LaunchScreenController.swift index b8cfb65d..22bb94b0 100644 --- a/Example/WebimMobileSDK/ViewControllers/LaunchScreenController.swift +++ b/Example/WebimMobileSDK/ViewControllers/LaunchScreenController.swift @@ -85,6 +85,9 @@ class LaunchScreenController: UIViewController { let rootVC = WMStartViewController.loadViewControllerFromXib() let navigationController = UINavigationController(rootViewController: rootVC) AppDelegate.shared.window?.rootViewController = navigationController + if AppDelegate.shared.hasRemoteNotification { + rootVC.startChat(self) + } } ) } diff --git a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/WMSettingsViewController+Setup.swift b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/WMSettingsViewController+Setup.swift index 3907aa51..b36f2c9e 100644 --- a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/WMSettingsViewController+Setup.swift +++ b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/WMSettingsViewController+Setup.swift @@ -91,6 +91,9 @@ extension WMSettingsViewController { ) imageView.image = navigationBarTitleImageViewImage imageView.contentMode = .scaleAspectFill + toogleTestModeGestureRecognizer.numberOfTapsRequired = 5 + imageView.isUserInteractionEnabled = true + imageView.addGestureRecognizer(toogleTestModeGestureRecognizer) } diff --git a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/WMSettingsViewController.swift b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/WMSettingsViewController.swift index 56e1aba7..7f6c5eb6 100644 --- a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/WMSettingsViewController.swift +++ b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/WMSettingsViewController.swift @@ -127,6 +127,7 @@ class WMSettingsViewController: UIViewController { if let navigationController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController { if let startViewController = navigationController.viewControllers.first as? WMStartViewController { startViewController.startChatButton.isHidden = false + startViewController.unreadMessageCounterView.isHidden = false } } } diff --git a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/ar.lproj/WMSettingsViewController.strings b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/ar.lproj/WMSettingsViewController.strings index 1731b1de..20d854c7 100644 --- a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/ar.lproj/WMSettingsViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/ar.lproj/WMSettingsViewController.strings @@ -1,6 +1,6 @@ "0cS-HU-Nd5.accessibilityLabel" = "حقل نص الموقع"; -"0cS-HU-Nd5.placeholder" = "الجوال"; -"0cS-HU-Nd5.text" = "الجوال"; +"0cS-HU-Nd5.placeholder" = "mobile"; +"0cS-HU-Nd5.text" = "mobile"; "5kC-9w-Cf1.accessibilityLabel" = "حقل نص عنوان الصفحة"; "5kC-9w-Cf1.text" = "تطبيق iOS"; "DAx-o8-RLt.text" = "بيانات المستخدم json (اختياري)"; diff --git a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/bg.lproj/WMSettingsViewController.strings b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/bg.lproj/WMSettingsViewController.strings index 343e92b4..e7291b4e 100644 --- a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/bg.lproj/WMSettingsViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/bg.lproj/WMSettingsViewController.strings @@ -1,6 +1,6 @@ "0cS-HU-Nd5.accessibilityLabel" = "Текстово поле за местоположение"; -"0cS-HU-Nd5.placeholder" = "мобилен"; -"0cS-HU-Nd5.text" = "мобилен"; +"0cS-HU-Nd5.placeholder" = "mobile"; +"0cS-HU-Nd5.text" = "mobile"; "5kC-9w-Cf1.accessibilityLabel" = "Текстово поле за заглавие на страница"; "5kC-9w-Cf1.text" = "Приложение за iOS"; "DAx-o8-RLt.text" = "Данни за потребителя в JSON формат (по избор)"; diff --git a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/es.lproj/WMSettingsViewController.strings b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/es.lproj/WMSettingsViewController.strings index 49e8f39b..8d833498 100644 --- a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/es.lproj/WMSettingsViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/es.lproj/WMSettingsViewController.strings @@ -1,6 +1,6 @@ "0cS-HU-Nd5.accessibilityLabel" = "Campo de texto de ubicación"; -"0cS-HU-Nd5.placeholder" = "móvil"; -"0cS-HU-Nd5.text" = "móvil"; +"0cS-HU-Nd5.placeholder" = "mobile"; +"0cS-HU-Nd5.text" = "mobile"; "5kC-9w-Cf1.accessibilityLabel" = "Campo de texto de título de página"; "5kC-9w-Cf1.text" = "Aplicación iOS"; "DAx-o8-RLt.text" = "Datos de usuario json (opcional)"; diff --git a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/he.lproj/WMSettingsViewController.strings b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/he.lproj/WMSettingsViewController.strings index e5db1cd6..0044cdcf 100644 --- a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/he.lproj/WMSettingsViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/he.lproj/WMSettingsViewController.strings @@ -1,6 +1,6 @@ "0cS-HU-Nd5.accessibilityLabel" = "שדה טקסט מיקום"; -"0cS-HU-Nd5.placeholder" = "נייד"; -"0cS-HU-Nd5.text" = "נייד"; +"0cS-HU-Nd5.placeholder" = "mobile"; +"0cS-HU-Nd5.text" = "mobile"; "5kC-9w-Cf1.accessibilityLabel" = "שדה טקסט כותרת הדף"; "5kC-9w-Cf1.text" = "אפליקציית iOS"; "DAx-o8-RLt.text" = "מידע משתמש בפורמט JSON (אופציונלי)"; diff --git a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/pt.lproj/WMSettingsViewController.strings b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/pt.lproj/WMSettingsViewController.strings index 1a290d66..6450a819 100644 --- a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/pt.lproj/WMSettingsViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/pt.lproj/WMSettingsViewController.strings @@ -1,6 +1,6 @@ "0cS-HU-Nd5.accessibilityLabel" = "Campo de texto de localização"; -"0cS-HU-Nd5.placeholder" = "celular"; -"0cS-HU-Nd5.text" = "celular"; +"0cS-HU-Nd5.placeholder" = "mobile"; +"0cS-HU-Nd5.text" = "mobile"; "5kC-9w-Cf1.accessibilityLabel" = "Campo de texto do título da página"; "5kC-9w-Cf1.text" = "app de iOS"; "DAx-o8-RLt.text" = "Dados do usuário json (opcional)"; diff --git a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/uk-UA.lproj/WMSettingsViewController.strings b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/uk-UA.lproj/WMSettingsViewController.strings index 203596a3..7f49f4a6 100644 --- a/Example/WebimMobileSDK/ViewControllers/SettingsViewController/uk-UA.lproj/WMSettingsViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/SettingsViewController/uk-UA.lproj/WMSettingsViewController.strings @@ -1,6 +1,6 @@ "0cS-HU-Nd5.accessibilityLabel" = "Текстове поле для введення місцезнаходження"; -"0cS-HU-Nd5.placeholder" = "мобільний"; -"0cS-HU-Nd5.text" = "мобільний"; +"0cS-HU-Nd5.placeholder" = "mobile"; +"0cS-HU-Nd5.text" = "mobile"; "5kC-9w-Cf1.accessibilityLabel" = "Текстове поле заголовка сторінки"; "5kC-9w-Cf1.text" = "Додаток для iOS"; "DAx-o8-RLt.text" = "Дані користувача json (необов'язково)"; diff --git a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/WMStartViewController.swift b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/WMStartViewController.swift index 37f00b4f..9fc673b0 100644 --- a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/WMStartViewController.swift +++ b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/WMStartViewController.swift @@ -58,8 +58,8 @@ final class WMStartViewController: UIViewController { setupSettingsButton() setupLogoTapGestureRecognizer() setupNavigationBarUpdater() - updateMessageCounter() updateNavigationBar() + self.unreadMessageCounterLabel.layer.masksToBounds = true } override func viewWillAppear(_ animated: Bool) { @@ -81,6 +81,7 @@ final class WMStartViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) startWebimSession() + updateMessageCounter() } @IBAction func startChat(_ sender: Any? = nil) { @@ -132,6 +133,7 @@ final class WMStartViewController: UIViewController { target: self, action: #selector(presentWMLogsViewController(_:))) logoImageView.addGestureRecognizer(gesture) + logoImageView.isUserInteractionEnabled = true } private func setupColorScheme() { @@ -224,6 +226,7 @@ extension WMStartViewController: FatalErrorHandlerDelegate { func showErrorDialog(withMessage message: String) { alertDialogHandler.showCreatingSessionFailureDialog(withMessage: message) startChatButton.isHidden = true + unreadMessageCounterView.isHidden = true } } diff --git a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/ar.lproj/WMStartViewController.strings b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/ar.lproj/WMStartViewController.strings index f094c499..a97d46cb 100644 --- a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/ar.lproj/WMStartViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/ar.lproj/WMStartViewController.strings @@ -1,6 +1,6 @@ "XiB-ag-Lpg.text" = "99"; "fgX-wy-ZIA.accessibilityLabel" = "عنوان الترحيب"; -"fgX-wy-ZIA.text" = "مرحبًا بك في تطبيق Webim Chat!"; +"fgX-wy-ZIA.text" = "مرحبًا بك في تطبيق Webim SDK App!"; "htJ-b1-n8p.accessibilityHint" = "يعرض الإعدادات."; "htJ-b1-n8p.accessibilityLabel" = "الإعدادات"; "htJ-b1-n8p.normalTitle" = "الإعدادات"; diff --git a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/bg.lproj/WMStartViewController.strings b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/bg.lproj/WMStartViewController.strings index 2c26b0de..a9bf5208 100644 --- a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/bg.lproj/WMStartViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/bg.lproj/WMStartViewController.strings @@ -1,6 +1,6 @@ "XiB-ag-Lpg.text" = "99"; "fgX-wy-ZIA.accessibilityLabel" = "Заглавие на приветствието"; -"fgX-wy-ZIA.text" = "Добре дошли в приложението на Webim Chat!"; +"fgX-wy-ZIA.text" = "Добре дошли в приложението на Webim SDK App!"; "htJ-b1-n8p.accessibilityHint" = "Показва настройките."; "htJ-b1-n8p.accessibilityLabel" = "Настройки"; "htJ-b1-n8p.normalTitle" = "Настройки"; diff --git a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/en.lproj/WMStartViewController.strings b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/en.lproj/WMStartViewController.strings index 4417e6e0..3a8e7838 100644 --- a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/en.lproj/WMStartViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/en.lproj/WMStartViewController.strings @@ -1,6 +1,6 @@ "XiB-ag-Lpg.text" = "99"; "fgX-wy-ZIA.accessibilityLabel" = "Greeting title"; -"fgX-wy-ZIA.text" = "Welcome to the Webim Chat App!"; +"fgX-wy-ZIA.text" = "Welcome to the Webim SDK App!"; "htJ-b1-n8p.accessibilityHint" = "Shows settings."; "htJ-b1-n8p.accessibilityLabel" = "Settings"; "htJ-b1-n8p.normalTitle" = "Settings"; diff --git a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/pt.lproj/WMStartViewController.strings b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/pt.lproj/WMStartViewController.strings index dfd1d390..c9ba933c 100644 --- a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/pt.lproj/WMStartViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/pt.lproj/WMStartViewController.strings @@ -1,6 +1,6 @@ "XiB-ag-Lpg.text" = "99"; "fgX-wy-ZIA.accessibilityLabel" = "Título de cumprimento"; -"fgX-wy-ZIA.text" = "Bem-vindo ao aplicativo Webim Chat!"; +"fgX-wy-ZIA.text" = "Bem-vindo ao aplicativo Webim SDK App!"; "htJ-b1-n8p.accessibilityHint" = "Mostra configurações."; "htJ-b1-n8p.accessibilityLabel" = "Configurações"; "htJ-b1-n8p.normalTitle" = "Configurações"; @@ -8,4 +8,4 @@ "wAh-BG-LeG.accessibilityLabel" = "Iniciar chat"; "wAh-BG-LeG.normalTitle" = "Iniciar chat"; "xS6-J8-Sm9.accessibilityLabel" = "Palavras de cumprimento"; -"xS6-J8-Sm9.text" = "To start a chat tap on the button below."; +"xS6-J8-Sm9.text" = "Para iniciar um bate-papo, toque no botão abaixo."; diff --git a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/ru.lproj/WMStartViewController.strings b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/ru.lproj/WMStartViewController.strings index d2735f24..0b097bc5 100644 --- a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/ru.lproj/WMStartViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/ru.lproj/WMStartViewController.strings @@ -1,6 +1,6 @@ "XiB-ag-Lpg.text" = "99"; "fgX-wy-ZIA.accessibilityLabel" = "Приветственное заглавие"; -"fgX-wy-ZIA.text" = "Добро пожаловать в приложение Webim Chat!"; +"fgX-wy-ZIA.text" = "Добро пожаловать в приложение Webim SDK App!"; "htJ-b1-n8p.accessibilityHint" = "Показывает настройки."; "htJ-b1-n8p.accessibilityLabel" = "Настройки"; "htJ-b1-n8p.normalTitle" = "Настройки"; diff --git a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/uk-UA.lproj/WMStartViewController.strings b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/uk-UA.lproj/WMStartViewController.strings index 9f2bd26b..70079f63 100644 --- a/Example/WebimMobileSDK/ViewControllers/WMStartViewController/uk-UA.lproj/WMStartViewController.strings +++ b/Example/WebimMobileSDK/ViewControllers/WMStartViewController/uk-UA.lproj/WMStartViewController.strings @@ -1,6 +1,6 @@ "XiB-ag-Lpg.text" = "99"; "fgX-wy-ZIA.accessibilityLabel" = "Заголовок привітання"; -"fgX-wy-ZIA.text" = "Ласкаво просимо до додатку Webim Chat!"; +"fgX-wy-ZIA.text" = "Ласкаво просимо до додатку Webim SDK App!"; "htJ-b1-n8p.accessibilityHint" = "Показує налаштування."; "htJ-b1-n8p.accessibilityLabel" = "Налаштування"; "htJ-b1-n8p.normalTitle" = "Налаштування"; diff --git a/Example/WebimMobileSDK/ar.lproj/Localizable.strings b/Example/WebimMobileSDK/ar.lproj/Localizable.strings index 46e07047..66f96826 100644 --- a/Example/WebimMobileSDK/ar.lproj/Localizable.strings +++ b/Example/WebimMobileSDK/ar.lproj/Localizable.strings @@ -11,7 +11,7 @@ //WebimService.swift "AccountBlocked" = "الحساب الذي يتم استخدامه لإنشاء الجلسة محظور. يرجى الاتصال بدعم Webim أو استخدام حساب آخر."; //UIAlertHandler.swift -"Alert account name" = "اسم حساب Webim أو عنوان الخادم الكامل (على سبيل المثال، \"demo\" أو \"https://demo.webim.ru\")"; +"Alert account name" = "اسم حساب Webim أو عنوان الخادم الكامل \n (for example, \"demo\" or \"https://demo.webim.ru\")"; //UIAlertHandler.swift "Cancel" = "إلغاء"; //ConnectionErrorView.xib @@ -44,3 +44,15 @@ "Your visitor account is in the black list." = "حساب الزائر الخاص بك في قائمة الحظر."; //WMSettingsViewController.swift "Parse demo visitor error." = "خطأ في تحليل زائر الديمو."; +//ChatViewController+CompletionHandler.swift,UIAlertHandler.swift +"Session creation failed" = "فشل إنشاء الجلسة"; +//ChatViewController+Listener.swift +"typing" = "يكتب"; +//StringConstants.swift +"Edit" = "تحرير"; +"Enter message" = "أدخل رسالة"; +//RateStarsViewController.swift,RateStarsViewController.xib +"Please rate the overall impression of the consultation" = "الرجاء تقييم الانطباع العام للاستشارة."; +//RateStarsViewController.xib +"Rate Operator" = "تقييم الوكيل"; +"Send" = "إرسال"; diff --git a/Example/WebimMobileSDK/bg.lproj/InfoPlist.strings b/Example/WebimMobileSDK/bg.lproj/InfoPlist.strings index 557e2575..e467f947 100644 --- a/Example/WebimMobileSDK/bg.lproj/InfoPlist.strings +++ b/Example/WebimMobileSDK/bg.lproj/InfoPlist.strings @@ -6,13 +6,13 @@ Copyright © 2022 Webim. All rights reserved. */ -"NSPhotoLibraryAddUsageDescription" = "Webim Chat се нуждае от разрешение за достъп до вашата библиотека със снимки, за да можете да запазвате изображения от чата."; +"NSPhotoLibraryAddUsageDescription" = "Webim SDK App се нуждае от разрешение за достъп до вашата библиотека със снимки, за да можете да запазвате изображения от чата."; -"NSCameraUsageDescription" = "Webim Chat се нуждае от разрешение за достъп до вашата камера, за да можете да изпращате снимки в чата."; +"NSCameraUsageDescription" = "Webim SDK App се нуждае от разрешение за достъп до вашата камера, за да можете да изпращате снимки в чата."; -"NSFileProviderDomainUsageDescription" = "Webim Chat се нуждае от разрешение за достъп до папката ви с документи, за да можете да изпращате файлове в чата."; +"NSFileProviderDomainUsageDescription" = "Webim SDK App се нуждае от разрешение за достъп до папката ви с документи, за да можете да изпращате файлове в чата."; -"NSFileProviderPresenceUsageDescription" = "Webim Chat се нуждае от разрешение за достъп до папката ви с документи, за да можете да изпращате файлове в чата."; +"NSFileProviderPresenceUsageDescription" = "Webim SDK App се нуждае от разрешение за достъп до папката ви с документи, за да можете да изпращате файлове в чата."; -"NSPhotoLibraryUsageDescription" = "Webim Chat се нуждае от разрешение за достъп до вашата библиотека със снимки, за да можете да изпращате изображения в чата."; +"NSPhotoLibraryUsageDescription" = "Webim SDK App се нуждае от разрешение за достъп до вашата библиотека със снимки, за да можете да изпращате изображения в чата."; diff --git a/Example/WebimMobileSDK/bg.lproj/Localizable.strings b/Example/WebimMobileSDK/bg.lproj/Localizable.strings index 7c7acac8..5c76b31b 100644 --- a/Example/WebimMobileSDK/bg.lproj/Localizable.strings +++ b/Example/WebimMobileSDK/bg.lproj/Localizable.strings @@ -44,3 +44,13 @@ "Your visitor account is in the black list." = "Вашият акаунт на посетител е в черния списък."; //WMSettingsViewController.swift "Parse demo visitor error." = "Грешка при анализа на демонстрационен посетител."; +//ChatViewController+CompletionHandler.swift,UIAlertHandler.swift +"Session creation failed" = "Създаването на сесия неуспешно"; +//ChatViewController+Listener.swift +"typing" = "пише"; +//StringConstants.swift +"Edit" = "Редактиране"; +"Enter message" = "Въведете съобщение"; +"Please rate the overall impression of the consultation" = "Моля, оценете общия впечатление от консултацията."; +"Rate Operator" = "Оцени агента"; +"Send" = "Изпращане"; diff --git a/Example/WebimMobileSDK/en.lproj/InfoPlist.strings b/Example/WebimMobileSDK/en.lproj/InfoPlist.strings index f1643e84..87be4614 100644 --- a/Example/WebimMobileSDK/en.lproj/InfoPlist.strings +++ b/Example/WebimMobileSDK/en.lproj/InfoPlist.strings @@ -6,12 +6,12 @@ Copyright © 2022 Webim. All rights reserved. */ -"NSPhotoLibraryAddUsageDescription" = "Webim Chat needs permission to access your photo library so you can save images from chat."; +"NSPhotoLibraryAddUsageDescription" = "Webim SDK App needs permission to access your photo library so you can save images from chat."; -"NSCameraUsageDescription" = "Webim Chat needs permission to access your camera so you can send photos to chat."; +"NSCameraUsageDescription" = "Webim SDK App needs permission to access your camera so you can send photos to chat."; -"NSFileProviderDomainUsageDescription" = "Webim Chat needs permission to access your documents folder so you can send files to chat."; +"NSFileProviderDomainUsageDescription" = "Webim SDK App needs permission to access your documents folder so you can send files to chat."; -"NSFileProviderPresenceUsageDescription" = "Webim Chat needs permission to access your documents folder so you can send files to chat."; +"NSFileProviderPresenceUsageDescription" = "Webim SDK App needs permission to access your documents folder so you can send files to chat."; -"NSPhotoLibraryUsageDescription" = "Webim Chat needs permission to access your photo library so you can send images to chat."; +"NSPhotoLibraryUsageDescription" = "Webim SDK App needs permission to access your photo library so you can send images to chat."; diff --git a/Example/WebimMobileSDK/en.lproj/Localizable.strings b/Example/WebimMobileSDK/en.lproj/Localizable.strings index 6427c600..08d92a0c 100644 --- a/Example/WebimMobileSDK/en.lproj/Localizable.strings +++ b/Example/WebimMobileSDK/en.lproj/Localizable.strings @@ -11,7 +11,7 @@ //WebimService.swift "AccountBlocked" = "Account that is used to create session is blocked. Please contact Webim support or use another one."; //UIAlertHandler.swift -"Alert account name" = "Webim account name or full server address (for example, \"demo\" or \"https://demo.webim.ru\")"; +"Alert account name" = "שם חשבון Webim או כתובת מלאה של השרת (לדוגמה, \"דמו\" או \"https://demo.webim.ru\")"; //UIAlertHandler.swift "Cancel" = "Cancel"; //ConnectionErrorView.xib @@ -37,10 +37,20 @@ //StringConstants.swift "Reply" = "Reply"; //WMStartViewController.xib -"Welcome to the WebimClientLibrary app!" = "Welcome to the Webim Chat App!"; +"Welcome to the WebimClientLibrary app!" = "Welcome to the Webim SDK App!"; //WMThanksAlertView.xib "Your feedback will help us become better." = "Your feedback will help us become better."; //WebimService.swift "Your visitor account is in the black list." = "Your visitor account is in the black list."; //WMSettingsViewController.swift "Parse demo visitor error." = "Parse demo visitor error."; +//ChatViewController+CompletionHandler.swift,UIAlertHandler.swift +"Session creation failed" = "Session creation failed"; +//ChatViewController+Listener.swift +"typing" = "typing"; +//StringConstants.swift +"Edit" = "Edit"; +"Enter message" = "Enter message"; +"Please rate the overall impression of the consultation" = "Please rate the overall impression of the consultation."; +"Rate Operator" = "Rate Agent"; +"Send" = "Send"; diff --git a/Example/WebimMobileSDK/es.lproj/InfoPlist.strings b/Example/WebimMobileSDK/es.lproj/InfoPlist.strings index 55c5517d..361aebe0 100644 --- a/Example/WebimMobileSDK/es.lproj/InfoPlist.strings +++ b/Example/WebimMobileSDK/es.lproj/InfoPlist.strings @@ -6,12 +6,12 @@ Copyright © 2022 Webim. All rights reserved. */ -"NSPhotoLibraryAddUsageDescription" = "Webim Chat necesita permiso para acceder a tu biblioteca de fotos para que puedas guardar imágenes del chat."; +"NSPhotoLibraryAddUsageDescription" = "Webim SDK App necesita permiso para acceder a tu biblioteca de fotos para que puedas guardar imágenes del chat."; -"NSCameraUsageDescription" = "Webim Chat necesita permiso para acceder a tu cámara para que puedas enviar fotos al chat."; +"NSCameraUsageDescription" = "Webim SDK App necesita permiso para acceder a tu cámara para que puedas enviar fotos al chat."; -"NSFileProviderDomainUsageDescription" = "Webim Chat necesita permiso para acceder a tu carpeta de documentos para que puedas enviar archivos al chat."; +"NSFileProviderDomainUsageDescription" = "Webim SDK App necesita permiso para acceder a tu carpeta de documentos para que puedas enviar archivos al chat."; -"NSFileProviderPresenceUsageDescription" = "Webim Chat necesita permiso para acceder a tu carpeta de documentos para que puedas enviar archivos al chat."; +"NSFileProviderPresenceUsageDescription" = "Webim SDK App necesita permiso para acceder a tu carpeta de documentos para que puedas enviar archivos al chat."; -"NSPhotoLibraryUsageDescription" = "Webim Chat necesita permiso para acceder a tu biblioteca de fotos para que puedas enviar imágenes al chat."; +"NSPhotoLibraryUsageDescription" = "Webim SDK App necesita permiso para acceder a tu biblioteca de fotos para que puedas enviar imágenes al chat."; diff --git a/Example/WebimMobileSDK/es.lproj/Localizable.strings b/Example/WebimMobileSDK/es.lproj/Localizable.strings index d7af923c..858c7662 100644 --- a/Example/WebimMobileSDK/es.lproj/Localizable.strings +++ b/Example/WebimMobileSDK/es.lproj/Localizable.strings @@ -44,3 +44,13 @@ "Your visitor account is in the black list." = "Tu cuenta de visitante está en la lista negra."; //WMSettingsViewController.swift "Parse demo visitor error." = "Error al analizar el visitante de demostración."; +//ChatViewController+CompletionHandler.swift,UIAlertHandler.swift +"Session creation failed" = "Fallo al crear la sesión"; +//ChatViewController+Listener.swift +"typing" = "escribiendo"; +//StringConstants.swift +"Edit" = "Editar"; +"Enter message" = "Escribe un mensaje"; +"Please rate the overall impression of the consultation" = "Por favor califica la impresión general de la consulta."; +"Rate Operator" = "Calificar agente"; +"Send" = "Enviar"; diff --git a/Example/WebimMobileSDK/he.lproj/InfoPlist.strings b/Example/WebimMobileSDK/he.lproj/InfoPlist.strings index 8aab1372..855f9950 100644 --- a/Example/WebimMobileSDK/he.lproj/InfoPlist.strings +++ b/Example/WebimMobileSDK/he.lproj/InfoPlist.strings @@ -6,12 +6,12 @@ Copyright © 2022 Webim. All rights reserved. */ -"NSPhotoLibraryAddUsageDescription" = "נידרשת רשות מ-Webim Chat ליכולת לגשת אל ספריית התמונות שלך כדי שתוכל לשמור תמונות מהצ'אט."; +"NSPhotoLibraryAddUsageDescription" = "נידרשת רשות מ-Webim SDK App ליכולת לגשת אל ספריית התמונות שלך כדי שתוכל לשמור תמונות מהצ'אט."; -"NSCameraUsageDescription" = "נידרשת רשות מ-Webim Chat ליכולת לגשת אל המצלמה שלך כדי שתוכל לשלוח תמונות לצ'אט."; +"NSCameraUsageDescription" = "נידרשת רשות מ-Webim SDK App ליכולת לגשת אל המצלמה שלך כדי שתוכל לשלוח תמונות לצ'אט."; -"NSFileProviderDomainUsageDescription" = "נידרשת רשות מ-Webim Chat ליכולת לגשת אל תיקיית המסמכים שלך כדי שתוכל לשלוח קבצים לצ'אט."; +"NSFileProviderDomainUsageDescription" = "נידרשת רשות מ-Webim SDK App ליכולת לגשת אל תיקיית המסמכים שלך כדי שתוכל לשלוח קבצים לצ'אט."; -"NSFileProviderPresenceUsageDescription" = "נידרשת רשות מ-Webim Chat ליכולת לגשת אל תיקיית המסמכים שלך כדי שתוכל לשלוח קבצים לצ'אט"; +"NSFileProviderPresenceUsageDescription" = "נידרשת רשות מ-Webim SDK App ליכולת לגשת אל תיקיית המסמכים שלך כדי שתוכל לשלוח קבצים לצ'אט"; -"NSPhotoLibraryUsageDescription" = "נידרשת רשות מ-Webim Chat ליכולת לגשת אל ספריית התמונות שלך כדי שתוכל לשלוח תמונות לצ'אט."; +"NSPhotoLibraryUsageDescription" = "נידרשת רשות מ-Webim SDK App ליכולת לגשת אל ספריית התמונות שלך כדי שתוכל לשלוח תמונות לצ'אט."; diff --git a/Example/WebimMobileSDK/he.lproj/Localizable.strings b/Example/WebimMobileSDK/he.lproj/Localizable.strings index d0a85b28..b1ebf157 100644 --- a/Example/WebimMobileSDK/he.lproj/Localizable.strings +++ b/Example/WebimMobileSDK/he.lproj/Localizable.strings @@ -11,7 +11,8 @@ //WebimService.swift "AccountBlocked" = "החשבון שמשמש ליצירת הסשן חסום. יש ליצור קשר עם תמיכת Webim או להשתמש בחשבון אחר."; //UIAlertHandler.swift -"Alert account name" = "שם החשבון של Webim או כתובת השרת המלאה (לדוגמה, \"demo\" או \"https://demo.webim.ru\")"; + +"Alert account name" = "שם חשבון Webim או כתובת מלאה של השרת \n (for example, \"demo\" or \"https://demo.webim.ru\") "; //UIAlertHandler.swift "Cancel" = "ביטול"; //ConnectionErrorView.xib @@ -44,3 +45,13 @@ "Your visitor account is in the black list." = "חשבון המבקר שלך נמצא ברשימה השחורה."; //WMSettingsViewController.swift "Parse demo visitor error." = "שגיאה בניתוח מבקר דמו."; +//ChatViewController+CompletionHandler.swift,UIAlertHandler.swift +"Session creation failed" = "נכשל ביצירת הסשן"; +//ChatViewController+Listener.swift +"typing" = "מקליד"; +//StringConstants.swift +"Edit" = "ערוך"; +"Enter message" = "הזן הודעה"; +"Please rate the overall impression of the consultation" = "נא לדרג את הרושם הכללי של הייעוץ."; +"Rate Operator" = "דרג את הסוכן"; +"Send" = "שלח"; diff --git a/Example/WebimMobileSDK/pt.lproj/InfoPlist.strings b/Example/WebimMobileSDK/pt.lproj/InfoPlist.strings index d5c479bb..22a80a5a 100644 --- a/Example/WebimMobileSDK/pt.lproj/InfoPlist.strings +++ b/Example/WebimMobileSDK/pt.lproj/InfoPlist.strings @@ -6,12 +6,12 @@ Copyright © 2022 Webim. All rights reserved. */ -"NSPhotoLibraryAddUsageDescription" = "O Webim Chat precisa de permissão para acessar sua biblioteca de fotos para que você possa salvar imagens do chat."; +"NSPhotoLibraryAddUsageDescription" = "O Webim SDK App precisa de permissão para acessar sua biblioteca de fotos para que você possa salvar imagens do chat."; -"NSCameraUsageDescription" = "O Webim Chat precisa de permissão para acessar sua câmera para que você possa enviar fotos para o chat."; +"NSCameraUsageDescription" = "O Webim SDK App precisa de permissão para acessar sua câmera para que você possa enviar fotos para o chat."; -"NSFileProviderDomainUsageDescription" = "O Webim Chat precisa de permissão para acessar sua pasta de documentos para que você possa enviar arquivos para o chat."; +"NSFileProviderDomainUsageDescription" = "O Webim SDK App precisa de permissão para acessar sua pasta de documentos para que você possa enviar arquivos para o chat."; -"NSFileProviderPresenceUsageDescription" = "O Webim Chat precisa de permissão para acessar sua pasta de documentos para que você possa enviar arquivos para o chat."; +"NSFileProviderPresenceUsageDescription" = "O Webim SDK App precisa de permissão para acessar sua pasta de documentos para que você possa enviar arquivos para o chat."; -"NSPhotoLibraryUsageDescription" = "O Webim Chat precisa de permissão para acessar sua biblioteca de fotos para que você possa enviar imagens para o chat."; +"NSPhotoLibraryUsageDescription" = "O Webim SDK App precisa de permissão para acessar sua biblioteca de fotos para que você possa enviar imagens para o chat."; diff --git a/Example/WebimMobileSDK/pt.lproj/Localizable.strings b/Example/WebimMobileSDK/pt.lproj/Localizable.strings index be8b3ee0..70f01a41 100644 --- a/Example/WebimMobileSDK/pt.lproj/Localizable.strings +++ b/Example/WebimMobileSDK/pt.lproj/Localizable.strings @@ -27,7 +27,7 @@ //AdditionalKeys.swift "P.OF" = "%@ enviou um arquivo: %@."; //AdditionalKeys.swift -"P.OM" = "%%@ escreveu uma mensagem: %@"; +"P.OM" = "%@ escreveu uma mensagem: %@"; //AdditionalKeys.swift "P.RO" = "Avalie o agente."; //StringConstants.swift @@ -37,10 +37,20 @@ //StringConstants.swift "Reply" = "Responder"; //WMStartViewController.xib -"Welcome to the WebimClientLibrary app!" = "Bem-vindo ao aplicativo WebimClientLibrary!"; +"Welcome to the WebimClientLibrary app!" = "Bem-vindo ao aplicativo de bate-papo Webim!"; //WMThanksAlertView.xib "Your feedback will help us become better." = "Seu feedback nos ajudará a melhorar."; //WebimService.swift "Your visitor account is in the black list." = "Sua conta de visitante está na lista negra."; //WMSettingsViewController.swift "Parse demo visitor error." = "Erro ao analisar o visitante de demonstração."; +//ChatViewController+CompletionHandler.swift,UIAlertHandler.swift +"Session creation failed" = "Falha na criação da sessão"; +//ChatViewController+Listener.swift +"typing" = "digitando"; +//StringConstants.swift +"Edit" = "Editar"; +"Enter message" = "Inserir mensagem"; +"Please rate the overall impression of the consultation" = "Por favor, avalie a impressão geral da consulta."; +"Rate Operator" = "Avaliar Agente"; +"Send" = "Enviar"; diff --git a/Example/WebimMobileSDK/ru.lproj/Localizable.strings b/Example/WebimMobileSDK/ru.lproj/Localizable.strings index 2998218a..d8cbba7f 100644 --- a/Example/WebimMobileSDK/ru.lproj/Localizable.strings +++ b/Example/WebimMobileSDK/ru.lproj/Localizable.strings @@ -35,12 +35,22 @@ //StringConstants.swift "Delete" = "Удалить"; //StringConstants.swift -"Reply" = "Редактировать"; +"Reply" = "Ответить"; //WMStartViewController.xib -"Welcome to the WebimClientLibrary app!" = "Добро пожаловать в приложение Webim Chat!"; +"Welcome to the WebimClientLibrary app!" = "Добро пожаловать в приложение Webim SDK App!"; //WMThanksAlertView.xib "Your feedback will help us become better." = "Ваш отзыв поможет нам стать лучше."; //WebimService.swift "Your visitor account is in the black list." = "Ваша учетная запись посетителя находится в черном списке."; //WMSettingsViewController.swift "Parse demo visitor error." = "Ошибка получения данных о пользователе."; +//ChatViewController+CompletionHandler.swift,UIAlertHandler.swift +"Session creation failed" = "Неудачная попытка создания сессии"; +//ChatViewController+Listener.swift +"typing" = "печатает"; +//StringConstants.swift +"Edit" = "Редактировать"; +"Enter message" = "Введите сообщение"; +"Please rate the overall impression of the consultation" = "Пожалуйста, оцените общие впечатления от консультации."; +"Rate Operator" = "Оценить оператора"; +"Send" = "Отправить"; diff --git a/Example/WebimMobileSDK/uk-UA.lproj/InfoPlist.strings b/Example/WebimMobileSDK/uk-UA.lproj/InfoPlist.strings index 297bf64c..1d93126e 100644 --- a/Example/WebimMobileSDK/uk-UA.lproj/InfoPlist.strings +++ b/Example/WebimMobileSDK/uk-UA.lproj/InfoPlist.strings @@ -6,11 +6,11 @@ Copyright © 2022 Webim. All rights reserved. */ -"NSPhotoLibraryAddUsageDescription" = "Webim Chat потребує дозволу на доступ до вашої фотокінотеки, щоб ви могли зберігати зображення з чату."; +"NSPhotoLibraryAddUsageDescription" = "Webim SDK App потребує дозволу на доступ до вашої фотокінотеки, щоб ви могли зберігати зображення з чату."; -"NSCameraUsageDescription" = "Webim Chat потребує дозволу на доступ до вашої камери, щоб ви могли надсилати фотографії в чат."; +"NSCameraUsageDescription" = "Webim SDK App потребує дозволу на доступ до вашої камери, щоб ви могли надсилати фотографії в чат."; -"NSFileProviderDomainUsageDescription" = "Webim Chat потребує дозволу на доступ до вашої папки з документами, щоб ви могли надсилати файли в чат."; +"NSFileProviderDomainUsageDescription" = "Webim SDK App потребує дозволу на доступ до вашої папки з документами, щоб ви могли надсилати файли в чат."; -"NSFileProviderPresenceUsageDescription" = "Webim Chat потребує дозволу на доступ до вашої папки з документами, щоб ви могли надсилати файли в чат."; +"NSFileProviderPresenceUsageDescription" = "Webim SDK App потребує дозволу на доступ до вашої папки з документами, щоб ви могли надсилати файли в чат."; diff --git a/Example/WebimMobileSDK/uk-UA.lproj/Localizable.strings b/Example/WebimMobileSDK/uk-UA.lproj/Localizable.strings index c927f198..57c6c3bd 100644 --- a/Example/WebimMobileSDK/uk-UA.lproj/Localizable.strings +++ b/Example/WebimMobileSDK/uk-UA.lproj/Localizable.strings @@ -44,3 +44,13 @@ "Your visitor account is in the black list." = "Ваш обліковий запис відвідувача перебуває в чорному списку."; //WMSettingsViewController.swift "Parse demo visitor error." = "Помилка розбору демонстраційного відвідувача."; +//ChatViewController+CompletionHandler.swift,UIAlertHandler.swift +"Session creation failed" = "Створення сесії не вдалося"; +//ChatViewController+Listener.swift +"typing" = "печатання"; +//StringConstants.swift +"Edit" = "Редагувати"; +"Enter message" = "Введіть повідомлення"; +"Please rate the overall impression of the consultation" = "Будь ласка, оцініть загальне враження від консультації."; +"Rate Operator" = "Оцінити агента"; +"Send" = "Надіслати"; diff --git a/LICENSE b/LICENSE index 5dcf0e38..7a301c67 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017-2023 Webim +Copyright (c) 2017-2024 Webim Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 1e74cba8..b028acf4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Mimimum Webim server version supported – **10.5.30**. ## Installation -> Minimum iOS version supported – 9.0. +> Minimum iOS version supported – 12.0. ### CocoaPods @@ -20,7 +20,7 @@ pod 'WebimMobileSDK' Add following line to your **Cartfile**: ``` -github "webim/webim-client-sdk-ios" ~> 3.41.11 +github "webim/webim-client-sdk-ios" ~> 3.42.0 ``` ### Swift Package Manager @@ -41,7 +41,8 @@ Previous _Objective-C_ version (version numbers 2.x.x) can be reached from **ver ## Release notes -* Method `getThumbURL()` fixed. +* Automatically send unsent messages from the last visit. +* Bugs fixed and stability improved. ## Example diff --git a/WebimMobileSDK.podspec b/WebimMobileSDK.podspec index b986f8f0..5bc7c63b 100644 --- a/WebimMobileSDK.podspec +++ b/WebimMobileSDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'WebimMobileSDK' - s.version = '3.41.11' + s.version = '3.42.0' s.author = { 'Webim.ru Ltd.' => 'n.lazarev-zubov@webim.ru' } s.homepage = 'https://webim.ru/integration/mobile-sdk/ios-sdk-howto/' diff --git a/WebimMobileSDK/Backend/AbstractRequestLoop.swift b/WebimMobileSDK/Backend/AbstractRequestLoop.swift index f83bea68..03c60f09 100644 --- a/WebimMobileSDK/Backend/AbstractRequestLoop.swift +++ b/WebimMobileSDK/Backend/AbstractRequestLoop.swift @@ -61,14 +61,17 @@ class AbstractRequestLoop { private var currentDataTask: URLSessionDataTask? let completionHandlerExecutor: ExecIfNotDestroyedHandlerExecutor? let internalErrorListener: InternalErrorListener? + var baseURL: String var requestHeader: [String: String]? init(completionHandlerExecutor: ExecIfNotDestroyedHandlerExecutor?, internalErrorListener: InternalErrorListener?, - requestHeader: [String: String]?) { + requestHeader: [String: String]?, + baseURL: String) { self.completionHandlerExecutor = completionHandlerExecutor self.internalErrorListener = internalErrorListener self.requestHeader = requestHeader + self.baseURL = baseURL } // MARK: - Methods @@ -105,7 +108,7 @@ class AbstractRequestLoop { func perform(request: URLRequest) throws -> Data { var requestWithUserAgent = request - requestWithUserAgent.setValue("iOS: Webim-Client 3.41.11; (\(UIDevice.current.model); \(UIDevice.current.systemVersion)); Bundle ID and version: \(Bundle.main.bundleIdentifier ?? "none") \(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "none")", forHTTPHeaderField: "User-Agent") + requestWithUserAgent.setValue("iOS: Webim-Client 3.42.0; (\(UIDevice.current.model); \(UIDevice.current.systemVersion)); Bundle ID and version: \(Bundle.main.bundleIdentifier ?? "none") \(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "none")", forHTTPHeaderField: "User-Agent") for (key, value) in requestHeader ?? [:] { requestWithUserAgent.setValue(value, forHTTPHeaderField: key) @@ -144,6 +147,16 @@ class AbstractRequestLoop { ) if let error = error { + if let error = error as NSError?, + error.domain == NSURLErrorDomain && error.code == NSURLErrorCannotFindHost, + var url = requestWithUserAgent.url?.absoluteString { + if (url.starts(with: baseURL)) { + baseURL = InternalUtils.changeDomainFor(url: baseURL) + } + url = InternalUtils.changeDomainFor(url: url) + requestWithUserAgent.url = URL(string: url) + } + semaphore.signal() WebimInternalLogger.shared.log( diff --git a/WebimMobileSDK/Backend/ActionRequestLoop.swift b/WebimMobileSDK/Backend/ActionRequestLoop.swift index 0e43cad0..7ed60fc2 100644 --- a/WebimMobileSDK/Backend/ActionRequestLoop.swift +++ b/WebimMobileSDK/Backend/ActionRequestLoop.swift @@ -44,14 +44,15 @@ class ActionRequestLoop: AbstractRequestLoop { // MARK: - Initialization init(completionHandlerExecutor: ExecIfNotDestroyedHandlerExecutor, - internalErrorListener: InternalErrorListener, notFatalErrorHandler: NotFatalErrorHandler?, requestHeader: [String: String]?) { - super.init(completionHandlerExecutor: completionHandlerExecutor, internalErrorListener: internalErrorListener, requestHeader: requestHeader) + internalErrorListener: InternalErrorListener, notFatalErrorHandler: NotFatalErrorHandler?, requestHeader: [String: String]?, baseURL: String) { + super.init(completionHandlerExecutor: completionHandlerExecutor, internalErrorListener: internalErrorListener, requestHeader: requestHeader, baseURL: baseURL) } init(completionHandlerExecutor: ExecIfNotDestroyedHandlerExecutor, internalErrorListener: InternalErrorListener, - requestHeader: [String: String]?) { - super.init(completionHandlerExecutor: completionHandlerExecutor, internalErrorListener: internalErrorListener, requestHeader: requestHeader) + requestHeader: [String: String]?, + baseURL: String) { + super.init(completionHandlerExecutor: completionHandlerExecutor, internalErrorListener: internalErrorListener, requestHeader: requestHeader, baseURL: baseURL) } // MARK: - Methods @@ -197,7 +198,6 @@ class ActionRequestLoop: AbstractRequestLoop { parameterDictionary[Parameter.authorizationToken.rawValue] = usedAuthorizationData.getAuthorizationToken() } let parametersString = request.getContentType() == ContentType.jsonEncoded.rawValue ? parameterDictionary.jsonFromHTTPParameters() : parameterDictionary.stringFromHTTPParameters() - var urlRequest: URLRequest? let httpMethod = request.getHTTPMethod() diff --git a/WebimMobileSDK/Backend/DeltaRequestLoop.swift b/WebimMobileSDK/Backend/DeltaRequestLoop.swift index 2754bff7..dca708ba 100644 --- a/WebimMobileSDK/Backend/DeltaRequestLoop.swift +++ b/WebimMobileSDK/Backend/DeltaRequestLoop.swift @@ -41,7 +41,6 @@ class DeltaRequestLoop: AbstractRequestLoop { // MARK: - Properties private static var providedAuthenticationTokenErrorCount = 0 private let appVersion: String? - private let baseURL: String private let deltaCallback: DeltaCallback private let deviceID: String @@ -49,7 +48,7 @@ class DeltaRequestLoop: AbstractRequestLoop { @WMSynchronized var authorizationData: AuthorizationData? @WMSynchronized private var providedAuthenticationToken: String? @WMSynchronized var queue: DispatchQueue? - @WMSynchronized var since: Int64 = 0 + @WMSynchronized var since: String = "0" @WMSynchronized private var deviceToken: String? @WMSynchronized private var remoteNotificationSystem: Webim.RemoteNotificationSystem? @WMSynchronized private var location: String @@ -84,7 +83,6 @@ class DeltaRequestLoop: AbstractRequestLoop { requestHeader: [String: String]?) { self.deltaCallback = deltaCallback self.sessionParametersListener = sessionParametersListener - self.baseURL = baseURL self.title = title self.location = location self.appVersion = appVersion @@ -98,7 +96,7 @@ class DeltaRequestLoop: AbstractRequestLoop { self.providedAuthenticationTokenStateListener = providedAuthenticationTokenStateListener self.providedAuthenticationToken = providedAuthenticationToken self.prechat = prechat - super.init(completionHandlerExecutor: completionHandlerExecutor, internalErrorListener: internalErrorListener, requestHeader: requestHeader) + super.init(completionHandlerExecutor: completionHandlerExecutor, internalErrorListener: internalErrorListener, requestHeader: requestHeader, baseURL: baseURL) } // MARK: - Methods @@ -132,7 +130,7 @@ class DeltaRequestLoop: AbstractRequestLoop { self.location = location authorizationData = nil - since = 0 + since = "0" requestInitialization() } @@ -143,7 +141,7 @@ class DeltaRequestLoop: AbstractRequestLoop { func run() { while isRunning() { - if authorizationData != nil && since != 0 { + if authorizationData != nil && since != "0" { requestDelta() } else { requestAccountConfig() @@ -190,9 +188,9 @@ class DeltaRequestLoop: AbstractRequestLoop { } func requestInitialization() { - let url = URL(string: baseURL + ServerPathSuffix.getDelta.rawValue + "?" + getInitializationParameterString()) + let url = URL(string: baseURL + ServerPathSuffix.initPath.rawValue + "?" + getInitializationParameterString()) var request = URLRequest(url: url!) - request.setValue("3.41.11", forHTTPHeaderField: Parameter.webimSDKVersion.rawValue) + request.setValue("3.42.0", forHTTPHeaderField: Parameter.webimSDKVersion.rawValue) request.httpMethod = AbstractRequestLoop.HTTPMethods.get.rawValue do { @@ -213,7 +211,7 @@ class DeltaRequestLoop: AbstractRequestLoop { func requestAccountConfig() { let url = URL(string: baseURL + ServerPathSuffix.getServerSideSettings.rawValue + "?" + "location=" + location) var request = URLRequest(url: url!) - request.setValue("3.41.11", forHTTPHeaderField: Parameter.webimSDKVersion.rawValue) + request.setValue("3.42.0", forHTTPHeaderField: Parameter.webimSDKVersion.rawValue) request.httpMethod = AbstractRequestLoop.HTTPMethods.get.rawValue do { @@ -362,7 +360,7 @@ class DeltaRequestLoop: AbstractRequestLoop { private func sleepBetweenInitializationAttempts() { authorizationData = nil - since = 0 + since = "0" usleep(1_000_000) // 1s } @@ -409,7 +407,7 @@ class DeltaRequestLoop: AbstractRequestLoop { private func handleReinitializationRequiredError() { authorizationData = nil - since = 0 + since = "0" } private func handleProvidedAuthenticationTokenNotFoundError() { diff --git a/WebimMobileSDK/Backend/FAQActions.swift b/WebimMobileSDK/Backend/FAQActions.swift index 56a93713..29ca420e 100644 --- a/WebimMobileSDK/Backend/FAQActions.swift +++ b/WebimMobileSDK/Backend/FAQActions.swift @@ -59,14 +59,11 @@ class FAQActions { } // MARK: - Properties - private let baseURL: String private let faqRequestLoop: FAQRequestLoop private static let deviceID = ClientSideID.generateClientSideID() // MARK: - Initialization - init(baseURL: String, - faqRequestLoop: FAQRequestLoop) { - self.baseURL = baseURL + init(faqRequestLoop: FAQRequestLoop) { self.faqRequestLoop = faqRequestLoop } @@ -77,7 +74,7 @@ class FAQActions { let dataToPost = [Parameter.itemId.rawValue: itemId, Parameter.userId.rawValue: FAQActions.deviceID] as [String: Any] - let urlString = baseURL + ServerPathSuffix.item.rawValue + let urlString = faqRequestLoop.baseURL + ServerPathSuffix.item.rawValue faqRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -90,7 +87,7 @@ class FAQActions { let dataToPost = [Parameter.categoryId.rawValue: categoryId, Parameter.userId.rawValue: FAQActions.deviceID] as [String: Any] - let urlString = baseURL + ServerPathSuffix.category.rawValue + let urlString = faqRequestLoop.baseURL + ServerPathSuffix.category.rawValue faqRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -107,7 +104,7 @@ class FAQActions { Parameter.language.rawValue: language, Parameter.departmentKey.rawValue: departmentKey] as [String: Any] - let urlString = baseURL + ServerPathSuffix.categories.rawValue + let urlString = faqRequestLoop.baseURL + ServerPathSuffix.categories.rawValue faqRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -119,7 +116,7 @@ class FAQActions { completion: @escaping (_ faqStructure: Data?) throws -> ()) { let dataToPost = [Parameter.categoryId.rawValue: categoryId] as [String: Any] - let urlString = baseURL + ServerPathSuffix.structure.rawValue + let urlString = faqRequestLoop.baseURL + ServerPathSuffix.structure.rawValue faqRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -135,7 +132,7 @@ class FAQActions { Parameter.query.rawValue: query, Parameter.limit.rawValue: limit] as [String: Any] - let urlString = baseURL + ServerPathSuffix.search.rawValue + let urlString = faqRequestLoop.baseURL + ServerPathSuffix.search.rawValue faqRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -148,7 +145,7 @@ class FAQActions { let dataToPost = [Parameter.itemId.rawValue: itemId, Parameter.userId.rawValue: FAQActions.deviceID] as [String: Any] - let urlString = baseURL + ServerPathSuffix.like.rawValue + let urlString = faqRequestLoop.baseURL + ServerPathSuffix.like.rawValue faqRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -161,7 +158,7 @@ class FAQActions { let dataToPost = [Parameter.itemId.rawValue: itemId, Parameter.userId.rawValue: FAQActions.deviceID] as [String: Any] - let urlString = baseURL + ServerPathSuffix.dislike.rawValue + let urlString = faqRequestLoop.baseURL + ServerPathSuffix.dislike.rawValue faqRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -181,7 +178,7 @@ class FAQActions { let dataToPost = [Parameter.itemId.rawValue: itemId, Parameter.open.rawValue: source] as [String: Any] - let urlString = baseURL + ServerPathSuffix.track.rawValue + let urlString = faqRequestLoop.baseURL + ServerPathSuffix.track.rawValue faqRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, diff --git a/WebimMobileSDK/Backend/FAQClient.swift b/WebimMobileSDK/Backend/FAQClient.swift index e5e5959e..5a57ed31 100644 --- a/WebimMobileSDK/Backend/FAQClient.swift +++ b/WebimMobileSDK/Backend/FAQClient.swift @@ -78,16 +78,15 @@ final class FAQClientBuilder { WebimInternalLogger.shared.log(entry: "Completion Handler Executor is nil in FAQClient.\(#function)") fatalError("Completion Handler Executor is nil in FAQClient.\(#function)") } - let faqRequestLoop = FAQRequestLoop(completionHandlerExecutor: handler) guard let baseURL = baseURL else { WebimInternalLogger.shared.log(entry: "Base URL is nil in FAQClient.\(#function)") fatalError("Base URL is nil in FAQClient.\(#function)") } + let faqRequestLoop = FAQRequestLoop(completionHandlerExecutor: handler, baseURL: baseURL) return FAQClient(withFAQRequestLoop: faqRequestLoop, - faqActions: FAQActions(baseURL: baseURL, - faqRequestLoop: faqRequestLoop), + faqActions: FAQActions(faqRequestLoop: faqRequestLoop), application: application, departmentKey: departmentKey, language: language) diff --git a/WebimMobileSDK/Backend/FAQRequestLoop.swift b/WebimMobileSDK/Backend/FAQRequestLoop.swift index 2fe1cb66..e1e4ed4f 100644 --- a/WebimMobileSDK/Backend/FAQRequestLoop.swift +++ b/WebimMobileSDK/Backend/FAQRequestLoop.swift @@ -41,9 +41,10 @@ class FAQRequestLoop: AbstractRequestLoop { // MARK: - Initialization - init(completionHandlerExecutor: ExecIfNotDestroyedFAQHandlerExecutor) { + init(completionHandlerExecutor: ExecIfNotDestroyedFAQHandlerExecutor, + baseURL: String) { self.completionFAQHandlerExecutor = completionHandlerExecutor - super.init(completionHandlerExecutor: nil, internalErrorListener: nil, requestHeader: nil) + super.init(completionHandlerExecutor: nil, internalErrorListener: nil, requestHeader: nil, baseURL: baseURL) } // MARK: - Methods diff --git a/WebimMobileSDK/Backend/FileUrlCreator.swift b/WebimMobileSDK/Backend/FileUrlCreator.swift index 9497cc37..5bdf597e 100644 --- a/WebimMobileSDK/Backend/FileUrlCreator.swift +++ b/WebimMobileSDK/Backend/FileUrlCreator.swift @@ -69,7 +69,8 @@ final class FileUrlCreator { WebimInternalLogger.shared.log(entry: "Adding Percent Encoding With Allowed Characters failure in MessageImpl.\(#function)", verbosityLevel: .warning) } - let fileURLString = serverURL + ServerPathSuffix.downloadFile.rawValue + "/" + let baseURL = webimClient?.getDeltaRequestLoop().baseURL ?? serverURL + let fileURLString = baseURL + ServerPathSuffix.downloadFile.rawValue + "/" + guid + "/" + formatedFilename + "?" + "page-id" + "=" + pageID + "&" diff --git a/WebimMobileSDK/Backend/Items/AccountConfigItem.swift b/WebimMobileSDK/Backend/Items/AccountConfigItem.swift index f4be01cc..45fbae18 100644 --- a/WebimMobileSDK/Backend/Items/AccountConfigItem.swift +++ b/WebimMobileSDK/Backend/Items/AccountConfigItem.swift @@ -42,6 +42,8 @@ final class AccountConfigItem { case visitorMessageEditing = "visitor_message_editing" case maxVisitorUploadFileSize = "max_visitor_upload_file_size" case allowedUploadFileTypes = "allowed_upload_file_types" + case rateOperator = "rate_operator" + case showRateOperator = "show_visitor_rate_operator_button" case disablingMessageInputField = "disabling_message_input_field" } @@ -51,6 +53,8 @@ final class AccountConfigItem { private var visitorMessageEditing: Bool? private var maxVisitorUploadFileSize: Int? private var allowedUploadFileTypes: [String]? + private var rateOperator: Bool? + private var showRateOperator: Bool? private var disablingMessageInputField: Bool? // MARK: - Initialization @@ -65,6 +69,14 @@ final class AccountConfigItem { self.visitorMessageEditing = visitorMessageEditing } + if let rateOperator = jsonDictionary[JSONField.rateOperator.rawValue] as? Bool { + self.rateOperator = rateOperator + } + + if let showRateOperator = jsonDictionary[JSONField.showRateOperator.rawValue] as? Bool { + self.showRateOperator = showRateOperator + } + if let maxVisitorUploadFileSize = jsonDictionary[JSONField.maxVisitorUploadFileSize.rawValue] as? Int { if maxVisitorUploadFileSize != 0 { self.maxVisitorUploadFileSize = maxVisitorUploadFileSize @@ -103,6 +115,14 @@ final class AccountConfigItem { return allowedUploadFileTypes ?? [] } + func getAllowedRateOperator() -> Bool { + return rateOperator ?? true + } + + func getShowRateOperator() -> Bool { + return showRateOperator ?? true + } + func getDisablingMessageInputField() -> Bool { return disablingMessageInputField ?? false } diff --git a/WebimMobileSDK/Backend/Items/HistoryRevisionItem.swift b/WebimMobileSDK/Backend/Items/HistoryRevisionItem.swift index c4c98977..ba814f4b 100644 --- a/WebimMobileSDK/Backend/Items/HistoryRevisionItem.swift +++ b/WebimMobileSDK/Backend/Items/HistoryRevisionItem.swift @@ -41,14 +41,17 @@ final class HistoryRevisionItem { } // MARK: - Properties - private var revision: Int64 + private var revision: String // MARK: - Initialization init(jsonDictionary: [String: Any?]) { - if let revision = jsonDictionary[JSONField.revision.rawValue] as? Int64 { + let revision = jsonDictionary[JSONField.revision.rawValue] + if let revision = revision as? String { self.revision = revision + } else if let revision = revision as? Int { + self.revision = String(revision) } else { - self.revision = 0 + self.revision = "0" } } diff --git a/WebimMobileSDK/Backend/Items/Responses/DeltaResponse.swift b/WebimMobileSDK/Backend/Items/Responses/DeltaResponse.swift index ecccee94..3fb00822 100644 --- a/WebimMobileSDK/Backend/Items/Responses/DeltaResponse.swift +++ b/WebimMobileSDK/Backend/Items/Responses/DeltaResponse.swift @@ -46,12 +46,15 @@ final class DeltaResponse { // MARK: - Properties private lazy var deltaList = [DeltaItem]() private var fullUpdate: FullUpdate? - private var revision: Int64? + private var revision: String? // MARK: - Initialization init(jsonDictionary: [String: Any?]) { - if let revision = jsonDictionary[JSONField.revision.rawValue] as? Int64 { + let revision = jsonDictionary[JSONField.revision.rawValue] + if let revision = revision as? String { self.revision = revision + } else if let revision = revision as? Int { + self.revision = String(revision) } if let fullUpdateValue = jsonDictionary[JSONField.fullUpdate.rawValue] as? [String: Any?] { @@ -71,7 +74,7 @@ final class DeltaResponse { // MARK: - Methods - func getRevision() -> Int64? { + func getRevision() -> String? { return revision } diff --git a/WebimMobileSDK/Backend/MessageHolder.swift b/WebimMobileSDK/Backend/MessageHolder.swift index 00ec26e9..3f6931f2 100644 --- a/WebimMobileSDK/Backend/MessageHolder.swift +++ b/WebimMobileSDK/Backend/MessageHolder.swift @@ -37,6 +37,7 @@ final class MessageHolder { // MARK: - Properties private let accessChecker: AccessChecker private let historyStorage: HistoryStorage + private weak var messageStream: MessageStream? private let remoteHistoryProvider: RemoteHistoryProvider private lazy var currentChatMessages = [MessageImpl]() private var lastChatMessageIndex = 0 @@ -75,8 +76,19 @@ final class MessageHolder { return messagesToSend } + func set(messageStream: MessageStream) { + self.messageStream = messageStream + } + + func getMessageStram() -> MessageStream? { + return messageStream + } + func removeFromMessagesToSendAt(index: Int) { weak var removedMessage = messagesToSend.remove(at: index) + if let message = removedMessage { + cancelResendWith(message: message) + } WebimInternalLogger.shared.log( entry: "Message \(removedMessage?.getText() ?? "") removed from messages to send.\nMessages to send count - \(messagesToSend.count) in MessageHolder", verbosityLevel: .verbose, @@ -347,6 +359,17 @@ final class MessageHolder { logType: .messageHistory) messageTracker?.messageListener?.added(message: message, after: nil) + var messages = [MessageImpl]() + messages.append(message) + receiveHistoryUpdateWith(messages: messages, deleted: Set()) { } + } + + func resending(message: MessageToSend) { + messagesToSend.append(message) + WebimInternalLogger.shared.log( + entry: "Message text to send - \(message.getText()).\nMessages to send count - \(messagesToSend.count)", + verbosityLevel: .verbose, + logType: .messageHistory) } func sendingCancelledWith(messageID: String) { @@ -360,12 +383,23 @@ final class MessageHolder { messagesToSend.remove(at: index) messageTracker?.messageListener?.removed(message: message) + cancelResendWith(message: message) return } } } + func cancelResendWith(message: Message, deleteFromChat: Bool = false) { + var messages = Set() + messages.insert(message.getID()) + receiveHistoryUpdateWith(messages: [MessageImpl](), deleted: messages) { + if deleteFromChat == true { + self.messageTracker?.messageListener?.removed(message: message) + } + } + } + func changing(messageID: String, message: String?) -> String? { if messageTracker == nil { WebimInternalLogger.shared.log( @@ -422,6 +456,9 @@ final class MessageHolder { entry: "Changing success.\nMessage \(messageImpl.getText()) changed to \(newMessage.getText()) in MessageHolder - \(#function)", verbosityLevel: .verbose, logType: .messageHistory) + var messages = [MessageImpl]() + messages.append(newMessage) + receiveHistoryUpdateWith(messages: messages, deleted: Set()) { } return messageImpl.getText() } diff --git a/WebimMobileSDK/Backend/SQLiteHistoryStorage.swift b/WebimMobileSDK/Backend/SQLiteHistoryStorage.swift index 695984cf..ab1dfa47 100644 --- a/WebimMobileSDK/Backend/SQLiteHistoryStorage.swift +++ b/WebimMobileSDK/Backend/SQLiteHistoryStorage.swift @@ -60,10 +60,12 @@ final class SQLiteHistoryStorage: HistoryStorage { case canVisitorReact = "can_visitor_react" case canVisitorChangeReaction = "can_visitor_change_reaction" case reaction = "reaction" + case status = "status" } // MARK: SQLite.swift abstractions + private static let SQLITE_CONSTRAINT: Int = 19 private static let history = Table(TableName.history.rawValue) // In DB columns order. @@ -78,10 +80,10 @@ final class SQLiteHistoryStorage: HistoryStorage { private static let data = SQLite.Expression(ColumnName.data.rawValue) private static let canBeReplied = SQLite.Expression(ColumnName.canBeReplied.rawValue) private static let quote = SQLite.Expression(ColumnName.quote.rawValue) - private static let SQLITE_CONSTRAINT: Int = 19 private static let canVisitorReact = SQLite.Expression(ColumnName.canVisitorReact.rawValue) private static let canVisitorChangeReaction = SQLite.Expression(ColumnName.canVisitorChangeReaction.rawValue) private static let reaction = SQLite.Expression(ColumnName.reaction.rawValue) + private static let status = SQLite.Expression(ColumnName.status.rawValue) // MARK: - Properties @@ -117,7 +119,7 @@ final class SQLiteHistoryStorage: HistoryStorage { // MARK: HistoryStorage protocol methods static func getMajorVersion() -> Int { - return 11 + return 12 } func getMajorVersion() -> Int { @@ -312,7 +314,8 @@ final class SQLiteHistoryStorage: HistoryStorage { + "\(SQLiteHistoryStorage.ColumnName.quote.rawValue), " + "\(SQLiteHistoryStorage.ColumnName.canVisitorReact.rawValue), " + "\(SQLiteHistoryStorage.ColumnName.canVisitorChangeReaction.rawValue), " - + "\(SQLiteHistoryStorage.ColumnName.reaction.rawValue)) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + + "\(SQLiteHistoryStorage.ColumnName.reaction.rawValue), " + + "\(SQLiteHistoryStorage.ColumnName.status.rawValue)) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" let statement = try db.prepare(query) try statement.run(messageHistoryID.getDBid(), message.getID(), @@ -327,7 +330,8 @@ final class SQLiteHistoryStorage: HistoryStorage { SQLiteHistoryStorage.convertToBlob(quote: message.getQuote()), message.canVisitorReact(), message.canVisitorChangeReaction(), - message.getVisitorReaction()) + message.getVisitorReaction(), + message.getSendStatus() == .sent ? "sent" : "sending") // Raw SQLite statement constructed because there's no way to implement INSERT OR FAIL query with SQLite.swift methods. Appropriate INSERT query can look like this: /*try db.run(SQLiteHistoryStorage .history @@ -375,8 +379,16 @@ final class SQLiteHistoryStorage: HistoryStorage { var newFirstKnownTimestamp = Int64.max for message in messages { - guard let messageHistoryID = message.getHistoryID() else { - continue + let messageHistoryID: HistoryID + if message.getSendStatus() == .sending { + messageHistoryID = HistoryID(dbID: message.getID(), + timeInMicrosecond: message.getTimeInMicrosecond()) + } else { + if let historyID = message.getHistoryID() { + messageHistoryID = historyID + } else { + continue + } } if ((self.firstKnownTimestamp != -1) @@ -562,7 +574,7 @@ final class SQLiteHistoryStorage: HistoryStorage { let dbPath = "\(libraryPath)/\(name)" do { let db = try Connection(dbPath) - db.userVersion = 5 + db.userVersion = Int32(getMajorVersion()) db.busyTimeout = 1.0 db.busyHandler() { tries in if tries >= 3 { @@ -618,6 +630,7 @@ final class SQLiteHistoryStorage: HistoryStorage { t.column(SQLiteHistoryStorage.canVisitorReact) t.column(SQLiteHistoryStorage.canVisitorChangeReaction) t.column(SQLiteHistoryStorage.reaction) + t.column(SQLiteHistoryStorage.status) }) db.trace { WebimInternalLogger.shared.log( @@ -715,6 +728,22 @@ final class SQLiteHistoryStorage: HistoryStorage { text = "" } + var quote: Quote? + if let quoteValue = row[SQLiteHistoryStorage.quote], + let data = NSKeyedUnarchiver.unarchiveObject(with: Data.fromDatatypeValue(quoteValue)) as? [String : Any?] { + quote = QuoteImpl.getQuote(quoteItem: QuoteItem(jsonDictionary: data), messageAttachment: nil, fileUrlCreator: fileUrlCreator) + } + + if row[SQLiteHistoryStorage.status] == "sending" { + return MessageToSend(serverURLString: serverURLString, + clientSideID: clientSideID ?? "", + senderName: row[SQLiteHistoryStorage.senderName], + type: type, + text: text, + timeInMicrosecond: row[SQLiteHistoryStorage.timestamp], + quote: quote) + } + var rawData: [String: Any?]? var group: Group? if let dataValue = row[SQLiteHistoryStorage.data] { @@ -782,12 +811,6 @@ final class SQLiteHistoryStorage: HistoryStorage { } let canBeReplied = row[SQLiteHistoryStorage.canBeReplied] ?? false - - var quote: Quote? - if let quoteValue = row[SQLiteHistoryStorage.quote], - let data = NSKeyedUnarchiver.unarchiveObject(with: Data.fromDatatypeValue(quoteValue)) as? [String : Any?] { - quote = QuoteImpl.getQuote(quoteItem: QuoteItem(jsonDictionary: data), messageAttachment: nil, fileUrlCreator: fileUrlCreator) - } let canVisitorReact = row[SQLiteHistoryStorage.canVisitorReact] ?? false let canVisitorChangeReact = row[SQLiteHistoryStorage.canVisitorChangeReaction] ?? false let reaction = row[SQLiteHistoryStorage.reaction] ?? nil @@ -821,11 +844,22 @@ final class SQLiteHistoryStorage: HistoryStorage { } private func insert(message: MessageImpl) throws { - guard let db = db, - let messageHistoryID = message.getHistoryID() else { + guard let db = db else { return } + let messageHistoryID: HistoryID + if message.getSendStatus() == .sending { + messageHistoryID = HistoryID(dbID: message.getID(), + timeInMicrosecond: message.getTimeInMicrosecond()) + } else { + if let historyID = message.getHistoryID() { + messageHistoryID = historyID + } else { + return + } + } + /* INSERT INTO history (id, client_side_id, @@ -863,7 +897,8 @@ final class SQLiteHistoryStorage: HistoryStorage { SQLiteHistoryStorage.quote <- SQLiteHistoryStorage.convertToBlob(quote: message.getQuote()), SQLiteHistoryStorage.canVisitorReact <- message.canVisitorReact(), SQLiteHistoryStorage.canVisitorChangeReaction <- message.canVisitorChangeReaction(), - SQLiteHistoryStorage.reaction <- message.getVisitorReaction())) + SQLiteHistoryStorage.reaction <- message.getVisitorReaction(), + SQLiteHistoryStorage.status <- message.getSendStatus() == .sent ? "sent" : "sending")) db.trace { WebimInternalLogger.shared.log( diff --git a/WebimMobileSDK/Backend/Utilities/Extensions/Dictionary.swift b/WebimMobileSDK/Backend/Utilities/Extensions/Dictionary.swift index ef78a095..72ad1cbf 100644 --- a/WebimMobileSDK/Backend/Utilities/Extensions/Dictionary.swift +++ b/WebimMobileSDK/Backend/Utilities/Extensions/Dictionary.swift @@ -62,10 +62,10 @@ extension Dictionary { WebimInternalLogger.shared.log(entry: "Key has incorrect type or nil in extension Dictionary.\(#function)") return String() } - + return getPercentEscapedString(forKey: key, value: value, isPartOfJSON: true) } - + return "{" + parameterArray.joined(separator: ",") + "}" } @@ -88,15 +88,15 @@ extension Dictionary { WebimInternalLogger.shared.log(entry: "Value has incorrect type in extension Dictionary.\(#function)") return String() } + if isPartOfJSON { + stringValue = stringValue.replacingOccurrences(of: "\"", with: "\\\"") + return "\"\(key)\"=\"\(stringValue)\"" + } guard let percentEscapedKey = key.addingPercentEncodingForURLQueryValue() else { WebimInternalLogger.shared.log(entry: "Adding Percent Encoding For URL Query Value to Key failure in Extension Dictionary.\(#function)") return "\(key)=\(stringValue)" } - if isPartOfJSON { - stringValue = stringValue.replacingOccurrences(of: "\"", with: "\\\"") - return "\"\(key)\"=\"\(stringValue)\"" - } guard let percentEscapedValue = stringValue.addingPercentEncodingForURLQueryValue() else { WebimInternalLogger.shared.log(entry: "Adding Percent Encoding For URL Query Value to Value failure in Extension Dictionary.\(#function)") diff --git a/WebimMobileSDK/Backend/Utilities/InternalUtils.swift b/WebimMobileSDK/Backend/Utilities/InternalUtils.swift index 1156eaa2..0d8e226e 100644 --- a/WebimMobileSDK/Backend/Utilities/InternalUtils.swift +++ b/WebimMobileSDK/Backend/Utilities/InternalUtils.swift @@ -34,9 +34,19 @@ import Foundation 2017 Webim */ final class InternalUtils { + static let domains = ["webim.ru", "webim2.ru", "webim.chat"] // MARK: - Methods + static func changeDomainFor(url: String) -> String { + for (index, domain) in domains.enumerated() { + if url.contains(domain) { + return url.replacingOccurrences(of: domains[index], with: domains[(index + 1) % domains.count]) + } + } + return url + } + static func createServerURLStringBy(accountName: String) -> String { var serverURLstring = accountName @@ -48,7 +58,7 @@ final class InternalUtils { return serverURLstring } - return "https://\(serverURLstring).webim.ru" + return "https://\(serverURLstring).\(domains[0])" } static func getCurrentTimeInMicrosecond() -> Int64 { diff --git a/WebimMobileSDK/Backend/WebimActionsImpl.swift b/WebimMobileSDK/Backend/WebimActionsImpl.swift index 27a4b7bf..fd539193 100644 --- a/WebimMobileSDK/Backend/WebimActionsImpl.swift +++ b/WebimMobileSDK/Backend/WebimActionsImpl.swift @@ -63,15 +63,13 @@ final class WebimActionsImpl { } // MARK: - Properties - private let baseURL: String + //private let baseURL: String let actionRequestLoop: ActionRequestLoop private var sendingFiles = [String: SendingFile]() // MARK: - Initialization - init(baseURL: String, - actionRequestLoop: ActionRequestLoop + init(actionRequestLoop: ActionRequestLoop ) { - self.baseURL = baseURL self.actionRequestLoop = actionRequestLoop } @@ -108,7 +106,7 @@ extension WebimActionsImpl: WebimActions { dataToPost[Parameter.data.rawValue] = dataJSONString } - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -134,7 +132,7 @@ extension WebimActionsImpl: WebimActions { let dataToPost = [Parameter.chatMode.rawValue: ChatMode.online.rawValue, Parameter.clientSideID.rawValue: clientSideID] as [String: Any] - let urlString = baseURL + ServerPathSuffix.uploadFile.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.uploadFile.rawValue let boundaryString = NSUUID().uuidString @@ -204,7 +202,7 @@ extension WebimActionsImpl: WebimActions { dataToPost[Parameter.fileProgress.rawValue] = progress.description } - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue let sendingFile = SendingFile( fileName: filename, @@ -235,7 +233,7 @@ extension WebimActionsImpl: WebimActions { dataToPost[Parameter.hintQuestion.rawValue] = isHintQuestion } - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -254,7 +252,7 @@ extension WebimActionsImpl: WebimActions { Parameter.message.rawValue: message, Parameter.quote.rawValue: getQuotedMessage(repliedMessageId: quotedMessageID)] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -272,7 +270,7 @@ extension WebimActionsImpl: WebimActions { let dataToPost = [Parameter.actionn.rawValue: Action.deleteMessage.rawValue, Parameter.clientSideID.rawValue: clientSideID] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -286,7 +284,7 @@ extension WebimActionsImpl: WebimActions { completionHandler: DeleteUploadedFileCompletionHandler?) { let dataToPost = [Parameter.guid.rawValue: fileGuid] as [String: Any] - let urlString = baseURL + ServerPathSuffix.fileDelete.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.fileDelete.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -312,7 +310,7 @@ extension WebimActionsImpl: WebimActions { dataToPost[Parameter.customFields.rawValue] = custom_fields } - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -323,7 +321,7 @@ extension WebimActionsImpl: WebimActions { func closeChat() { let dataToPost = [Parameter.actionn.rawValue: Action.closeChat.rawValue] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -341,7 +339,7 @@ extension WebimActionsImpl: WebimActions { dataToPost[Parameter.draft.rawValue] = draft } - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -353,7 +351,7 @@ extension WebimActionsImpl: WebimActions { let dataToPost = [Parameter.actionn.rawValue: Action.setPrechat.rawValue, Parameter.prechat.rawValue: prechatFields] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -368,7 +366,7 @@ extension WebimActionsImpl: WebimActions { dataToPost[Parameter.since.rawValue] = since } - let urlString = baseURL + ServerPathSuffix.getHistory.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.getHistory.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -380,7 +378,7 @@ extension WebimActionsImpl: WebimActions { completion: @escaping (_ data: Data?) throws -> ()) { let dataToPost = [Parameter.beforeTimestamp.rawValue: beforeMessageTimestamp] as [String: Any] - let urlString = baseURL + ServerPathSuffix.getHistory.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.getHistory.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -401,7 +399,7 @@ extension WebimActionsImpl: WebimActions { dataToPost[Parameter.visitorNote.rawValue] = visitorNote } - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -414,7 +412,7 @@ extension WebimActionsImpl: WebimActions { let dataToPost = [Parameter.actionn.rawValue: Action.respondSentryCall.rawValue, Parameter.clientSideID.rawValue: id] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -427,7 +425,7 @@ extension WebimActionsImpl: WebimActions { let authToken = self.actionRequestLoop.authorizationData?.getAuthorizationToken() ?? "" let parameterDictionary: [String: String] = [Parameter.pageID.rawValue: pageId, Parameter.query.rawValue: query, Parameter.authorizationToken.rawValue:authToken] - let urlString = baseURL + ServerPathSuffix.search.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.search.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: parameterDictionary, baseURLString: urlString, @@ -438,7 +436,7 @@ extension WebimActionsImpl: WebimActions { let dataToPost = [Parameter.actionn.rawValue: Action.setDeviceToken.rawValue, Parameter.deviceToken.rawValue: deviceToken] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -449,7 +447,7 @@ extension WebimActionsImpl: WebimActions { func setChatRead() { let dataToPost = [Parameter.actionn.rawValue: Action.chatRead.rawValue] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -461,7 +459,7 @@ extension WebimActionsImpl: WebimActions { let dataToPost = [Parameter.actionn.rawValue: Action.widgetUpdate.rawValue, Parameter.data.rawValue: data] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -476,7 +474,7 @@ extension WebimActionsImpl: WebimActions { Parameter.buttonId.rawValue: buttonId, Parameter.requestMessageId.rawValue: messageId] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -491,7 +489,7 @@ extension WebimActionsImpl: WebimActions { let dataToPost = [Parameter.actionn.rawValue: Action.sendChatHistory.rawValue, Parameter.email.rawValue: emailAddress] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -510,7 +508,7 @@ extension WebimActionsImpl: WebimActions { Parameter.clientSideID.rawValue: clientSideId ] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest( httpMethod: .post, @@ -537,7 +535,7 @@ extension WebimActionsImpl: WebimActions { Parameter.clientSideID.rawValue: clientSideId ] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest( httpMethod: .post, @@ -560,7 +558,7 @@ extension WebimActionsImpl: WebimActions { Parameter.surveyQuestionID.rawValue: questionID, Parameter.surveyAnswer.rawValue: surveyAnswer] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -574,7 +572,7 @@ extension WebimActionsImpl: WebimActions { let dataToPost = [Parameter.actionn.rawValue: Action.surveyAnswer.rawValue, Parameter.surveyID.rawValue: surveyID] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -587,7 +585,7 @@ extension WebimActionsImpl: WebimActions { completion: @escaping (_ data: Data?) throws -> ()) { let dataToPost = [Parameter.location.rawValue: location] as [String: Any] - let urlString = baseURL + ServerPathSuffix.getOnlineStatus.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.getOnlineStatus.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -599,7 +597,7 @@ extension WebimActionsImpl: WebimActions { func clearHistory() { let dataToPost = [Parameter.actionn.rawValue: Action.clearHistory.rawValue] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, @@ -610,7 +608,7 @@ extension WebimActionsImpl: WebimActions { func getServerSettings(forLocation location: String, completion: @escaping (Data?) throws -> ()) { let dataToPost = [String: Any]() - let urlString = baseURL + ServerPathSuffix.getConfig.rawValue + "/\(location)" + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.getConfig.rawValue + "/\(location)" actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: dataToPost, @@ -632,7 +630,7 @@ extension WebimActionsImpl: WebimActions { func getServerSideSettings(completionHandler: ServerSideSettingsCompletionHandler?) { - let urlString = baseURL + ServerPathSuffix.getServerSideSettings.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.getServerSideSettings.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .get, primaryData: [:], @@ -645,7 +643,7 @@ extension WebimActionsImpl: WebimActions { Parameter.latitude.rawValue: latitude, Parameter.longitude.rawValue: longitude] as [String: Any] - let urlString = baseURL + ServerPathSuffix.doAction.rawValue + let urlString = actionRequestLoop.baseURL + ServerPathSuffix.doAction.rawValue actionRequestLoop.enqueue(request: WebimRequest(httpMethod: .post, primaryData: dataToPost, diff --git a/WebimMobileSDK/Backend/WebimClient.swift b/WebimMobileSDK/Backend/WebimClient.swift index ed09b27f..65b29f7d 100644 --- a/WebimMobileSDK/Backend/WebimClient.swift +++ b/WebimMobileSDK/Backend/WebimClient.swift @@ -183,10 +183,16 @@ final class WebimClientBuilder { fatalError("Building Webim client failure because Internal Error Listener is nil in WebimClient.\(#function)") } + guard let baseURL = baseURL else { + WebimInternalLogger.shared.log(entry: "Building Webim client failure because Base URL is nil in WebimClient.\(#function)") + fatalError("Building Webim client failure because Base URL is nil in WebimClient.\(#function)") + } + let actionRequestLoop = ActionRequestLoop(completionHandlerExecutor: completionHandlerExecutor, internalErrorListener: internalErrorListener, notFatalErrorHandler: notFatalErrorHandler, - requestHeader: requestHeader) + requestHeader: requestHeader, + baseURL: baseURL) actionRequestLoop.set(authorizationData: authorizationData) @@ -194,10 +200,6 @@ final class WebimClientBuilder { WebimInternalLogger.shared.log(entry: "Building Webim client failure because Delta Callback is nil in WebimClient.\(#function)") fatalError("Building Webim client failure because Delta Callback is nil in WebimClient.\(#function)") } - guard let baseURL = baseURL else { - WebimInternalLogger.shared.log(entry: "Building Webim client failure because Base URL is nil in WebimClient.\(#function)") - fatalError("Building Webim client failure because Base URL is nil in WebimClient.\(#function)") - } guard let title = title else { WebimInternalLogger.shared.log(entry: "Building Webim client failure because Title is nil in WebimClient.\(#function)") fatalError("Building Webim client failure because Title is nil in WebimClient.\(#function)") @@ -235,8 +237,7 @@ final class WebimClientBuilder { return WebimClient(withActionRequestLoop: actionRequestLoop, deltaRequestLoop: deltaRequestLoop, - webimActions: WebimActionsImpl(baseURL: baseURL, - actionRequestLoop: actionRequestLoop)) + webimActions: WebimActionsImpl(actionRequestLoop: actionRequestLoop)) } } diff --git a/WebimMobileSDK/Implementation/MessageStreamImpl.swift b/WebimMobileSDK/Implementation/MessageStreamImpl.swift index 89fb16b5..ad4c8b06 100644 --- a/WebimMobileSDK/Implementation/MessageStreamImpl.swift +++ b/WebimMobileSDK/Implementation/MessageStreamImpl.swift @@ -676,6 +676,53 @@ extension MessageStreamImpl: MessageStream { return messageID } + func resend(message: Message, + completionHandler: ResendMessageCompletionHandler?) throws { + try accessChecker.checkAccess() + + if message.getSendStatus() != .sending { + return + } + + try startChat() + WebimInternalLogger.shared.log(entry: "Request resend message - \(message.getText()) in MessageStream - \(#function)", verbosityLevel: .verbose, logType: .networkRequest) + switch message.getType() { + case .stickerVisitor: + if let sticker = message.getSticker() { + messageHolder.resending(message: sendingMessageFactory.createStickerMessageToSendWith(id: message.getID(), stickerId: sticker.getStickerId())) + webimActions.sendSticker(stickerId: sticker.getStickerId(), clientSideId: message.getID()) + } + case .visitorMessage: + messageHolder.resending(message: sendingMessageFactory.createTextMessageToSendWith(id: message.getID(), + text: message.getText())) + if let quote = message.getQuote() { + webimActions.replay(message: message.getText(), + clientSideID: message.getID(), + quotedMessageID: quote.getMessageID() ?? "") + } else { + webimActions.send(message: message.getText(), + clientSideID: message.getID(), + dataJSONString: nil, + isHintQuestion: false, + dataMessageCompletionHandler: nil, + editMessageCompletionHandler: nil, + sendMessageCompletionHandler: nil) + } + default: + return + } + } + + func cancelResend(message: Message) throws { + try accessChecker.checkAccess() + + if message.getSendStatus() != .sending { + return + } + + messageHolder.cancelResendWith(message: message, deleteFromChat: true) + } + func send(uploadedFiles: [UploadedFile], completionHandler: SendFilesCompletionHandler?) throws -> String { try accessChecker.checkAccess() diff --git a/WebimMobileSDK/Implementation/MessageTrackerImpl.swift b/WebimMobileSDK/Implementation/MessageTrackerImpl.swift index 7550518e..f26f2908 100644 --- a/WebimMobileSDK/Implementation/MessageTrackerImpl.swift +++ b/WebimMobileSDK/Implementation/MessageTrackerImpl.swift @@ -537,6 +537,7 @@ extension MessageTrackerImpl: MessageTracker { let wrappedCompletion: ([Message]) -> () = { [weak self] messages in (self?.destroyed != false) ? completion(messages) : completion([Message]()) + self?.resend(messages: messages) } allMessageSourcesEnded = false @@ -557,6 +558,7 @@ extension MessageTrackerImpl: MessageTracker { wrappedCompletion(result) firstHistoryUpdateReceived = true messagesLoading = false + resend(messages: result) } } @@ -569,6 +571,7 @@ extension MessageTrackerImpl: MessageTracker { let wrappedCompletion: ([Message]) -> () = { [weak self] messages in (self?.destroyed != false) ? completion(messages) : completion([Message]()) + self?.resend(messages: messages) } messagesLoading = true @@ -641,6 +644,17 @@ extension MessageTrackerImpl: MessageTracker { } } + private func resend(messages: [Message]) { + for message in messages { + if message.getSendStatus() == .sending { + do { + try messageHolder.getMessageStram()?.resend(message: message, completionHandler: nil) + } catch { + } + } + } + } + private func canLoading(byLimitOfMessages limitOfMessages: Int = 1, completion: @escaping ([Message]) -> () = { _ in }) -> Bool { guard destroyed != true else { WebimInternalLogger.shared.log(entry: "MessageTracker object is destroyed. Unable to perform request to get new messages.") diff --git a/WebimMobileSDK/Implementation/WebimServerSideSettings.swift b/WebimMobileSDK/Implementation/WebimServerSideSettings.swift index b76b32e1..d7daa659 100644 --- a/WebimMobileSDK/Implementation/WebimServerSideSettings.swift +++ b/WebimMobileSDK/Implementation/WebimServerSideSettings.swift @@ -56,6 +56,7 @@ public struct AccountConfig: Codable { // let fileURLExpiringTimeout: Int // let checkVisitorAuth, operatorStatusTimer, , // let offlineChatProcessing, openChatInNewTabForMobile: Bool + enum CodingKeys: String, CodingKey { case webAndMobileQuoting = "web_and_mobile_quoting" case visitorMessageEditing = "visitor_message_editing" diff --git a/WebimMobileSDK/Implementation/WebimSessionImpl.swift b/WebimMobileSDK/Implementation/WebimSessionImpl.swift index 80a8d750..4840e341 100644 --- a/WebimMobileSDK/Implementation/WebimSessionImpl.swift +++ b/WebimMobileSDK/Implementation/WebimSessionImpl.swift @@ -328,6 +328,7 @@ final class WebimSessionImpl { messageComposingHandler: MessageComposingHandler(webimActions: webimActions, queue: webimSdkQueue), locationSettingsHolder: LocationSettingsHolder(userDefaultsKey: userDefaultsKey)) + messageHolder.set(messageStream: messageStream) let historyPoller = HistoryPoller(withSessionDestroyer: sessionDestroyer, queue: webimSdkQueue, @@ -1103,7 +1104,7 @@ final private class DestroyOnFatalErrorListener: InternalErrorListener { // MARK: - Properties private let internalErrorListener: InternalErrorListener? - private weak var notFatalErrorHandler: NotFatalErrorHandler? + private let notFatalErrorHandler: NotFatalErrorHandler? private var sessionDestroyer: SessionDestroyer // MARK: - Initialization diff --git a/WebimMobileSDK/Message.swift b/WebimMobileSDK/Message.swift index 62846a17..03a4bf54 100644 --- a/WebimMobileSDK/Message.swift +++ b/WebimMobileSDK/Message.swift @@ -1398,7 +1398,7 @@ public protocol Group { - author: Nikita Kaberov - copyright: - 2024 Webim + 2014 Webim */ func getID() -> String @@ -1407,7 +1407,7 @@ public protocol Group { - author: Nikita Kaberov - copyright: - 2024 Webim + 2014 Webim */ func getMessageCount() -> Int @@ -1416,7 +1416,7 @@ public protocol Group { - author: Nikita Kaberov - copyright: - 2024 Webim + 2014 Webim */ func getMessageNumber() -> Int } diff --git a/WebimMobileSDK/MessageStream.swift b/WebimMobileSDK/MessageStream.swift index b9329987..cd2798d0 100644 --- a/WebimMobileSDK/MessageStream.swift +++ b/WebimMobileSDK/MessageStream.swift @@ -519,6 +519,43 @@ public protocol MessageStream: class { func send(message: String, isHintQuestion: Bool?) throws -> String + /** + Resends a message, if it wasn't sent. + - parameter message: + Message with sending status. + - parameter completionHandler: + Completion handler that executes when operation is done. + - returns: + ID of the message. + - throws: + `AccessError.invalidThread` if the method was called not from the thread the WebimSession was created in. + `AccessError.invalidSession` if WebimSession was destroyed. + - author: + Nikita Kaberov + - copyright: + 2024 Webim + */ + func resend(message: Message, + completionHandler: ResendMessageCompletionHandler?) throws + + /** + Delete a message with sending status, if message wasn't sent. + - parameter message: + Message with sending status. + - parameter completionHandler: + Completion handler that executes when operation is done. + - returns: + ID of the message. + - throws: + `AccessError.invalidThread` if the method was called not from the thread the WebimSession was created in. + `AccessError.invalidSession` if WebimSession was destroyed. + - author: + Nikita Kaberov + - copyright: + 2024 Webim + */ + func cancelResend(message: Message) throws + /** Sends a message with uploaded files. When calling this method, if there is an active `MessageTracker` object (see `newMessageTracker(messageListener:)` method). `MessageListener.added(message:after:)`) with a message `MessageSendStatus.sending` in the status is also called. @@ -1252,6 +1289,12 @@ public protocol SendMessageCompletionHandler: class { func onSuccess(messageID: String) } +public protocol ResendMessageCompletionHandler: class { + func onSuccess(messageID: String) + + func onFailure() +} + /** - seealso: `MessageStream.send(file:filename:mimeType:completionHandler:)` @@ -2703,13 +2746,6 @@ public enum SendFileError: Error { */ case uploadCanceled - /** - Sent file was detected as malicious - - author: - Anna Frolova - - copyright: - 2024 Webim - */ case maliciousFileDetected }