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-1567: Pledge redemption (Backer Shipping Expectation & Backer Shipping Country) #2112

Merged
merged 33 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
fd7114b
MBL-1568: Rewards carousel has shipping selector (#2068)
Arkariang Jul 9, 2024
c1056e2
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Jul 10, 2024
5cc49ef
MBL-1569: Remove shipping selector from AddOns Screen Crowdfund check…
Arkariang Jul 11, 2024
e77957c
MBL-1578: rewards carousel late pledges contains shipping selector (#…
Arkariang Jul 15, 2024
2c02922
MBL-1570: Remove shipping selector from addOns late pledge (#2074)
Arkariang Jul 18, 2024
573b9a2
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Jul 18, 2024
1fa32a4
Merge branch 'feature/pledge-redemption-ml1' of github.com:kickstarte…
Arkariang Jul 18, 2024
3fcf3c2
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Jul 18, 2024
b37be38
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Jul 23, 2024
6081ddb
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Jul 23, 2024
7c60d26
MBL-1587: Unify add ons selection screen for crowdfund and late pledg…
Arkariang Jul 24, 2024
89e2dd1
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Jul 24, 2024
9885cb2
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Aug 1, 2024
d32fa71
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Aug 6, 2024
3fc65ed
MBL-1571: Bonus support in add ons screen (#2089)
Arkariang Aug 8, 2024
fc3828b
[MBL-1575] Add estimated shipping costs for rewards and add-ons (#2090)
mtgriego Aug 8, 2024
3cd81eb
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Aug 8, 2024
01986fb
remove update pledge button from manage pledge screen (#2093)
mtgriego Aug 9, 2024
97e377b
MBL-1576: Filter rewards by location (#2094)
Arkariang Aug 12, 2024
ac6281a
MBL[1583/1584] Add bonus support visual treatments based on selected …
mtgriego Aug 14, 2024
127a79d
MBL-1573: Checkout Screen (Pledge and update payment method flows for…
Arkariang Aug 14, 2024
05c5e6e
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Aug 15, 2024
2a5123e
MBL-1573: Add new payment method using stripe (#2102)
Arkariang Aug 15, 2024
b68e508
MBL-1573: Checkout Screen (Change Reward flow for crowdfund) (#2103)
Arkariang Aug 19, 2024
ab70600
MBL-1573: Checkout Screen (Reward noReward for crowdfund and late ple…
Arkariang Aug 19, 2024
0d89037
Merge branches 'feature/pledge-redemption-ml1' and 'master' of github…
Arkariang Aug 19, 2024
2c63694
[MBL-1574] Add estimated shipping visuals to checkout screens (#2105)
mtgriego Aug 20, 2024
0329373
[no-jira]: Strings to be tranlated (#2106)
Arkariang Aug 21, 2024
34d787c
MBL-1573: Testing + small UI details (#2107)
Arkariang Aug 22, 2024
b0f720b
MBL-1585: small touch ups (#2110)
Arkariang Aug 27, 2024
53e187d
Merge branch 'master' of github.com:kickstarter/android-oss into feat…
Arkariang Aug 27, 2024
b3e6cb2
- version names
Arkariang Aug 27, 2024
70a3c59
Merge branch 'master' into feature/pledge-redemption-ml1
Arkariang Aug 27, 2024
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
2 changes: 1 addition & 1 deletion app/internal_version_code.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2014150908
2014150913
2 changes: 1 addition & 1 deletion app/internal_version_name.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.23.0
3.24.0
14 changes: 14 additions & 0 deletions app/src/main/graphql/fragments.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,21 @@ fragment reward on Reward {
convertedAmount{
... amount
}
shippingRules {
... shippingRule
}
shippingPreference
remainingQuantity
limit
limitPerBacker
startsAt
endsAt
rewardType
allowedAddons {
nodes {
id
}
}
localReceiptLocation {
... location
}
Expand Down Expand Up @@ -360,6 +368,12 @@ fragment shippingRule on ShippingRule {
location {
... location
}
estimatedMin {
amount
}
estimatedMax {
amount
}
}


Expand Down
22 changes: 22 additions & 0 deletions app/src/main/java/com/kickstarter/libs/KSCurrency.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,33 @@ package com.kickstarter.libs
import com.kickstarter.libs.models.Country
import com.kickstarter.libs.models.Country.Companion.findByCurrencyCode
import com.kickstarter.libs.utils.NumberUtils
import com.kickstarter.libs.utils.ProjectViewUtils
import com.kickstarter.libs.utils.extensions.trimAllWhitespace
import com.kickstarter.models.Project
import java.math.RoundingMode
import kotlin.jvm.JvmOverloads

/**
* Currency symbol, which can be positioned at start or end of amount depending on country
*/
fun KSCurrency?.getCurrencySymbols(project: Project): Pair<String, String> {
val currencySymbolStartAndEnd = this?.let {
val symbolAndStart = ProjectViewUtils.currencySymbolAndPosition(
project,
this
)
val symbol = symbolAndStart.first
val symbolAtStart = symbolAndStart.second
if (symbolAtStart) {
Pair(symbol.toString(), "")
} else {
Pair("", symbol.toString())
}
} ?: Pair("", "")

return currencySymbolStartAndEnd
}

class KSCurrency(private val currentConfig: CurrentConfigType) {
/**
* Returns a currency string appropriate to the user's locale and location relative to a project.
Expand Down
32 changes: 32 additions & 0 deletions app/src/main/java/com/kickstarter/libs/utils/RewardUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import android.util.Pair
import com.kickstarter.R
import com.kickstarter.libs.KSString
import com.kickstarter.libs.models.Country
import com.kickstarter.libs.utils.extensions.isNonZero
import com.kickstarter.models.Project
import com.kickstarter.models.Reward
Expand All @@ -14,6 +15,12 @@ import kotlin.math.max

object RewardUtils {

fun minPledgeAmount(reward: Reward, project: Project): Double {
return Country.findByCurrencyCode(project.currency())?.minPledge?.toDouble() ?: 0.0
}

fun maxPledgeAmount(reward: Reward, project: Project): Double = Country.findByCurrencyCode(project.currency())?.maxPledge?.toDouble() ?: 0.0

/**
* Returns `true` if the reward has backers, `false` otherwise.
*/
Expand Down Expand Up @@ -66,6 +73,10 @@ object RewardUtils {
return rewardsItems != null && rewardsItems.isNotEmpty()
}

fun shipsWorldwide(reward: Reward): Boolean = reward.shippingPreference().equals(Reward.ShippingPreference.UNRESTRICTED.name, ignoreCase = true)

fun shipsToRestrictedLocations(reward: Reward): Boolean = reward.shippingPreference().equals(Reward.ShippingPreference.RESTRICTED.name, ignoreCase = true)

/**
* Returns `true` if the reward has a limit set, and the limit has not been reached, `false` otherwise.
*/
Expand Down Expand Up @@ -221,4 +232,25 @@ object RewardUtils {
fun getFinalBonusSupportAmount(addedBonusSupport: Double, initialBonusSupport: Double): Double {
return if (addedBonusSupport > 0) addedBonusSupport else initialBonusSupport
}

/** For the checkout we need to send a list repeating as much addOns items
* as the user has selected:
* User selection [R, 2xa, 3xb]
* Checkout data [R, a, a, b, b, b]
*/
fun extendAddOns(flattenedList: List<Reward>): List<Reward> {
val mutableList = mutableListOf<Reward>()

flattenedList.map {
if (!it.isAddOn()) mutableList.add(it)
else {
val q = it.quantity() ?: 1
for (i in 1..q) {
mutableList.add(it)
}
}
}

return mutableList.toList()
}
}
109 changes: 109 additions & 0 deletions app/src/main/java/com/kickstarter/libs/utils/RewardViewUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import com.kickstarter.libs.KSCurrency
import com.kickstarter.libs.KSString
import com.kickstarter.libs.models.Country
import com.kickstarter.libs.utils.extensions.isBacked
import com.kickstarter.libs.utils.extensions.isNull
import com.kickstarter.libs.utils.extensions.trimAllWhitespace
import com.kickstarter.models.Project
import com.kickstarter.models.Reward
import com.kickstarter.models.ShippingRule
import java.math.RoundingMode

object RewardViewUtils {
Expand Down Expand Up @@ -139,4 +141,111 @@ object RewardViewUtils {
maxInputAmountWithCurrency
) ?: ""
}

/**
* Return the string for the estimated shipping costs for a given shipping rule
*
* Ex. "About $10-$15" or "About $10-%15 each"
*/
fun getEstimatedShippingCostString(
context: Context,
ksCurrency: KSCurrency,
ksString: KSString,
project: Project,
rewards: List<Reward> = listOf(),
selectedShippingRule: ShippingRule,
multipleQuantitiesAllowed: Boolean,
useUserPreference: Boolean,
useAbout: Boolean
): String {
var min = ""
var max = ""
var minTotal = 0.0
var maxtotal = 0.0
rewards.forEach { reward ->
if (!RewardUtils.isDigital(reward) && RewardUtils.shipsToRestrictedLocations(reward) && !RewardUtils.isLocalPickup(reward)) {
reward.shippingRules()?.filter {
it.location()?.id() == selectedShippingRule.location()?.id()
}?.map {
minTotal += (it.estimatedMin() * (reward.quantity() ?: 1))
maxtotal += (it.estimatedMax() * (reward.quantity() ?: 1))
}
}

if (RewardUtils.shipsWorldwide(reward) && !reward.shippingRules().isNullOrEmpty()) {
reward.shippingRules()?.first()?.let {
minTotal += (it.estimatedMin() * (reward.quantity() ?: 1))
maxtotal += (it.estimatedMax() * (reward.quantity() ?: 1))
}
}
}
if (minTotal <= 0 || maxtotal <= 0) return ""

min = if (useUserPreference) {
ksCurrency.formatWithUserPreference(minTotal, project, RoundingMode.HALF_UP, precision = 2)
} else {
ksCurrency.format(minTotal, project, RoundingMode.HALF_UP)
}
max = if (useUserPreference) {
ksCurrency.formatWithUserPreference(maxtotal, project, RoundingMode.HALF_UP, precision = 2)
} else {
ksCurrency.format(maxtotal, project, RoundingMode.HALF_UP)
}

if (min.isEmpty() || max.isEmpty()) return ""

// TODO: Replace with defined string
val minToMaxString = if (multipleQuantitiesAllowed) "$min-$max each" else "$min-$max"

return if (useAbout) {
ksString.format(
context.getString(R.string.About_reward_amount),
"reward_amount",
minToMaxString
)
} else {
minToMaxString
}
}

/**
* Returns a string for the shipping costs for add-on cards
*
* Ex. " + $5 each"
*/
fun getAddOnShippingAmountString(
context: Context,
project: Project,
reward: Reward,
rewardShippingRules: List<ShippingRule>?,
ksCurrency: KSCurrency?,
ksString: KSString?,
selectedShippingRule: ShippingRule
): String {
if (rewardShippingRules.isNullOrEmpty() || ksCurrency.isNull() || ksString.isNull()) return ""
val shippingAmount =
if (!RewardUtils.isDigital(reward) && RewardUtils.isShippable(reward) && !RewardUtils.isLocalPickup(reward)) {
var cost = 0.0
rewardShippingRules.filter {
it.location()?.id() == selectedShippingRule.location()?.id()
}.map {
cost += it.cost()
}
if (cost > 0) ksCurrency?.format(cost, project)
else ""
} else {
""
}
if (shippingAmount.isNullOrEmpty()) return ""
val rewardAndShippingString =
context.getString(R.string.reward_amount_plus_shipping_cost_each)
val stringSections = rewardAndShippingString.split("+")
val shippingString = " +" + stringSections[1]
val ammountAndShippingString = ksString?.format(
shippingString,
"shipping_cost",
shippingAmount
)
return ammountAndShippingString ?: ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.kickstarter.libs.Config
import com.kickstarter.libs.preferences.StringPreferenceType
import com.kickstarter.models.ShippingRule
import org.json.JSONArray

/**
Expand Down Expand Up @@ -71,7 +72,6 @@ fun Config.syncUserFeatureFlagsFromPref(featuresFlagPreference: StringPreference
/**
* set the saved feature flags in to config feature object
*/

fun Config.setUserFeatureFlagsPrefWithFeatureFlag(
featuresFlagPreference: StringPreferenceType?,
featureName: String,
Expand All @@ -96,3 +96,30 @@ fun Config.setUserFeatureFlagsPrefWithFeatureFlag(
}?.toMap()
).build()
}

/**
* From a selected list of shipping rules, select the one that matches the config location
* if none matches return the first one.
* Config countryCode is based on IP location,
* example: if your network is within Canada, it will return Canada
* example: if your network is within Canada, but the given shipping Rules does not include
* Canada, it will return the first rule given in tha shipping rules list.
*/
fun Config.getDefaultLocationFrom(shippingRules: List<ShippingRule>): ShippingRule {
return if (shippingRules.isNotEmpty()) {
shippingRules.firstOrNull { it.location()?.country() == this.countryCode() }
?: shippingRules.first()
} else {
ShippingRule.builder().build()
}
// val location = Location.builder()
// .id(23424814)
// .country("FK")
// .displayableName("Falkland Islands")
// .name("Falkland Islands")
// .build()
// return ShippingRule.builder()
// .id(23424814)
// .location(location)
// .build()
Comment on lines +115 to +124
Copy link
Contributor

Choose a reason for hiding this comment

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

dont think we need this comment anymore

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import androidx.fragment.app.Fragment
import com.kickstarter.ui.ArgumentsKey
import com.kickstarter.ui.data.PledgeData
import com.kickstarter.ui.data.PledgeReason
import com.kickstarter.ui.fragments.CrowdfundCheckoutFragment
import com.kickstarter.ui.fragments.PledgeFragment

fun Fragment.selectPledgeFragment(
pledgeData: PledgeData,
pledgeReason: PledgeReason
): Fragment {
return PledgeFragment().withData(pledgeData, pledgeReason)
val fragment = if (pledgeReason == PledgeReason.FIX_PLEDGE) {
PledgeFragment()
} else CrowdfundCheckoutFragment()
return fragment.withData(pledgeData, pledgeReason)
}

fun Fragment.withData(pledgeData: PledgeData?, pledgeReason: PledgeReason?): Fragment {
Expand Down
Loading