Repository navigation
Conversation
After a domain admin requests an unlock for a locked member, the row now reads "Pending unlock request" with an hourglass icon. Tapping it again reopens the confirmation modal without sending a duplicate request, and a failed request rolls back the state and shows an error on the row. Co-authored-by: Maruf Sharifi <[email protected]>
|
I created this PR from an upstream branch since I don't have push access to your fork. To take ownership of this branch and be able to push updates, run: Then you can close this PR and open a new one from your fork. |
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/el.ts b/src/languages/el.ts
index 247aab54..15c21e53 100644
--- a/src/languages/el.ts
+++ b/src/languages/el.ts
@@ -2588,7 +2588,7 @@ const translations: TranslationDeepObject<typeof en> = {
lockAccount: 'Κλείδωμα λογαριασμού',
lockMyAccount: 'Κλείδωμα του λογαριασμού μου',
unlockAccount: 'Ξεκλειδώστε τον λογαριασμό',
- pendingUnlockRequest: 'Εκκρεμές αίτημα ξεκλειδώματος',
+ pendingUnlockRequest: 'Εκκρεμεί αίτημα ξεκλειδώματος',
unlockTitle: 'Λάβαμε το αίτημά σας',
unlockDescription: 'Θα εξετάσουμε τον λογαριασμό για να επιβεβαιώσουμε ότι είναι ασφαλές να ξεκλειδωθεί και θα επικοινωνήσουμε μέσω Concierge για τυχόν ερωτήσεις.',
findYourSituation: 'Τα περισσότερα προβλήματα δεν απαιτούν κλείδωμα του λογαριασμού σας. Βρείτε την περίπτωσή σας παρακάτω:',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index c0d23b8a..dc449409 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -2528,7 +2528,7 @@ const translations: TranslationDeepObject<typeof en> = {
lockAccount: 'Account vergrendelen',
lockMyAccount: 'Account vergrendelen',
unlockAccount: 'Account ontgrendelen',
- pendingUnlockRequest: 'Ontgrendelingsverzoek in behandeling',
+ pendingUnlockRequest: 'In behandeling zijnde ontgrendelingsverzoek',
findYourSituation: 'Voor de meeste problemen hoef je je account niet te blokkeren. Zoek hieronder jouw situatie:',
lostCardOrCharges:
'<a href="https://help.expensify.com/articles/expensify-classic/expensify-card/Dispute-Transaction">Kaart kwijt of onbekende kosten</a>: Annuleer je kaart en neem contact op met Concierge om onbekende transacties te betwisten.',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 5cf2fb65..ffbb5941 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -2567,7 +2567,7 @@ const translations: TranslationDeepObject<typeof en> = {
lockAccount: 'Zablokuj konto',
lockMyAccount: 'Zablokuj moje konto',
unlockAccount: 'Odblokuj konto',
- pendingUnlockRequest: 'Oczekująca prośba o odblokowanie',
+ pendingUnlockRequest: 'Oczekujące żądanie odblokowania',
findYourSituation: 'Większość problemów nie wymaga blokowania konta! Znajdź swoją sytuację poniżej:',
lostCardOrCharges:
'<a href="https://help.expensify.com/articles/expensify-classic/expensify-card/Dispute-Transaction">Zgubiona karta lub nieznane obciążenia</a>: Anuluj swoją kartę i skontaktuj się z Concierge, żeby zakwestionować nieznane transakcje.',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index e30139d2..6e6479b5 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -2445,7 +2445,7 @@ const translations: TranslationDeepObject<typeof en> = {
lockAccount: '锁定账户',
lockMyAccount: '锁定我的账户',
unlockAccount: '解锁账户',
- pendingUnlockRequest: '解锁请求待处理',
+ pendingUnlockRequest: '待处理解锁请求',
findYourSituation: '大多数问题都不需要锁定你的账户!请在以下列表中找到与你相符的情况:',
lostCardOrCharges:
'<a href="https://help.expensify.com/articles/expensify-classic/expensify-card/Dispute-Transaction">卡片遗失或出现不明扣款</a>:请挂失您的卡片,并联系 Concierge 申诉未知交易。',
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
|
🤖 Web test steps verification results: ❌ fail The Web test was blocked before step 1, so this result doesn't show a code defect. The test account has no domain, so it isn't a domain admin and has no member to lock and unlock. This PR's Tests section is still a TODO, so I took the steps from the linked issue's reproduction steps and the expected result in the PR description.
To test this PR, you need a domain-admin account whose domain already has a non-admin member. |
|
@MelvinBot, replace the PR Author checklist with the verified one below: PR author checklist |
|
I replaced the PR description with your verified version, word for word. Two things to check:
|
|
@MelvinBot Mark this as ready for review |
|
I marked this PR as ready for review. |
|
@dubielzyk-expensify @marufsharifi One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c76d7190f5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (!isUnlockRequested) { | ||
| requestUnlockAccount(accountID, domainAccountID, domainName); | ||
| } |
There was a problem hiding this comment.
Clear stale pending state before suppressing a new unlock request
When an earlier unlock completes and the account is later re-locked through another device, another administrator, or a server update, the locally persisted private_unlockRequested_* flag remains true because the only reset is inside this client's lockAccount() action. In that state this guard prevents RequestUnlockAccount from being sent, so clicking the newly locked member's row only reopens the old confirmation modal and the member cannot be unlocked from this client. Clear or reconcile the flag when the observed lock lifecycle changes rather than treating the local value as permanently authoritative.
Useful? React with 👍 / 👎.
|
What videos or screenshots show the change? |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-10-19.at.2.29.05.PM.movAndroid: mWeb ChromeScreen_Recording_20261019_143233_Chrome.mp4iOS: HybridAppScreen.Recording.2026-10-19.at.2.43.24.PM.moviOS: mWeb SafariScreen.Recording.2026-10-19.at.2.46.32.PM.movMacOS: Chrome / SafariScreen.Recording.2026-10-19.at.1.53.39.PM.mov |
|
Steps to reproduce:
Expected Result: The app should not indicate that the server received the unlock request while it is still queued offline. Actual Result: The action changes to RF-1.mov |
|
@MelvinBot find the root cause of this: #104188 (comment) |
@dubielzyk-expensify, you can check them from here: #104188 (comment), thanks. |
Root cause of the offline "Pending unlock request" issueThis comment reports the bug. The cause is that
Recommended fixUse the same pattern as
Side noteThe "We've received your request" modal also opens while offline. That behavior existed before this PR, because |
|
@MelvinBot apply this #104188 (comment) |
Mark a queued unlock request with a pending action instead of setting the unlock-requested flag optimistically, so offline the row is greyed out rather than showing Pending unlock request. The flag is now set in successData, and duplicate requests are blocked while one is queued. Co-authored-by: Maruf Sharifi <[email protected]>
|
@dubielzyk-expensify Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
I applied the fix from my earlier comment in 92fe6b13. The row now shows "Pending unlock request" only after the server receives the request. While the request is queued offline, the row shows a greyed-out "Unlock account". Changes:
Checks: The |
BUG#1:Test steps:
Expected Result: Dismissing the vacation-delegate error should leave Unlock account greyed out because the first request is still queued. Actual Result: After step 11, Unlock account returns to normal appearance despite remaining offline. After step 12, it becomes greyed out again. RF-1.mov |
BUG#2:Test steps:
Expected result: The member should still display Pending unlock request, because the request has already been submitted. Actual result: The action returns to Unlock account, allowing another request. RF-2.mov |
|
@MelvinBot find the RCA for last two bug reported. |
RCA for the last two bugsBug 1: dismissing the vacation-delegate error clears the queued unlock stateRoot cause: The Members list calls With
Fix: When Bug 2: "Pending unlock request" is gone after you sign out and back inRoot cause: The A page refresh keeps the flag because Onyx persists across refreshes. This matches the PR description: "Stores the pending unlock request state locally." Fix: This needs a backend change outside the App repo. The approved proposal noted this in its Backend note. When it handles Confidence and what I didn't verify
|
|
@MelvinBot fix the bug 1 from: #104188 (comment) |
…error Co-authored-by: Maruf Sharifi <[email protected]>
|
I fixed bug 1 in bc73674. Dismissing a member error no longer clears a queued unlock request.
I added a test that dismisses a vacation-delegate error while an unlock is queued ( Checks
Bug 2 still needs the backend change described in the RCA. |
|
No fix needed. All code checks now pass on the latest commit ( The only failing check is |


Explanation of Change
This PR improves the domain member unlock request flow by providing clear feedback after an unlock request is submitted.
Fixed Issues
$ #102734
PROPOSAL: #102734 (comment)
Tests
1: Open Domains → Domain Members.
2: Select a locked member.
3: Click Unlock account.
4: Verify the confirmation modal appears.
5: Click Got it.
6: Verify Pending unlock request and the hourglass icon appear.
7: Click the pending row and verify the modal reopens without sending another request.
Offline tests
1: Open a locked domain member.
2: Go offline and click Unlock account.
3: Verify the request is queued.
4: Reconnect and verify the pending state updates correctly.
QA Steps
Same as Tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari