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

MBL-2069 Add images on rewards UI #2224

Merged
merged 9 commits into from
Feb 13, 2025

Conversation

ycheng-kickstarter
Copy link
Contributor

@ycheng-kickstarter ycheng-kickstarter commented Feb 12, 2025

📲 What

We're adding images on rewards!

🤔 Why

Because it's about damn time.

🛠 How

- Images on 3 different surfaces
Used Coil for image loading and caching. Rewards carousel, add ons, and manage pledge were all separate surfaces using different technologies (xml vs compose, vertical vs horizontal scrollview) that each needed to be dealt with differently. See the associated files with each screen to make your code review easier:

Rewards screen Add ons screen Manage your pledge screen
rewards screen add ons screen manage your pledge screen
--- --- ---
KSRewardCard, RewardCarouselScreen AddOnsContainer, AddOnsScreen AddOnsViewHolder, AddOnsViewHolderViewModel, RewardAndAddOnsAdapter, BackingFragmentViewModel, item_add_on

- API
Most of the API integration was done in #2219 but for the manage pledge screen I missed some graphql changes for the backing fragment, so added that to this PR. We can't simply add a field for image on the main Reward fragment because we want to fetch a bare minimum Reward object when querying Projects to prevent transactionTooLarge exception: #2173

- Image crop
Images provided by server were not cropped! Image views had to be set to aspect ratio of 3:2 on client.

👀 See

Before 🐛 After 🦋
before after

📋 QA

  • On a project without images (dance category is a good one), go through the rewards and add ons flow to verify cards are unchanged
  • On a project with images, go through the rewards and add ons flow to view images
  • On a project you've backed, go through the Manage Your Pledge flow to view images
  • Verify grey placeholder is displayed while images are loading
  • Verify images have 3:2 aspect ratio
  • Go to Manage Your Pledge -> Choose another reward, and confirm that the "Your selection" tag is placed over the image:

Story 📖

https://kickstarter.atlassian.net/browse/MBL-2069

}
Column(
modifier = Modifier.padding(dimensions.paddingMediumLarge)
) {
Copy link
Contributor Author

@ycheng-kickstarter ycheng-kickstarter Feb 12, 2025

Choose a reason for hiding this comment

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

Please ignore everything after this line. Only relevant changes in this file are up to here. The diff just did a terrible job of detecting actual differences.

@@ -44,6 +50,7 @@ fun KSRewardCardPreview() {
conversion = "about $400",
title = "Deck of cards",
backerCountBadgeText = "23 backers",
image = Photo.builder().altText("").full("https://i.kickstarter.com/Superbacker_Lock-up-0e9d240.png").build(),
Copy link
Contributor Author

@ycheng-kickstarter ycheng-kickstarter Feb 12, 2025

Choose a reason for hiding this comment

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

Was trying to get an image to show in the compose preview but it didn't work. If anyone has ideas lmk

Copy link
Contributor

Choose a reason for hiding this comment

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

For preview to render needs to be a local resource I'm afraid, you can:

  • try to add the file path to a local resource (local to the internal builds if wanna to a real image).
  • or whichever library we are using for the image add a placeholder for the empty use case, chances are it will render on the preview

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, empty placeholder renders if I pass in an empty string url

shape = RoundedCornerShape(dimensions.radiusMediumSmall),
) {
Column(
modifier = Modifier.background(colors.kds_white)
) {
if (yourSelectionIsVisible) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved the Your Selection tag to its own composable.

@@ -60,6 +60,9 @@ data class KSDimensions(
val storedCardImageWidth: Dp = Dp.Unspecified,
val alertIconSize: Dp = Dp.Unspecified,
val plotChargeItemWidth: Dp = Dp.Unspecified,
val cardWidth: Dp = Dp.Unspecified,
val cardImageHeight: Dp = Dp.Unspecified,
val cardImageAspectRatio: Float = Float.NaN,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the rewards carousel where we use a fixed card width, use the cardWidth and cardImageHeight dimens. For the add ons screen and manage pledge screen, the card width is match_parent, so use cardImageAspectRatio dimen instead.

@@ -545,6 +545,7 @@ interface BackingFragmentViewModel {

private fun joinProjectDataAndReward(projectData: ProjectData): Pair<ProjectData, Reward> {
val reward = projectData.backing()?.reward()
?: projectData.project().backing()?.reward()
Copy link
Contributor Author

@ycheng-kickstarter ycheng-kickstarter Feb 12, 2025

Choose a reason for hiding this comment

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

‼️‼️‼️‼️

Please sanity check me, as I'm not as familiar with how projectData gets passed around. I feel confident that this logic won't break anything because if L548 is null it'll just move on to the next line. But still, please confirm I have the right understanding!

For this screen, projectData.backing() = null (so L547 will not be used). Instead, the backing exists on the projectData.project(). And I want to get the reward off the backing because that's where the rewardImage field lies, so that's what L548 does.

What I don't want to do is L549, where I try to find the reward off the project using the backedReward extension function, because the reward off the project specifically does NOT contain the rewardImage field:

fun Backing.backedReward(project: Project): Reward? {
    val rewards = project.rewards() ?: return null
    for (reward in rewards) {
        if (isBacked(reward)) {
            return reward
        }
    }
    return null
}

I added tests to BackingFragmentViewModelTest to test L548 and L549 when the backing has a reward on it vs when the backing only has a rewardId on it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fyi, take a look here for how ProjectData goes to BackingFragment ->
1-


2 -
private fun updateFragments(projectData: ProjectData) {

3 ->

app:layout_constraintDimensionRatio="@string/reward_card_image_aspect_ratio"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can ignore the changes in this file after this line!

@codecov-commenter
Copy link

codecov-commenter commented Feb 12, 2025

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 0% with 115 lines in your changes missing coverage. Please review.

Project coverage is 67.93%. Comparing base (ea6ccdd) to head (4ac556b).

Files with missing lines Patch % Lines
.../activities/compose/projectpage/AddOnsContainer.kt 0.00% 111 Missing ⚠️
...arter/services/transformers/GraphQLTransformers.kt 0.00% 4 Missing ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #2224      +/-   ##
============================================
- Coverage     67.97%   67.93%   -0.05%     
  Complexity     2197     2197              
============================================
  Files           356      356              
  Lines         23966    23983      +17     
  Branches       3521     3524       +3     
============================================
  Hits          16292    16292              
- Misses         5829     5846      +17     
  Partials       1845     1845              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@ycheng-kickstarter ycheng-kickstarter marked this pull request as ready for review February 12, 2025 23:23
Arkariang
Arkariang previously approved these changes Feb 13, 2025
Copy link
Contributor

@Arkariang Arkariang left a comment

Choose a reason for hiding this comment

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

🎆 🥳 🎈 Finally!! getting Images on rewards!

@ycheng-kickstarter
Copy link
Contributor Author

ycheng-kickstarter commented Feb 13, 2025

Fixed an issue with center crop. See before and after:

Copy link
Contributor

@leighdouglas leighdouglas left a comment

Choose a reason for hiding this comment

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

this looks so good! great job 🎉

@ycheng-kickstarter ycheng-kickstarter merged commit fca318a into master Feb 13, 2025
3 checks passed
@ycheng-kickstarter ycheng-kickstarter deleted the MBL-2069-images-on-rewards-ui branch February 13, 2025 17:02
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.

4 participants