Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Implement clickable image through CustomJson (RMCCX-7223) #328

Merged
merged 4 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Send RMC impression event to RAT [RMCCX-6939]
- Send RMC push primer event to RAT [RMCCX-6938]
- Implement console warning if LogEvent is called before calling RegisterPreference [RMCCX-7109]
- Implement clickable image through CustomJson [RMCCX-7233]

### 8.3.0 (2024-05-13)
- Improvements:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,34 +72,29 @@ internal struct ButtonBehavior: Codable {

internal struct CustomJson: Codable {
let pushPrimer: PrimerButton?
let clickableImage: ClickableImage?

enum CodingKeys: String, CodingKey {
case pushPrimer
case clickableImage
}

init(pushPrimer: PrimerButton? = nil) {
init(pushPrimer: PrimerButton? = nil, clickableImage: ClickableImage? = nil) {
self.pushPrimer = pushPrimer
self.clickableImage = clickableImage
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
pushPrimer = try container.decodeIfPresent(PrimerButton.self, forKey: .pushPrimer)
clickableImage = try container.decodeIfPresent(ClickableImage.self, forKey: .clickableImage)
}
}

internal struct PrimerButton: Codable {
let button: Int?

enum CodingKeys: String, CodingKey {
case button
}

init(button: Int? = nil) {
self.button = button
}
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
button = try container.decodeIfPresent(Int.self, forKey: .button)
}
internal struct ClickableImage: Codable {
let url: String?
}
11 changes: 11 additions & 0 deletions Sources/RInAppMessaging/Views/FullView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ internal class FullView: UIView, FullViewType, RichContentBrowsable {
super.layoutSubviews()

updateUIConstants()

// Clickable Image Feature only for RMC
if RInAppMessaging.isRMCEnvironment {
imageView.isUserInteractionEnabled = true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are making isUserInteractionEnabled = false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default its false. Its only enabled for RMC-IAM. Rest of the validation is in didClickCampaignImage()

imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onClickCampaignImage)))
}

bodyViewOffsetYConstraint.constant = hasImage ? 0 : uiConstants.bodyViewSafeAreaOffsetY

DispatchQueue.main.async {
Expand Down Expand Up @@ -350,4 +357,8 @@ internal class FullView: UIView, FullViewType, RichContentBrowsable {
@objc private func onExitButtonClick() {
presenter.didClickExitButton()
}

@objc private func onClickCampaignImage() {
presenter.didClickCampaignImage()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ internal protocol FullViewPresenterType: BaseViewPresenterType {
func loadButtons()
func didClickAction(sender: ActionButton)
func didClickExitButton()
func didClickCampaignImage()
}

internal class FullViewPresenter: BaseViewPresenter, FullViewPresenterType, ErrorReportable {
Expand Down Expand Up @@ -114,6 +115,18 @@ internal class FullViewPresenter: BaseViewPresenter, FullViewPresenterType, Erro

view?.dismiss()
}

func didClickCampaignImage() {
guard !campaign.isPushPrimer else { return }
guard let clickImageData = campaign.data.customJson?.clickableImage,
let uriToOpen = URL(string: clickImageData.url ?? "") else {
return
}
logImpression(type: .clickContent)
sendImpressions()
UIApplication.shared.open(uriToOpen)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what will happen if uriToOpen is empty string ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected behaviour is to do nothing on tap. It will behave as a normal campaign.
This is covered in the test cases .

view?.dismiss()
}

// MARK: - Private

Expand Down
72 changes: 72 additions & 0 deletions Tests/Tests/ViewPresenterSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,78 @@ class ViewPresenterSpec: QuickSpec {
expect(view.addedButtons.map({ $0.0.type })[1]).to(equal(ActionType.pushPrimer))
}
}

context("when clickableImage content is available in Campaign Data and image is clicked") {

beforeEach {
BundleInfoMocked.bundleMock = BundleMock()
RInAppMessaging.bundleInfo = BundleInfoMocked.self
RInAppMessaging.deinitializeModule()
}

afterEach {
RInAppMessaging.bundleInfo = BundleInfo.self
}

let campaignClickableImage = TestHelpers.generateCampaign(id: "ClickableImage", hasImage: true,
customJson: CustomJson(clickableImage: ClickableImage(url: "https://www.google.com")))
it("will call dismiss on the view object") {
presenter.campaign = campaignClickableImage
presenter.didClickCampaignImage()
expect(view.wasDismissCalled).to(beTrue())
}

it("will send impressions containing .clickContent type") {
presenter.campaign = campaignClickableImage
presenter.didClickCampaignImage()
expect(impressionService.sentImpressions?.list).to(contain(.clickContent))
expect(impressionService.sentImpressions).toNot(beNil())
}

it("will do nothing if redirect URL is empty") {
let campaignClickableImage = TestHelpers.generateCampaign(id: "ClickableImage",
hasImage: true,
customJson: CustomJson(clickableImage: ClickableImage(url: "")))
presenter.campaign = campaignClickableImage
presenter.didClickCampaignImage()
expect(view.wasDismissCalled).to(beFalse())
expect(impressionService.sentImpressions).to(beNil())
}

it("will do nothing if clickableImage is empty") {
let campaignClickableImage = TestHelpers.generateCampaign(id: "ClickableImage",
hasImage: true,
customJson: CustomJson(clickableImage: nil ))
presenter.campaign = campaignClickableImage
presenter.didClickCampaignImage()
expect(view.wasDismissCalled).to(beFalse())
expect(impressionService.sentImpressions).to(beNil())
}
it("will do nothing if the campaign is a pushPrimer campaign") {
let campaignPrimer = TestHelpers.generateCampaign(id: "PushPrimer", buttons: [
Button(buttonText: "button1",
buttonTextColor: "#000000",
buttonBackgroundColor: "#000000",
buttonBehavior: ButtonBehavior(action: .close, uri: nil),
campaignTrigger: Trigger(type: .event,
eventType: .custom,
eventName: "trigger",
attributes: [])),
Button(buttonText: "button2",
buttonTextColor: "#ffffff",
buttonBackgroundColor: "#ffffff",
buttonBehavior: ButtonBehavior(action: .redirect, uri: "uri"),
campaignTrigger: nil)
],
customJson: CustomJson(pushPrimer: PrimerButton(button: 2),
clickableImage: ClickableImage(url: "https://url"))
)
presenter.campaign = campaignPrimer
presenter.didClickCampaignImage()
expect(view.wasDismissCalled).to(beFalse())
expect(impressionService.sentImpressions).to(beNil())
}
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions Tests/Tests/ViewSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ class FullViewPresenterMock: FullViewPresenterType {
}
func handleButtonTrigger(_ trigger: Trigger?) { }
func optOutCampaign() { }
func didClickCampaignImage() { }
}

class SlideUpViewPresenterMock: SlideUpViewPresenterType {
Expand Down
Loading