Skip to content

fix(clients): delete profile photos through deleteClientProfilePhoto - #2406

Merged
vecchp merged 1 commit into
mainfrom
DEV-warnings/client-photo-delete-mutation
Sep 1, 2026
Merged

vecchp merged 1 commit into
mainfrom
DEV-warnings/client-photo-delete-mutation

Conversation

@vecchp

@vecchp vecchp commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Split out of #2402 so it can ship on its own.

updateClientProfilePhoto is deprecated, but ProfilePhotoModal was still using it as the delete path — sending photo: null — while deleteClientProfilePhoto sat implemented, permissioned and uncalled on main. The modal now uses the latter.

Why separately

Nothing here depends on #2402: deleteClientProfilePhoto has been on main since before it. Keeping the swap inside a large breaking backend PR meant the EAS rollout clock did not start until that PR cleared review — and the backend cannot drop updateClientProfilePhoto (#2405) until installed clients have taken this update.

It also isolates the manual QA. This is the only part of the original PR that needs checking in the app; #2402's backend work is covered by tests.

Worth a manual check

Photo deletion in the app. deleteClientProfilePhoto has backend tests and TypeScript enforces the new variable shape, but this mutation has never run in production. No component test: the delete flows through DeleteModal and would need substantial native-UI mocking for what is a wiring swap.

Verification

  • graphql-codegen regenerated against main's schema; the diff is confined to the swapped mutation
  • tsc --noEmit -p tsconfig.lib.json clean
  • vitest run — 22 files, 172 tests passed

🤖 Generated with Claude Code

Summary by Sourcery

Bug Fixes:

  • Route profile photo deletion through the dedicated deleteClientProfilePhoto mutation instead of the deprecated update mutation.

`updateClientProfilePhoto` is deprecated, but `ProfilePhotoModal` was still using
it as the delete path by sending `photo: null`, while `deleteClientProfilePhoto`
sat implemented, permissioned and uncalled. The modal now uses the latter.

This ships on its own so installed clients start picking it up immediately. The
backend cannot drop `updateClientProfilePhoto` until they have.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

The PR reroutes profile-photo deletion from the deprecated updateClientProfilePhoto mutation, which sent a null photo payload, to the existing dedicated deleteClientProfilePhoto mutation. The GraphQL operation, generated types/metadata, and ProfilePhotoModal mutation wiring are updated, while the existing profile refetch and error handling remain in place.

Sequence diagram for profile photo deletion

sequenceDiagram
    actor User
    participant ProfilePhotoModal
    participant GraphQLAPI
    participant ClientProfile

    User->>ProfilePhotoModal: deleteFile()
    ProfilePhotoModal->>GraphQLAPI: deleteClientProfilePhoto(clientProfileId)
    GraphQLAPI-->>ProfilePhotoModal: OperationInfo
    ProfilePhotoModal->>GraphQLAPI: refetch ClientProfileDocument(id)
    GraphQLAPI-->>ClientProfile: updated profile without photo
Loading

File-Level Changes

Change Details Files
Replace the deprecated photo-update mutation with the dedicated profile-photo deletion mutation in the GraphQL operation and modal wiring.
  • Define DeleteClientProfilePhoto with a clientProfileId variable.
  • Update the modal to invoke the delete mutation and refetch the client profile.
  • Regenerate GraphQL client and metadata artifacts for the operation change.
libs/expo/betterangels/src/lib/screens/Client/Client.graphql
libs/expo/betterangels/src/lib/screens/Client/ClientHeader/ProfilePhotoModal.tsx
libs/expo/betterangels/src/lib/screens/Client/__generated__/Client.generated.ts
libs/expo/betterangels/src/lib/screens/Client/__generated__/Client_meta.generated.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot 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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

vecchp pushed a commit that referenced this pull request Sep 1, 2026
`createClientDocument` was the last multipart upload mutation. The presigned
pipeline replaced it in #2018, which deleted the components that called it, and
nothing has called it since: no `.graphql` document, no generated operation, no
component, and zero occurrences in the compiled mobile bundle — against one
occurrence of `updateClientProfilePhoto` in the same bundle, so the search is
sound rather than a false negative. Its only remaining consumers were backend
test fixtures.

Reviewing the previous commit surfaced that this path was also entirely
unvalidated — no size limit and no content-type allowlist, because
`CLIENT_DOCUMENT_CONFIG` is only ever applied on the presigned side. Rather than
add a validator and an exception class to code nobody reaches, remove the path:

- the `create_client_document` mutation and `CreateClientDocumentInput`
- `create_multipart_attachment`, added in the previous commit and now orphaned
- `python-magic`, whose only two call sites were the deleted `Attachment.save()`
  override and this service

`_create_client_document_fixture` now builds its document through the presigned
mutations, so the ~28 test call sites keep working. Two tests went with the
mutation: `test_create_client_document`, and
`test_create_client_document_permission`, which duplicated
`test_resolve_client_document_uploads_permission` parameter for parameter.

`updateClientProfilePhoto` stays. It is present in the shipped bundle, so it has
to outlive the app taking #2406, which moves photo deletion onto
`deleteClientProfilePhoto`. #2405 removes it once that rollout is confirmed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

🚀 Expo continuous deployment is ready for betterangels!

  • Project → betterangels
  • Environment → Preview
  • Platforms → android, ios
  • Scheme → betterangels
  🤖 Android 🍎 iOS
Runtime Version 4d84f3f87051ebb9c1713e7e2d4522e4b23135d9 4d84f3f87051ebb9c1713e7e2d4522e4b23135d9
Build Details Build Permalink
DetailsDistribution: INTERNAL
Build profile: preview
Runtime version: 4d84f3f87051ebb9c1713e7e2d4522e4b23135d9
App version: 1.2.11
Git commit: a1900145e6c2315da5a85930acd4fcf2f8619aa2
Build Permalink
DetailsDistribution: INTERNAL
Build profile: preview
Runtime version: 4d84f3f87051ebb9c1713e7e2d4522e4b23135d9
App version: 1.2.11
Git commit: a1900145e6c2315da5a85930acd4fcf2f8619aa2
Update Details Update Permalink
DetailsBranch: DEV-warnings-client-photo-delete-mutation
Runtime version: 4d84f3f87051ebb9c1713e7e2d4522e4b23135d9
Git commit: b2796d6b9a5aea56126a3ceb749e2051433ec472
Update Permalink
DetailsBranch: DEV-warnings-client-photo-delete-mutation
Runtime version: 4d84f3f87051ebb9c1713e7e2d4522e4b23135d9
Git commit: b2796d6b9a5aea56126a3ceb749e2051433ec472
Update QR

iOS Simulator Build: Simulator Build Link

vecchp pushed a commit that referenced this pull request Sep 1, 2026
`createClientDocument` was the last multipart upload mutation. The presigned
pipeline replaced it in #2018, which deleted the components that called it, and
nothing has called it since: no `.graphql` document, no generated operation, no
component, and zero occurrences in the compiled mobile bundle — against one
occurrence of `updateClientProfilePhoto` in the same bundle, so the search is
sound rather than a false negative. Its only remaining consumers were backend
test fixtures.

Reviewing the previous commit surfaced that this path was also entirely
unvalidated — no size limit and no content-type allowlist, because
`CLIENT_DOCUMENT_CONFIG` is only ever applied on the presigned side. Rather than
add a validator and an exception class to code nobody reaches, remove the path:

- the `create_client_document` mutation and `CreateClientDocumentInput`
- `create_multipart_attachment`, added in the previous commit and now orphaned
- `python-magic`, whose only two call sites were the deleted `Attachment.save()`
  override and this service

`_create_client_document_fixture` now builds its document through the presigned
mutations, so the ~28 test call sites keep working. Two tests went with the
mutation: `test_create_client_document`, and
`test_create_client_document_permission`, which duplicated
`test_resolve_client_document_uploads_permission` parameter for parameter.

`updateClientProfilePhoto` stays. It is present in the shipped bundle, so it has
to outlive the app taking #2406, which moves photo deletion onto
`deleteClientProfilePhoto`. #2405 removes it once that rollout is confirmed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vecchp
vecchp added this pull request to the merge queue Sep 1, 2026
Merged via the queue into main with commit 8caa810 Sep 1, 2026
7 checks passed
@vecchp
vecchp deleted the DEV-warnings/client-photo-delete-mutation branch September 1, 2026 19:14
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.

3 participants