Skip to content

[PM-40534] feat: Restrict Send creation flow when only one Send type is allowed - #7249

Merged
andrebispo5 merged 10 commits into
PM-40530-hide-hide-my-email-switch-when-enforcedfrom
PM-40534-restrict-send-creation-when-one-type-allowed
Aug 12, 2026
Merged

[PM-40534] feat: Restrict Send creation flow when only one Send type is allowed#7249
andrebispo5 merged 10 commits into
PM-40530-hide-hide-my-email-switch-when-enforcedfrom
PM-40534-restrict-send-creation-when-one-type-allowed

Conversation

@andrebispo5

@andrebispo5 andrebispo5 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

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

📔 Objective

When an org's SendControls policy only allows one Send type, tapping "+" now goes straight to that type's editor instead of asking Text or File first. Picking from a list of one isn't much of a choice.

The skip runs through the same checks the picker did, so nothing gets bypassed on the way through. A File-only org on a free account still gets the "Premium required" dialog rather than landing in an editor it can't use.

The "Types" filter rows on the Send list are hidden in that case too, since filtering by category only makes sense when there's more than one.

With both types allowed, no restriction, or the flag off, everything behaves as it did. That falls out on its own: allowedSendTypes is null in all of those cases, so there's no extra flag check anywhere in this change.

Also plumbs allowedSendTypes through to the Send screen, which PM-40528 hadn't done yet, and adds the SendTypeJson to SendItemType mapper that didn't exist.

Stacked on #7239 (PM-40528), so this targets that branch rather than main.

📸 Screenshots

Screen.Recording.2026-08-06.at.18.51.22.mov

@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context t:feature Change Type - Feature Development labels Aug 7, 2026
@andrebispo5
andrebispo5 marked this pull request as ready for review August 7, 2026 09:00
@andrebispo5
andrebispo5 requested review from a team and david-livefront as code owners August 7, 2026 09:01
Copilot AI review requested due to automatic review settings August 7, 2026 09:01
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the Send creation flow restriction: the new singleAllowedSendType derivation from EffectiveSendPolicy, the shared navigateToNewSendOrShowDialog path that keeps the disable-send and premium checks intact when the picker is skipped, the conditional Types section in SendContent, and the new SendTypeJson.toSendItemType() mapper. The policy flow now drives both policyDisablesSend and singleAllowedSendType, so state stays correct if the policy changes while the screen is open, and the existing AddSendSelected behavior is unchanged. Test coverage matches the new branches (both types allowed, text-only skip, file-only premium and disable-send dialogs, live policy updates, and the mapper). No security, correctness, or breaking-change issues found.

Code Review Details
  • ❓ : Empty allowedSendTypes list is treated the same as null (no restriction) by singleOrNull()
    • app/src/main/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt:937

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.41270% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 84.47%. Comparing base (f1a561e) to head (9808658).

Files with missing lines Patch % Lines
...t/bitwarden/ui/tools/feature/send/SendViewModel.kt 96.29% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@                                 Coverage Diff                                  @@
##           PM-40530-hide-hide-my-email-switch-when-enforced    #7249      +/-   ##
====================================================================================
- Coverage                                             85.61%   84.47%   -1.15%     
====================================================================================
  Files                                                   965     1114     +149     
  Lines                                                 67288    68737    +1449     
  Branches                                               9836     9923      +87     
====================================================================================
+ Hits                                                  57611    58068     +457     
- Misses                                                 6165     7150     +985     
- Partials                                               3512     3519       +7     
Flag Coverage Δ
app-data 17.67% <0.00%> (+0.14%) ⬆️
app-ui-auth-tools 18.74% <98.41%> (+0.05%) ⬆️
app-ui-platform 17.13% <0.00%> (+0.65%) ⬆️
app-ui-vault 27.90% <0.00%> (-0.05%) ⬇️
authenticator 6.09% <0.00%> (-0.01%) ⬇️
lib-core-network-bridge 4.10% <0.00%> (-0.01%) ⬇️
lib-data-ui 1.20% <0.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Updates the Send creation UX so that when an org’s SendControls policy restricts Send creation to a single type (Text or File), tapping “+” skips the type picker and routes directly through the same restriction checks that would have applied after a picker selection. It also hides the “Types” filter section on the Send list when filtering is meaningless (only one allowed type).

Changes:

  • Add a SendTypeJson -> SendItemType mapper and unit tests for both SendType and SendTypeJson mappings.
  • Teach SendViewModel to derive a singleAllowedSendType from EffectiveSendPolicy.allowedSendTypes and use it to (a) skip the type picker on “+” and (b) control whether the Types filter section is shown.
  • Update Compose UI to conditionally render the Types filter section, with Compose test coverage.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/util/SendTypeExtensionsTest.kt Adds coverage for toSendItemType() mapping for both SendType and SendTypeJson.
app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt Adds tests for “+” behavior under single-allowed-type policies and for live policy updates affecting UI state.
app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendScreenTest.kt Verifies the Types filter section is shown/hidden based on singleAllowedSendType.
app/src/main/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/util/SendTypeExtensions.kt Introduces SendTypeJson.toSendItemType() mapping.
app/src/main/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt Implements single-allowed-type derivation, skips picker on “+” when applicable, and threads state into UI.
app/src/main/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendScreen.kt Plumbs shouldShowTypesSection into SendContent.
app/src/main/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendContent.kt Conditionally renders the Types filter header + rows based on shouldShowTypesSection.

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

Comment on lines +937 to +940
private val EffectiveSendPolicy.singleAllowedSendType: SendItemType?
get() = allowedSendTypes
?.singleOrNull()
?.toSendItemType()

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.

QUESTION: How should an empty allowedSendTypes list be treated?

Details

singleOrNull() returns null for both an empty list and a list with 2+ entries, so allowedSendTypes = [] is handled identically to null (no restriction): the type picker is shown and both Text and File remain creatable.

If the server can ever emit [] for a SendControls policy where the admin selected no types, this would be a permissive fallback. If [] is not reachable (or genuinely means "unrestricted"), no change is needed — just confirming the intent, since the KDoc only documents the null case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Server always sends the list with at least one type. It is required.

@andrebispo5
andrebispo5 changed the base branch from PM-40528-send-controls-precedence-enforcement to PM-40530-hide-hide-my-email-switch-when-enforced August 11, 2026 09:16
…PM-40534-restrict-send-creation-when-one-type-allowed
…PM-40534-restrict-send-creation-when-one-type-allowed
…PM-40534-restrict-send-creation-when-one-type-allowed
)
val viewModel = createViewModel()
viewModel.trySendAction(SendAction.AddSendClick)
assertEquals(

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.

Compare whole state

viewModel.trySendAction(SendAction.AddSendClick)
assertEquals(SendEvent.NavigateNewSend(sendType = SendItemType.TEXT), awaitItem())
}
assertNull(viewModel.stateFlow.value.dialogState)

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.

Compare whole state

}
}

@Suppress("MaxLineLength")

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.

this does not need this Suppress


val updatedState = awaitItem()
assertEquals(SendItemType.FILE, updatedState.singleAllowedSendType)
assertEquals(false, updatedState.shouldShowTypesSection)

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.

Compare whole state

title = null,
message = BitwardenString.send_disabled_warning.asText(),
),
viewModel.stateFlow.value.dialogState,

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.

Compare the state not the dialogState


viewModel.trySendAction(SendAction.AddSendClick)

assertEquals(

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.

assert the state not the dialogState

@andrebispo5
andrebispo5 requested a review from aj-rosado August 12, 2026 15:56
Comment on lines +233 to +245
assertEquals(DEFAULT_STATE, initialState)
assertEquals(true, initialState.shouldShowTypesSection)

mutableEffectiveSendPolicyFlow.value = DEFAULT_EFFECTIVE_SEND_POLICY.copy(
allowedSendTypes = listOf(SendTypeJson.FILE),
)

val updatedState = awaitItem()
assertEquals(
DEFAULT_STATE.copy(singleAllowedSendType = SendItemType.FILE),
updatedState,
)
assertEquals(false, updatedState.shouldShowTypesSection)

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.

This is still comparing the individual properties

)
}

@Suppress("MaxLineLength")

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.

is this suppress necessary?

)
}

@Suppress("MaxLineLength")

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.

is this suppress necessary?

@aj-rosado aj-rosado left a comment

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.

LGTM

@andrebispo5
andrebispo5 added this pull request to the merge queue Aug 12, 2026
Merged via the queue into main with commit 1ef5568 Aug 12, 2026
26 checks passed
@andrebispo5
andrebispo5 deleted the PM-40534-restrict-send-creation-when-one-type-allowed branch August 12, 2026 17:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app:password-manager Bitwarden Password Manager app context t:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants