Conversation
* Implement the new card in the Profile screen for the subscription * Forecast in tab in station details + in forecast details (#807) * WIP of forecast tab's state & prompt. Billing library missing to check state. * Integrate the billing library, create functionality for initializing it and fetching if we have an active sub or not and update the UI respectively * Handle UI in Forecast Details if premium is available or not
* Implement the new card in the Profile screen for the subscription (#805) * Implement the new card in the Profile screen for the subscription * Forecast in tab in station details + in forecast details (#807) * WIP of forecast tab's state & prompt. Billing library missing to check state. * Integrate the billing library, create functionality for initializing it and fetching if we have an active sub or not and update the UI respectively * Handle UI in Forecast Details if premium is available or not * Create the initial manage subscription screen * Manage subscription + plans screen * Launch billing flow, handle purchases * Update UI of the purchase update state + support of remote configurable banner * Finalize the UI * Fixes * Fix detekt + unit tests * Fixes on when clearing the shared flow * Add some view content events * UI fixes + add "powered by Meteoblue banner" * Update gradle version * Fix the actions * Fixed the check of free premium trial available * Fixes on logged out state in forecast tab * Handle login state in Manage Subscription screen * Handle tabs coloring in device details * Fix text on success button in the billing flow * Fixes
…emium in the response
…w the corner radius and make it customizable
… with gradient when premium
…to better adjust the border when in hyperlocal tab
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/weatherxm/ui/managesubscription/PremiumPlanView.kt (1)
52-359:⚠️ Potential issue | 🟡 MinorSplit
PremiumPlanViewinto smaller composables to reduce cyclomatic complexity.
PremiumPlanViewis at complexity 17 (threshold 15), so this currently fails Detekt. Extracting header, promo banners, pricing, and feature list into private composables should resolve this cleanly.Refactor outline
fun PremiumPlanView(...) { - // full UI with header + badges + promo + pricing + features + debug label + PremiumHeader(isCurrentPlan = isCurrentPlan, primaryColor = primaryColor) + PremiumPromoSection(sub = sub, hasFreeTrialAvailable = hasFreeTrialAvailable) + PremiumPricingSection(sub = sub) + PremiumDescriptionAndFeatures() + DebugOfferId(sub = sub) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/weatherxm/ui/managesubscription/PremiumPlanView.kt` around lines 52 - 359, PremiumPlanView exceeds Detekt's cyclomatic complexity threshold (17 > 15); split it into smaller private composables to reduce complexity: extract the header row (including LargeText and the current-plan / best-accuracy badge) into a private HeaderComposable, extract the free-trial token banner (logic using TAG_FREE_TRIAL, freeTrialPeriod) into FreeTrialBanner, extract the launch offer title block (TAG_LAUNCH_OFFER) into LaunchOfferTitle, extract the pricing section (uses sub.price, PLAN_MONTHLY/PLAN_YEARLY, TAG_DISCOUNT, discountedCycles, basePrice) into PricingComposable, and extract the features list (uses PremiumFeatureItem and annotated strings) into FeaturesComposable; replace the corresponding inline blocks in PremiumPlanView with calls to these new private composables and pass only the needed parameters (sub, isCurrentPlan, hasFreeTrialAvailable, isSelected) to keep each function focused and reduce overall cyclomatic complexity.
♻️ Duplicate comments (5)
app/src/main/java/com/weatherxm/ui/managesubscription/PremiumPlanView.kt (2)
225-227:⚠️ Potential issue | 🟡 MinorWrap
toIntOrNull()to a new line in the nullable chain.Line 226 still violates
CascadingCallWrapping.Proposed fix
val freeTrialMonths = sub.freeTrialPeriod - ?.filter { it.isDigit() }?.toIntOrNull() ?: 2 + ?.filter { it.isDigit() } + ?.toIntOrNull() ?: 2🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/weatherxm/ui/managesubscription/PremiumPlanView.kt` around lines 225 - 227, The nullable chain assigning freeTrialMonths (variable freeTrialMonths using sub.freeTrialPeriod) should place the ?.toIntOrNull() on its own line to satisfy CascadingCallWrapping; refactor the expression so the filter call stays on one line and the subsequent ?.toIntOrNull() is moved to a new line before the elvis fallback (?: 2), e.g. keep sub.freeTrialPeriod?.filter { it.isDigit() } on one line and put ?.toIntOrNull() on the next line to preserve nullable chaining and fix the lint violation.
298-303:⚠️ Potential issue | 🟡 MinorBreak the long discount condition into a multiline boolean.
Line 298 still exceeds max line length and fails Detekt.
Proposed fix
- if (TAG_DISCOUNT in sub.tags && sub.discountedCycles != null && sub.basePrice != null) { + val hasDiscount = TAG_DISCOUNT in sub.tags && + sub.discountedCycles != null && + sub.basePrice != null + if (hasDiscount) { val discountStringRes = if (TAG_FREE_TRIAL in sub.tags) { R.string.offer_discount_after_trial } else {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/weatherxm/ui/managesubscription/PremiumPlanView.kt` around lines 298 - 303, The long boolean condition in PremiumPlanView.kt (the if checking TAG_DISCOUNT in sub.tags && sub.discountedCycles != null && sub.basePrice != null) exceeds max line length; refactor it into a multiline boolean by grouping each predicate on its own line (e.g., split "TAG_DISCOUNT in sub.tags", "sub.discountedCycles != null", and "sub.basePrice != null" across multiple lines with proper parentheses) and keep the nested TAG_FREE_TRIAL check for discountStringRes unchanged so the logic remains identical.app/src/test/java/com/weatherxm/ui/forecastdetails/ForecastDetailsViewModelTest.kt (1)
260-260:⚠️ Potential issue | 🟡 MinorShorten or wrap the test description string to satisfy max line length.
Line 260 still exceeds the configured line length limit.
Proposed fix
- then("LiveData onDeviceDefaultForecast should post the error for the empty forecast") { + then("LiveData onDeviceDefaultForecast should post error for empty forecast") { viewModel.onDeviceDefaultForecast().isError(emptyForecastMsg) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/test/java/com/weatherxm/ui/forecastdetails/ForecastDetailsViewModelTest.kt` at line 260, The test description string in ForecastDetailsViewModelTest (the then(...) call in ForecastDetailsViewModelTest.kt) is too long; shorten or wrap it to satisfy line-length limits by replacing the current message ("LiveData onDeviceDefaultForecast should post the error for the empty forecast") with a shorter phrase (e.g., "LiveData onDeviceDefaultForecast posts error for empty forecast") or split it across concatenated strings so the then("...") argument no longer exceeds the max line length.app/src/main/java/com/weatherxm/service/BillingService.kt (2)
237-239:⚠️ Potential issue | 🟡 MinorWrap the Elvis
return@forEachon a separate line in the call chain.Line 239 still violates
CascadingCallWrappingand is a current pipeline blocker.Proposed fix
val displayPrice = (discountPhase ?: basePhase) ?.formattedPrice - ?.replaceLast(" ", "") ?: return@forEach + ?.replaceLast(" ", "") + ?: return@forEach🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/weatherxm/service/BillingService.kt` around lines 237 - 239, The Elvis operator's fallback "return@forEach" is currently on the same line as the call chain, which violates CascadingCallWrapping; move the "?: return@forEach" onto its own line after the chained calls so the chain ends on its own line — locate the expression that builds displayPrice (using discountPhase, basePhase, formattedPrice and the call to replaceLast) and place the Elvis operator and return@forEach on a separate line to satisfy the wrapping rule.
216-224:⚠️ Potential issue | 🟡 MinorBreak the debug Timber logs into wrapped multiline strings.
Line 219 and Line 223 still exceed the max line length and are currently failing Detekt.
Proposed fix
if (BuildConfig.DEBUG) { productDetails.also { - Timber.d("Product [id=${it?.productId}, name = ${it?.name}, description = ${it?.description}, title = ${it?.title}]]") + Timber.d( + "Product [id=${it?.productId}, name=${it?.name}, " + + "description=${it?.description}, title=${it?.title}]" + ) it?.subscriptionOfferDetails?.forEach { o -> Timber.d("\tOffer [id=${o.offerId}, tags = ${o.offerTags}]") o.pricingPhases.pricingPhaseList.forEach { p -> - Timber.d("\t\tPhase [pricef = ${p.formattedPrice}, period = ${p.billingPeriod}, cycles = ${p.billingCycleCount}, recur = ${p.recurrenceMode}], price = ${p.priceAmountMicros}, curr = ${p.priceCurrencyCode}]") + Timber.d( + "\t\tPhase [pricef=${p.formattedPrice}, period=${p.billingPeriod}, " + + "cycles=${p.billingCycleCount}, recur=${p.recurrenceMode}], " + + "price=${p.priceAmountMicros}, curr=${p.priceCurrencyCode}]" + ) } } } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/weatherxm/service/BillingService.kt` around lines 216 - 224, The Timber debug logs in the productDetails block (calls to Timber.d within the if (BuildConfig.DEBUG) and the nested subscriptionOfferDetails/pricingPhases loops) exceed max line length; refactor each Timber.d call to use wrapped multiline/raw strings (e.g., Kotlin triple-quoted strings or build a small StringBuilder/format block) and interpolate the same fields (productId, name, description, title, o.offerId, o.offerTags, p.formattedPrice, p.billingPeriod, p.billingCycleCount, p.recurrenceMode, p.priceAmountMicros, p.priceCurrencyCode) across multiple shorter lines so no single line exceeds the limit while preserving the exact logged values and structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@app/src/main/java/com/weatherxm/ui/managesubscription/PremiumPlanView.kt`:
- Around line 52-359: PremiumPlanView exceeds Detekt's cyclomatic complexity
threshold (17 > 15); split it into smaller private composables to reduce
complexity: extract the header row (including LargeText and the current-plan /
best-accuracy badge) into a private HeaderComposable, extract the free-trial
token banner (logic using TAG_FREE_TRIAL, freeTrialPeriod) into FreeTrialBanner,
extract the launch offer title block (TAG_LAUNCH_OFFER) into LaunchOfferTitle,
extract the pricing section (uses sub.price, PLAN_MONTHLY/PLAN_YEARLY,
TAG_DISCOUNT, discountedCycles, basePrice) into PricingComposable, and extract
the features list (uses PremiumFeatureItem and annotated strings) into
FeaturesComposable; replace the corresponding inline blocks in PremiumPlanView
with calls to these new private composables and pass only the needed parameters
(sub, isCurrentPlan, hasFreeTrialAvailable, isSelected) to keep each function
focused and reduce overall cyclomatic complexity.
---
Duplicate comments:
In `@app/src/main/java/com/weatherxm/service/BillingService.kt`:
- Around line 237-239: The Elvis operator's fallback "return@forEach" is
currently on the same line as the call chain, which violates
CascadingCallWrapping; move the "?: return@forEach" onto its own line after the
chained calls so the chain ends on its own line — locate the expression that
builds displayPrice (using discountPhase, basePhase, formattedPrice and the call
to replaceLast) and place the Elvis operator and return@forEach on a separate
line to satisfy the wrapping rule.
- Around line 216-224: The Timber debug logs in the productDetails block (calls
to Timber.d within the if (BuildConfig.DEBUG) and the nested
subscriptionOfferDetails/pricingPhases loops) exceed max line length; refactor
each Timber.d call to use wrapped multiline/raw strings (e.g., Kotlin
triple-quoted strings or build a small StringBuilder/format block) and
interpolate the same fields (productId, name, description, title, o.offerId,
o.offerTags, p.formattedPrice, p.billingPeriod, p.billingCycleCount,
p.recurrenceMode, p.priceAmountMicros, p.priceCurrencyCode) across multiple
shorter lines so no single line exceeds the limit while preserving the exact
logged values and structure.
In `@app/src/main/java/com/weatherxm/ui/managesubscription/PremiumPlanView.kt`:
- Around line 225-227: The nullable chain assigning freeTrialMonths (variable
freeTrialMonths using sub.freeTrialPeriod) should place the ?.toIntOrNull() on
its own line to satisfy CascadingCallWrapping; refactor the expression so the
filter call stays on one line and the subsequent ?.toIntOrNull() is moved to a
new line before the elvis fallback (?: 2), e.g. keep sub.freeTrialPeriod?.filter
{ it.isDigit() } on one line and put ?.toIntOrNull() on the next line to
preserve nullable chaining and fix the lint violation.
- Around line 298-303: The long boolean condition in PremiumPlanView.kt (the if
checking TAG_DISCOUNT in sub.tags && sub.discountedCycles != null &&
sub.basePrice != null) exceeds max line length; refactor it into a multiline
boolean by grouping each predicate on its own line (e.g., split "TAG_DISCOUNT in
sub.tags", "sub.discountedCycles != null", and "sub.basePrice != null" across
multiple lines with proper parentheses) and keep the nested TAG_FREE_TRIAL check
for discountStringRes unchanged so the logic remains identical.
In
`@app/src/test/java/com/weatherxm/ui/forecastdetails/ForecastDetailsViewModelTest.kt`:
- Line 260: The test description string in ForecastDetailsViewModelTest (the
then(...) call in ForecastDetailsViewModelTest.kt) is too long; shorten or wrap
it to satisfy line-length limits by replacing the current message ("LiveData
onDeviceDefaultForecast should post the error for the empty forecast") with a
shorter phrase (e.g., "LiveData onDeviceDefaultForecast posts error for empty
forecast") or split it across concatenated strings so the then("...") argument
no longer exceeds the max line length.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
app/src/main/java/com/weatherxm/service/BillingService.ktapp/src/main/java/com/weatherxm/ui/common/UIModels.ktapp/src/main/java/com/weatherxm/ui/managesubscription/PremiumPlanView.ktapp/src/test/java/com/weatherxm/ui/forecastdetails/ForecastDetailsViewModelTest.kt
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/com/weatherxm/ui/common/UIModels.kt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Coverage Report (Type: Instructions)
|
No description provided.