From 538d7935528369ef15d346881e9f132cefbaeaeb Mon Sep 17 00:00:00 2001 From: "personaclick-courier[bot]" <205635110+personaclick-courier[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 11:51:56 +0000 Subject: [PATCH] feat(release): sync --- .../Classes/Presentation/PopupPresenter.swift | 24 +++++++++++++++++ .../Sdk/Extensions/Popup.extension.swift | 4 +-- .../Classes/Sdk/Model/Popup.struct.swift | 20 +++++++------- .../Sdk/impl/SimplePersonalizationSDK.swift | 6 ++++- .../PersonalizationSDK.protocol.swift | 1 + .../impl/SubscriptionServiceImpl.swift | 7 ++++- .../Service/impl/TrackServiceImpl.swift | 26 +++++++++++++++++++ .../protocol/TrackEventServiceProtocol.swift | 1 + version.properties | 4 +-- 9 files changed, 77 insertions(+), 16 deletions(-) diff --git a/PersonaClick/Classes/Presentation/PopupPresenter.swift b/PersonaClick/Classes/Presentation/PopupPresenter.swift index 643cef6..06f5202 100644 --- a/PersonaClick/Classes/Presentation/PopupPresenter.swift +++ b/PersonaClick/Classes/Presentation/PopupPresenter.swift @@ -15,6 +15,7 @@ public class PopupPresenter { private var currentPopup: NotificationWidget? private var popupQueue: [Popup] = [] private let serialQueue = DispatchQueue(label: "com.personaClick.popup.presenter") + private var popupShownFlags: [Int: Date] = [:] public init(sdk: AnyObject) { self.sdk = sdk @@ -55,6 +56,14 @@ public class PopupPresenter { // MARK: - Private Methods private func showPopupNow(_ popup: Popup) { + // Check if popup was shown in the last 60 seconds + if let shownDate = popupShownFlags[popup.id] { + let timeSinceShown = Date().timeIntervalSince(shownDate) + if timeSinceShown < 60 { + return // Popup was already shown, skip + } + } + guard let presentingVC = getPresentingViewController(for: popup) else { return // No VC available or delegate prevented presentation } @@ -68,6 +77,21 @@ public class PopupPresenter { self?.dismissCurrentPopup() } ) + + // Store popup shown flag in memory for 60 seconds + self.popupShownFlags[popup.id] = Date() + + // Remove flag after 60 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 60) { [weak self] in + self?.popupShownFlags.removeValue(forKey: popup.id) + } + + // Send popup shown event to server + if let sdk = self.sdk as? PersonalizationSDK { + sdk.trackPopupShown(popupId: popup.id) { _ in + // Handle result (log if needed) + } + } } } diff --git a/PersonaClick/Classes/Sdk/Extensions/Popup.extension.swift b/PersonaClick/Classes/Sdk/Extensions/Popup.extension.swift index ee517ff..3d771fa 100644 --- a/PersonaClick/Classes/Sdk/Extensions/Popup.extension.swift +++ b/PersonaClick/Classes/Sdk/Extensions/Popup.extension.swift @@ -1,6 +1,6 @@ import Foundation extension Popup { - init(json: [String: Any]) { + public init(json: [String: Any]) { self.id = json["id"] as? Int ?? 0 self.channels = json["channels"] as? [String] ?? [] self.position = json["position"] as? String ?? "" @@ -17,7 +17,7 @@ extension Popup { } } - func extractTitleAndSubtitle() -> (title: String?, subTitle: String?) { + public func extractTitleAndSubtitle() -> (title: String?, subTitle: String?) { let title = RegexHelper.extract( using: RegexPattern.title, from: html diff --git a/PersonaClick/Classes/Sdk/Model/Popup.struct.swift b/PersonaClick/Classes/Sdk/Model/Popup.struct.swift index 699d804..19a2ab3 100644 --- a/PersonaClick/Classes/Sdk/Model/Popup.struct.swift +++ b/PersonaClick/Classes/Sdk/Model/Popup.struct.swift @@ -1,22 +1,22 @@ import Foundation public struct Popup: Codable { - enum Position: String { + public enum Position: String { case centered = "centered" case bottom = "fixed_bottom" case top = "top" } - let id: Int - let channels: [String] - let position: String - let delay: Int - let html: String - let components: PopupComponents? - let web_push_system: Bool - let popup_actions: String + public let id: Int + public let channels: [String] + public let position: String + public let delay: Int + public let html: String + public let components: PopupComponents? + public let web_push_system: Bool + public let popup_actions: String - func getParsedPopupActions() -> PopupActions? { + public func getParsedPopupActions() -> PopupActions? { guard let data = popup_actions.data(using: .utf8) else { return nil } let decoder = JSONDecoder() do { diff --git a/PersonaClick/Classes/Sdk/impl/SimplePersonalizationSDK.swift b/PersonaClick/Classes/Sdk/impl/SimplePersonalizationSDK.swift index 9f7318a..67c6c21 100644 --- a/PersonaClick/Classes/Sdk/impl/SimplePersonalizationSDK.swift +++ b/PersonaClick/Classes/Sdk/impl/SimplePersonalizationSDK.swift @@ -408,6 +408,10 @@ class SimplePersonalizationSDK: PersonalizationSDK { trackEventService.trackEvent(event: event, category: category, label: label, value: value, completion: completion) } + func trackPopupShown(popupId: Int, completion: @escaping (Result) -> Void) { + trackEventService.trackPopupShown(popupId: popupId, completion: completion) + } + func trackSource(source: RecommendedByCase, code: String) { trackSourceService.trackSource(source: source, code: code) } @@ -1196,7 +1200,7 @@ class SimplePersonalizationSDK: PersonalizationSDK { } do { if data.isEmpty { - if path.contains("clicked") || path.contains("closed") || path.contains("received") { + if path.contains("clicked") || path.contains("closed") || path.contains("received") || path.contains("showed") { completion(.success([:])) return } diff --git a/PersonaClick/Classes/Sdk/protocol/PersonalizationSDK.protocol.swift b/PersonaClick/Classes/Sdk/protocol/PersonalizationSDK.protocol.swift index 1fae1fd..1dbde1a 100644 --- a/PersonaClick/Classes/Sdk/protocol/PersonalizationSDK.protocol.swift +++ b/PersonaClick/Classes/Sdk/protocol/PersonalizationSDK.protocol.swift @@ -35,6 +35,7 @@ public protocol PersonalizationSDK { func track(event: Event, recommendedBy: RecomendedBy?, completion: @escaping (Result) -> Void) func trackSource(source: RecommendedByCase, code: String) func trackEvent(event: String, category: String?, label: String?, value: Int?, completion: @escaping (Result) -> Void) + func trackPopupShown(popupId: Int, completion: @escaping (Result) -> Void) func recommend(blockId: String, currentProductId: String?, currentCategoryId: String?, locations: String?, imageSize: String?,timeOut: Double?, withLocations: Bool, extended: Bool, completion: @escaping (Result) -> Void) func suggest(query: String, locations: String?, excludedMerchants: [String]?, excludedBrands: [String]?, timeOut: Double?, extended: String?, completion: @escaping (Result) -> Void) func getProductsList(brands: String?, merchants: String?, categories: String?, locations: String?, limit: Int?, page: Int?, filters: [String: Any]?, completion: @escaping(Result) -> Void) diff --git a/PersonaClick/Classes/Subscription/Services/impl/SubscriptionServiceImpl.swift b/PersonaClick/Classes/Subscription/Services/impl/SubscriptionServiceImpl.swift index 705be1b..dd02892 100644 --- a/PersonaClick/Classes/Subscription/Services/impl/SubscriptionServiceImpl.swift +++ b/PersonaClick/Classes/Subscription/Services/impl/SubscriptionServiceImpl.swift @@ -65,7 +65,12 @@ class SubscriptionServiceImpl: SubscriptionServiceProtocol { sdk.postRequest(path: path, params: params) { result in switch result { - case .success: + case let .success(successResult): + // Check if response contains popup and show it + if let popupData = successResult["popup"] as? [String: Any], !popupData.isEmpty { + let popup = Popup(json: popupData) + sdk.popupPresenter.presentPopup(popup) + } completion(.success(Void())) case let .failure(error): completion(.failure(error)) diff --git a/PersonaClick/Classes/Tracking/Service/impl/TrackServiceImpl.swift b/PersonaClick/Classes/Tracking/Service/impl/TrackServiceImpl.swift index 3e8b282..35885f8 100644 --- a/PersonaClick/Classes/Tracking/Service/impl/TrackServiceImpl.swift +++ b/PersonaClick/Classes/Tracking/Service/impl/TrackServiceImpl.swift @@ -15,6 +15,7 @@ class TrackEventServiceImpl: TrackEventServiceProtocol { private struct Constants { static let trackStoriesPath = "track/stories" static let trackCustomEventPath = "push/custom" + static let popupShownPath = "popup/showed" static let id = "id" static let amount = "amount" @@ -283,6 +284,31 @@ class TrackEventServiceImpl: TrackEventServiceProtocol { } } + func trackPopupShown(popupId: Int, completion: @escaping (Result) -> Void) { + guard let sdk = sdk else { + completion(.failure(.custom(error: "trackPopupShown: SDK is not initialized"))) + return + } + + sessionQueue.addOperation { + let params: [String: Any] = [ + Constants.shopId: sdk.shopId, + Constants.did: sdk.deviceId, + Constants.sid: sdk.userSeance, + "popup": popupId + ] + + sdk.postRequest(path: Constants.popupShownPath, params: params) { result in + switch result { + case .success: + completion(.success(Void())) + case .failure(let error): + completion(.failure(error)) + } + } + } + } + private func showPopup(jsonResult: [String: Any]) { guard let popupData = jsonResult["popup"] as? [String: Any], !popupData.isEmpty else { return diff --git a/PersonaClick/Classes/Tracking/Service/protocol/TrackEventServiceProtocol.swift b/PersonaClick/Classes/Tracking/Service/protocol/TrackEventServiceProtocol.swift index e4d3450..58ed9c4 100644 --- a/PersonaClick/Classes/Tracking/Service/protocol/TrackEventServiceProtocol.swift +++ b/PersonaClick/Classes/Tracking/Service/protocol/TrackEventServiceProtocol.swift @@ -4,4 +4,5 @@ import Foundation protocol TrackEventServiceProtocol { func track(event: Event, recommendedBy: RecomendedBy?, completion: @escaping (Result) -> Void) func trackEvent(event: String, category: String?, label: String?, value: Int?, completion: @escaping (Result) -> Void) + func trackPopupShown(popupId: Int, completion: @escaping (Result) -> Void) } diff --git a/version.properties b/version.properties index 3ded943..34b3bf8 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -VERSION_CODE=14 -VERSION_NAME=3.20.0 +VERSION_CODE=15 +VERSION_NAME=3.21.0