Skip to content

[PM-34898] feat: Self-hosted support for PremiumUpgradeView#2558

Merged
andrebispo5 merged 7 commits into
mainfrom
pm-34898/selfhost-upgrade-premium
Apr 22, 2026
Merged

[PM-34898] feat: Self-hosted support for PremiumUpgradeView#2558
andrebispo5 merged 7 commits into
mainfrom
pm-34898/selfhost-upgrade-premium

Conversation

@andrebispo5
Copy link
Copy Markdown
Contributor

@andrebispo5 andrebispo5 commented Apr 20, 2026

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-34898

📔 Objective

When the user is on a self-hosted server, the Premium Upgrade screen now shows a different layout:

  • Info banner: A dismissable ActionCard banner above the content card informing the user to manage their Premium subscription via the web vault on a computer.
  • Header: Replaces the price section with a single headline-style title ("Unlock more advanced features with a Premium plan.").
  • Hidden controls: The "Upgrade now" button and Stripe footer are hidden since self-hosted users cannot purchase through the app.

Additionally, ActionCard was updated to support an optional title (String?), allowing cards with only a message and no title.

📸 Screenshots

Screen.Recording.2026-04-20.at.10.38.14.mov

@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context app:authenticator Bitwarden Authenticator app context t:feature labels Apr 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 20, 2026

Logo
Checkmarx One – Scan Summary & Details1a8d2fff-34b7-4043-b5ae-71a282b21203

Great job! No new security vulnerabilities introduced in this pull request

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

❌ Patch coverage is 88.79310% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.08%. Comparing base (df3afa9) to head (df985c7).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
...miumUpgrade/PremiumUpgradeView+SnapshotTests.swift 0.00% 9 Missing ⚠️
...UI/Billing/PremiumUpgrade/PremiumUpgradeView.swift 89.47% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2558      +/-   ##
==========================================
- Coverage   87.16%   86.08%   -1.09%     
==========================================
  Files        1883     2115     +232     
  Lines      166523   181478   +14955     
==========================================
+ Hits       145150   156219   +11069     
- Misses      21373    25259    +3886     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@andrebispo5 andrebispo5 marked this pull request as ready for review April 20, 2026 14:09
Copilot AI review requested due to automatic review settings April 20, 2026 14:09
@andrebispo5 andrebispo5 requested review from a team and matt-livefront as code owners April 20, 2026 14:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds self-hosted-aware UI behavior to the Premium upgrade flow so users on self-hosted environments are guided to manage subscriptions via the web vault, and updates ActionCard to support message-only cards.

Changes:

  • Add a dismissible self-hosted info banner and hide in-app purchase controls (Upgrade button + Stripe footer) when self-hosted.
  • Introduce isSelfHosted / isBannerDismissed state + processor logic to derive self-hosted behavior.
  • Update ActionCard to allow an optional title and add coverage for the message-only variant.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeView.swift Conditionally shows self-hosted banner and hides purchase UI; triggers .appeared effect.
BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeView+ViewInspectorTests.swift Adds ViewInspector coverage for banner visibility and self-hosted button hiding.
BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeView+SnapshotTests.swift Adds (disabled) snapshot entries for self-hosted states.
BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeState.swift Adds self-hosted + banner dismissal state and a computed showSelfHostedBanner.
BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeProcessorTests.swift Adds tests for .appeared self-host detection and banner dismissal action.
BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeProcessor.swift Reads environment region on .appeared; handles banner dismissal.
BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeEffect.swift Adds .appeared effect.
BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeAction.swift Adds .dismissBannerTapped action.
BitwardenShared/UI/Billing/BillingCoordinator.swift Expands coordinator services to include HasEnvironmentService.
BitwardenResources/Localizations/en.lproj/Localizable.strings Adds banner message localization key/value.
BitwardenKit/UI/Platform/Application/Views/ActionCard.swift Makes title optional and hides it when nil.
BitwardenKit/UI/Platform/Application/Views/ActionCard+ViewInspectorTests.swift Adds test for the nil-title/message-only rendering case.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 47 to 52
override func perform(_ effect: PremiumUpgradeEffect) async {
switch effect {
case .appeared:
state.isSelfHosted = services.environmentService.region == .selfHosted
case .upgradeNowTapped:
await createCheckoutSession()
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

isSelfHosted is only set inside the async .appeared effect. Since the view renders with the default isSelfHosted = false before the .task runs, self-hosted users will initially see the cloud layout (price section + Upgrade button + Stripe footer) and then have it removed once .appeared updates state. Consider initializing state.isSelfHosted synchronously in init (or when constructing PremiumUpgradeState) using services.environmentService.region, and then either removing the .appeared effect or keeping it only if you expect the region to change while the view is on-screen.

Copilot uses AI. Check for mistakes.
processor.state.isSelfHosted = true
XCTAssertThrowsError(
try subject.inspect().find(asyncButton: Localizations.upgradeNow),
)
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

The self-hosted layout change hides both the Upgrade button and the Stripe footer, but the current test only asserts the button is missing. Add an assertion (or a separate test) that the Stripe footer text (e.g., Localizations.youllGoToStripeSecureCheckoutToCompleteYourPurchase) is also not present when isSelfHosted = true.

Suggested change
)
)
XCTAssertThrowsError(
try subject.inspect().find(
text: Localizations.youllGoToStripeSecureCheckoutToCompleteYourPurchase,
),
)

Copilot uses AI. Check for mistakes.
Comment thread BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeView.swift Outdated
Comment thread BitwardenResources/Localizations/en.lproj/Localizable.strings Outdated
Comment thread BitwardenShared/UI/Billing/PremiumUpgrade/PremiumUpgradeView.swift Outdated
@andrebispo5 andrebispo5 merged commit c47b3a6 into main Apr 22, 2026
20 checks passed
@andrebispo5 andrebispo5 deleted the pm-34898/selfhost-upgrade-premium branch April 22, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app:authenticator Bitwarden Authenticator app context app:password-manager Bitwarden Password Manager app context t:feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants