Skip to content

App Inbox: add default media fallback when orientation is missing#530

Open
darshanclevertap wants to merge 2 commits intoSDK-5626-v7.6.0from
codex/app-inbox-default-media-fallback
Open

App Inbox: add default media fallback when orientation is missing#530
darshanclevertap wants to merge 2 commits intoSDK-5626-v7.6.0from
codex/app-inbox-default-media-fallback

Conversation

@darshanclevertap
Copy link
Copy Markdown
Contributor

@darshanclevertap darshanclevertap commented Mar 30, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Fixed carousel and inbox messages displaying missing or empty media items
    • Improved media aspect ratio calculations for better image and video rendering
    • Enhanced placeholder image selection for consistent media presentation
  • New Features

    • Added default media layout variants for improved message formatting

@francispereira
Copy link
Copy Markdown

francispereira commented Mar 30, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • ^(task|feat|feature|fix)/

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 76a6aceb-927f-42f3-a3f4-8b2e8fa5d4b3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR introduces a new default media layout feature for inbox message cells by adding enum variants, a new secondary media view property, helper methods to determine layout eligibility, and conditional logic that routes image rendering between standard and default views based on orientation detection.

Changes

Cohort / File(s) Summary
Base cell infrastructure
CleverTapSDK/Inbox/cells/CTInboxBaseMessageCell.h, CleverTapSDK/Inbox/cells/CTInboxBaseMessageCell.m
Added three new CTMediaPlayerCellType enum cases (*Default variants), two new properties (defaultCellImageView, defaultMediaHeightConstraint), and six new helper methods for default media layout management. Updated media cell type selection to use *Default variants when shouldUseDefaultMediaLayout is true, and introduced dual-view routing via activeMediaImageView.
Message cell implementations
CleverTapSDK/Inbox/cells/CTInboxIconMessageCell.m, CleverTapSDK/Inbox/cells/CTInboxSimpleMessageCell.m
Updated prepareForReuse to clean up defaultCellImageView, reworked doLayoutForMessage: to call default-layout configuration when applicable, and refactored setupMessage: to route image rendering through activeMediaImageView and update default layout dimensions based on loaded image aspect ratios.
Carousel cell handling
CleverTapSDK/Inbox/cells/CTCarouselMessageCell.m, CleverTapSDK/Inbox/cells/CTCarouselImageMessageCell.m
Modified height calculations to use shouldUseDefaultMediaLayout for layout decisions, updated carousel image/view population to skip empty mediaUrl entries, changed constraint priorities based on layout type, and updated CTCarouselImageView initialization calls with new orientationKnown parameter.
Carousel image view
CleverTapSDK/Inbox/views/CTCarouselImageView.h, CleverTapSDK/Inbox/views/CTCarouselImageView.m
Added orientationKnown parameter to both public constructors, updated contentMode selection to prefer AspectFill when orientation is known, and made constraint priority adjustments conditional on the orientationKnown flag.
Visibility tracking
CleverTapSDK/Inbox/controllers/CleverTapInboxViewController.m
Extended cellMediaIsVisible: to handle three new *Default media cell types with specific offset-based visibility thresholds.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

eaa

Suggested reviewers

  • nishant-clevertap
  • akashvercetti
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a default media fallback for App Inbox when orientation information is missing, which aligns with the substantial modifications across multiple cell classes and the new default media layout infrastructure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/app-inbox-default-media-fallback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@darshanclevertap
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CleverTapSDK/Inbox/cells/CTCarouselImageMessageCell.m (1)

14-21: ⚠️ Potential issue | 🟠 Major

Preserve the original content index when skipping empty media.

Lines 20-21 now drop empty-string media URLs, but Line 37 still tags the rendered card with the compacted index. handleItemViewTapGesture: forwards that tag as index, so any visible card after a skipped entry will resolve the wrong message.content[...] item on tap.

💡 Suggested fix
-    int index = 0;
+    NSInteger contentIndex = 0;
     int imageNumber = 1;
     for (CleverTapInboxMessageContent *content in (self.message.content)) {
         NSString *imageUrl = content.mediaUrl;
         NSString *actionUrl = content.actionUrl;
         NSString *imageDescription = content.mediaDescription ? content.mediaDescription : [NSString stringWithFormat:@"Message Image %d", imageNumber];
         imageNumber = imageNumber + 1;
         
         if (imageUrl == nil || imageUrl.length == 0) {
+            contentIndex++;
             continue;
         }
         CTCarouselImageView *itemView;
         if (itemView == nil){
             CGRect frame = self.carouselView.bounds;
@@
         UITapGestureRecognizer *itemViewTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:`@selector`(handleItemViewTapGesture:)];
         itemView.userInteractionEnabled = YES;
-        itemView.tag = index;
+        itemView.tag = contentIndex;
         [itemView addGestureRecognizer:itemViewTapGesture];
         [self.itemViews addObject:itemView];
-        index++;
+        contentIndex++;
     }

Also applies to: 35-40

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CleverTapSDK/Inbox/cells/CTCarouselImageMessageCell.m` around lines 14 - 21,
The loop that builds carousel image views currently skips entries with empty
mediaUrl but compacts the index used as the view tag, causing
handleItemViewTapGesture: to resolve the wrong CleverTapInboxMessageContent from
self.message.content; fix by preserving the original content index (e.g., use an
index variable like originalIndex or capture the for-loop index) and assign that
original index to the view.tag even when you continue for empty media, making
sure any counters used for layout (visibleImageCount) remain separate from the
contentIndex used for tags and lookup in handleItemViewTapGesture:.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CleverTapSDK/Inbox/cells/CTCarouselMessageCell.m`:
- Around line 41-44: populateLandscapeViews and populateItemViews compress the
loop index when skipping media-less contents, but handleItemViewTapGesture
expects each view.tag to equal the original index in self.message.content; this
causes mis-mapped taps/analytics. Fix by preserving the original content index
when assigning view.tag (e.g., use an originalIndex variable or the loop index
over self.message.content) and only skip adding views for empty mediaUrls while
still setting tag = originalIndex; apply this change in both
populateLandscapeViews and populateItemViews (and the other mentioned blocks
around lines 68-73, 91-93, 110-115) so tags always map to the source content
array index.

In `@CleverTapSDK/Inbox/cells/CTInboxBaseMessageCell.m`:
- Around line 632-639: resetDefaultMediaView currently clears only
defaultMediaHeightConstraint but the shared imageViewHeightConstraint (the
outlet promoted at lines referencing imageViewHeightConstraint) keeps a fallback
constant so reused text-only cells retain stale blank space; update
resetDefaultMediaView to either reset/restore the original constant on
imageViewHeightConstraint (store the original constant when promoting it, then
set it back and reactivate/deactivate appropriately) or separate sizing by
keeping defaultMediaHeightConstraint exclusively for default media and never
mutating imageViewHeightConstraint elsewhere (ensure methods that set the
fallback height target defaultMediaHeightConstraint). Also apply the same fix
pattern to the other similar methods in the 642-683 range that manipulate media
sizing so all paths restore or isolate the shared constraint.

---

Outside diff comments:
In `@CleverTapSDK/Inbox/cells/CTCarouselImageMessageCell.m`:
- Around line 14-21: The loop that builds carousel image views currently skips
entries with empty mediaUrl but compacts the index used as the view tag, causing
handleItemViewTapGesture: to resolve the wrong CleverTapInboxMessageContent from
self.message.content; fix by preserving the original content index (e.g., use an
index variable like originalIndex or capture the for-loop index) and assign that
original index to the view.tag even when you continue for empty media, making
sure any counters used for layout (visibleImageCount) remain separate from the
contentIndex used for tags and lookup in handleItemViewTapGesture:.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 29b6ad35-89e6-4124-a9a5-ad427e600e31

📥 Commits

Reviewing files that changed from the base of the PR and between ef6f3e6 and 78b96a4.

📒 Files selected for processing (9)
  • CleverTapSDK/Inbox/cells/CTCarouselImageMessageCell.m
  • CleverTapSDK/Inbox/cells/CTCarouselMessageCell.m
  • CleverTapSDK/Inbox/cells/CTInboxBaseMessageCell.h
  • CleverTapSDK/Inbox/cells/CTInboxBaseMessageCell.m
  • CleverTapSDK/Inbox/cells/CTInboxIconMessageCell.m
  • CleverTapSDK/Inbox/cells/CTInboxSimpleMessageCell.m
  • CleverTapSDK/Inbox/controllers/CleverTapInboxViewController.m
  • CleverTapSDK/Inbox/views/CTCarouselImageView.h
  • CleverTapSDK/Inbox/views/CTCarouselImageView.m

Comment on lines 41 to +44
for (CleverTapInboxMessageContent *content in (self.message.content)) {
if (content.mediaUrl.length == 0) {
continue;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep tag mapped to the source content index in both carousel builders.

The new skip paths for empty media URLs compact the local index, but handleItemViewTapGesture: later treats tag as the original message.content index. After a skipped item, taps and analytics will be sent for the wrong carousel entry in both populateLandscapeViews and populateItemViews.

💡 Suggested fix
-    NSUInteger index = 0;
+    NSUInteger contentIndex = 0;
     int imageNumber = 1;
     for (CleverTapInboxMessageContent *content in (self.message.content)) {
         if (content.mediaUrl.length == 0) {
+            contentIndex++;
             continue;
         }
         CTCarouselImageView *carouselItemView;
         if (carouselItemView == nil){
             carouselItemView  = [[[CTUIUtils bundle] loadNibNamed: NSStringFromClass([CTCarouselImageView class]) owner:nil options:nil] lastObject];
@@
         UITapGestureRecognizer *carouselViewTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:`@selector`(handleItemViewTapGesture:)];
         carouselItemView.userInteractionEnabled = YES;
-        carouselItemView.tag = index;
+        carouselItemView.tag = contentIndex;
         [carouselItemView addGestureRecognizer:carouselViewTapGesture];
         [self.itemViews addObject:carouselItemView];
-        index++;
+        contentIndex++;
     }

Apply the same pattern in populateItemViews.

Also applies to: 68-73, 91-93, 110-115

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CleverTapSDK/Inbox/cells/CTCarouselMessageCell.m` around lines 41 - 44,
populateLandscapeViews and populateItemViews compress the loop index when
skipping media-less contents, but handleItemViewTapGesture expects each view.tag
to equal the original index in self.message.content; this causes mis-mapped
taps/analytics. Fix by preserving the original content index when assigning
view.tag (e.g., use an originalIndex variable or the loop index over
self.message.content) and only skip adding views for empty mediaUrls while still
setting tag = originalIndex; apply this change in both populateLandscapeViews
and populateItemViews (and the other mentioned blocks around lines 68-73, 91-93,
110-115) so tags always map to the source content array index.

Comment on lines +632 to +639
- (void)resetDefaultMediaView {
[self.defaultCellImageView sd_cancelCurrentImageLoad];
self.defaultCellImageView.image = nil;
self.defaultCellImageView.hidden = YES;
if (self.defaultMediaHeightConstraint) {
self.defaultMediaHeightConstraint.constant = 0;
self.defaultMediaHeightConstraint.active = NO;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep the fallback height off the shared imageViewHeightConstraint.

Lines 654-655 promote the existing outlet constraint, and Line 674 overwrites its constant, but resetDefaultMediaView only clears defaultMediaHeightConstraint. After reuse, a text-only or standard-layout cell can inherit the previous fallback height and render with stale blank space. Please keep the default-media sizing on its own constraint, or restore the original constant during reset.

Also applies to: 642-683

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CleverTapSDK/Inbox/cells/CTInboxBaseMessageCell.m` around lines 632 - 639,
resetDefaultMediaView currently clears only defaultMediaHeightConstraint but the
shared imageViewHeightConstraint (the outlet promoted at lines referencing
imageViewHeightConstraint) keeps a fallback constant so reused text-only cells
retain stale blank space; update resetDefaultMediaView to either reset/restore
the original constant on imageViewHeightConstraint (store the original constant
when promoting it, then set it back and reactivate/deactivate appropriately) or
separate sizing by keeping defaultMediaHeightConstraint exclusively for default
media and never mutating imageViewHeightConstraint elsewhere (ensure methods
that set the fallback height target defaultMediaHeightConstraint). Also apply
the same fix pattern to the other similar methods in the 642-683 range that
manipulate media sizing so all paths restore or isolate the shared constraint.

@akashvercetti akashvercetti changed the base branch from develop to SDK-5626-v7.6.0 April 1, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants