Skip to content

Commit

Permalink
fix: Fix in-app messages overlay color ignored from message payload (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mahmoud-elmorabea authored Jan 8, 2025
1 parent 3e513bd commit bbc708d
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 7 deletions.
3 changes: 2 additions & 1 deletion Sources/MessagingInApp/Gist/Managers/MessageManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ class MessageManager: EngineWebDelegate {
return
}

let gistProperties = currentMessage.gistProperties
logger.logWithModuleTag("Loading modal message: \(currentMessage.describeForLogs)", level: .debug)
modalViewManager = ModalViewManager(gistView: gistView, position: currentMessage.gistProperties.position)
modalViewManager = ModalViewManager(gistView: gistView, position: gistProperties.position, overlayColor: gistProperties.overlayColor)
modalViewManager?.showModalView { [weak self] in
guard let self = self else { return }

Expand Down
7 changes: 5 additions & 2 deletions Sources/MessagingInApp/Gist/Managers/ModalViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ class ModalViewManager {
var window: UIWindow?
var viewController: GistModalViewController!
var position: MessagePosition
var overlayColor: String?

init(gistView: GistView, position: MessagePosition) {
init(gistView: GistView, position: MessagePosition, overlayColor: String?) {
self.viewController = GistModalViewController()
viewController.gistView = gistView
viewController.setup(position: position)
self.position = position
self.overlayColor = overlayColor
}

func showModalView(completionHandler: @escaping () -> Void) {
Expand All @@ -37,11 +39,12 @@ class ModalViewManager {
finalPosition = viewController.view.center.y - viewController.view.bounds.height
}

let overlayColor = UIColor.fromHex(overlayColor) ?? UIColor.black.withAlphaComponent(0.2)
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseIn], animations: {
self.viewController.view.center.y = finalPosition
}, completion: { _ in
UIView.animate(withDuration: 0.1, delay: 0, options: [.curveEaseIn], animations: {
self.viewController.view.backgroundColor = UIColor.black.withAlphaComponent(0.2)
self.viewController.view.backgroundColor = overlayColor
}, completion: nil)
completionHandler()
})
Expand Down
19 changes: 15 additions & 4 deletions Sources/MessagingInApp/Gist/Managers/Models/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@ public class GistProperties {
public let campaignId: String?
public let position: MessagePosition
public let persistent: Bool?

init(routeRule: String?, elementId: String?, campaignId: String?, position: MessagePosition, persistent: Bool?) {
public let overlayColor: String?

init(
routeRule: String?,
elementId: String?,
campaignId: String?,
position: MessagePosition,
persistent: Bool?,
overlayColor: String?
) {
self.routeRule = routeRule
self.elementId = elementId
self.position = position
self.campaignId = campaignId
self.persistent = persistent
self.overlayColor = overlayColor
}
}

Expand Down Expand Up @@ -45,21 +54,23 @@ public class Message {
private static func parseGistProperties(from gist: [String: Any]?) -> GistProperties {
let defaultPosition = MessagePosition.center
guard let gist = gist else {
return GistProperties(routeRule: nil, elementId: nil, campaignId: nil, position: defaultPosition, persistent: false)
return GistProperties(routeRule: nil, elementId: nil, campaignId: nil, position: defaultPosition, persistent: false, overlayColor: nil)
}

let position = (gist["position"] as? String).flatMap(MessagePosition.init) ?? defaultPosition
let routeRule = gist["routeRuleApple"] as? String
let elementId = gist["elementId"] as? String
let campaignId = gist["campaignId"] as? String
let persistent = gist["persistent"] as? Bool ?? false
let overlayColor = gist["overlayColor"] as? String

return GistProperties(
routeRule: routeRule,
elementId: elementId,
campaignId: campaignId,
position: position,
persistent: persistent
persistent: persistent,
overlayColor: overlayColor
)
}
}
Expand Down
42 changes: 42 additions & 0 deletions Sources/MessagingInApp/Gist/Utilities/UIColor+Hex.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import UIKit

extension UIColor {
static func fromHex(_ hex: String?) -> UIColor? {
guard let hex = hex else { return nil }

let cleanHex = hex.hasPrefix("#") ? String(hex.dropFirst()) : hex

// Validate hex string length
guard cleanHex.count == 6 || cleanHex.count == 8 else {
return nil
}

let correctHex = correctColorFormatIfNeeded(cleanHex)

var rgbValue: UInt64 = 0
guard Scanner(string: correctHex).scanHexInt64(&rgbValue) else {
return nil
}

// Extract color components
let red = CGFloat((rgbValue >> 16) & 0xFF) / 255.0
let green = CGFloat((rgbValue >> 8) & 0xFF) / 255.0
let blue = CGFloat(rgbValue & 0xFF) / 255.0
let alpha = cleanHex.count == 8
? CGFloat((rgbValue >> 24) & 0xFF) / 255.0
: 1.0

return UIColor(red: red, green: green, blue: blue, alpha: alpha)
}

private static func correctColorFormatIfNeeded(_ color: String) -> String {
guard color.count == 8 else { return color }

let red = color[color.index(color.startIndex, offsetBy: 0) ..< color.index(color.startIndex, offsetBy: 2)]
let green = color[color.index(color.startIndex, offsetBy: 2) ..< color.index(color.startIndex, offsetBy: 4)]
let blue = color[color.index(color.startIndex, offsetBy: 4) ..< color.index(color.startIndex, offsetBy: 6)]
let alpha = color[color.index(color.startIndex, offsetBy: 6) ..< color.endIndex]

return "\(alpha)\(red)\(green)\(blue)"
}
}
77 changes: 77 additions & 0 deletions Tests/MessagingInApp/Utilities/UIColor+HexTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
@testable import CioMessagingInApp

import UIKit
import XCTest

class UIColorFromHexTests: UnitTest {
func test_parseColor_givenInputColorIsNull_expectNullAsResult() {
let result = UIColor.fromHex(nil)

XCTAssertNil(result)
}

func test_parseColor_givenInputColorIsEmpty_expectNullAsResult() {
let result = UIColor.fromHex("")

XCTAssertNil(result)
}

func test_parseColor_givenInputColorHasUnexpectedCharCount_expectNullAsResult() {
// Only colors with 6 or 8 chars are accepted
XCTAssertNil(UIColor.fromHex("#"))
XCTAssertNil(UIColor.fromHex("#FF11F"))
XCTAssertNil(UIColor.fromHex("#FF11FF1"))
}

func test_parseColor_givenInputColorWithNonHexChars_expectNullResult() {
XCTAssertNil(UIColor.fromHex("#MMXXMMYY"))
}

func test_parseColor_givenValidInputColorWithoutAlpha_expectCorrectResult() {
let result = UIColor.fromHex("#007AFF") // R:0, G:122, B:255

var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
result?.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

XCTAssertEqual(red, 0.0, accuracy: 0.01)
XCTAssertEqual(green, 0.48, accuracy: 0.01)
XCTAssertEqual(blue, 1.00, accuracy: 0.01)
XCTAssertEqual(alpha, 1.0, "Alpha should be 1.0 for 6-character hex.")
}

func test_parseColor_givenValidInputColorWithoutHashAndWithoutAlpha_expectCorrectResult() {
let result = UIColor.fromHex("007AFF") // R:0, G:122, B:255

var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
result?.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

XCTAssertEqual(red, 0.0, accuracy: 0.01)
XCTAssertEqual(green, 0.48, accuracy: 0.01)
XCTAssertEqual(blue, 1.00, accuracy: 0.01)
XCTAssertEqual(alpha, 1.0, "Alpha should be 1.0 for 6-character hex.")
}

func test_parseColor_givenValidInputColorWithAlpha_expectCorrectResult() {
let result = UIColor.fromHex("#007AFF80") // R:0, G:122, B:255, Alpha:50%

var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
result?.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

XCTAssertEqual(red, 0.0, accuracy: 0.01)
XCTAssertEqual(green, 0.48, accuracy: 0.01)
XCTAssertEqual(blue, 1.00, accuracy: 0.01)
XCTAssertEqual(alpha, 0.5, accuracy: 0.01)
}

func test_parseColor_givenValidInputColorWithoutHashAndWithAlpha_expectCorrectResult() {
let result = UIColor.fromHex("007AFF80") // R:0, G:122, B:255, Alpha:50%

var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
result?.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

XCTAssertEqual(red, 0.0, accuracy: 0.01)
XCTAssertEqual(green, 0.48, accuracy: 0.01)
XCTAssertEqual(blue, 1.00, accuracy: 0.01)
XCTAssertEqual(alpha, 0.5, accuracy: 0.01)
}
}

0 comments on commit bbc708d

Please sign in to comment.