Skip to content

Conversation

@MoMannn
Copy link
Contributor

@MoMannn MoMannn commented Nov 24, 2025

Description

This PR improves the user experience when revoking Gator permissions by adding optimistic UI feedback.

What is the reason for the change?

When users clicked the "Revoke" button for a Gator permission, there was a noticeable delay before the button appeared disabled and showed "Pending..." text. This created a poor user experience where users might click multiple times, unsure if their action was registered.

What is the improvement/solution?

The solution implements optimistic UI feedback by:

  1. Immediately setting a pending state when the revoke button is clicked
  2. Disabling the button and displaying "Pending..." text right away
  3. Managing the pending state with a 800ms timeout after the revoke operation completes to prevent visual flashing before the transaction confirmation window appears
  4. Properly cleaning up timeouts on component unmount to prevent memory leaks
  5. Immediately clearing the pending state if an error occurs

The implementation uses local component state (pendingRevokeClicks) combined with the existing global pending revocations selector to provide a smooth, responsive user experience.

Open in GitHub Codespaces

Changelog

CHANGELOG entry: Improved responsiveness of revoke button in Gator permissions by adding immediate UI feedback

Manual testing steps

  1. Go to all permissions
  2. Find a permission
  3. Click revoke -> the button should turn to pending revocation right away, before the revoke transaction window shows.

Screenshots/Recordings

Before

Screen.Recording.2025-11-24.at.14.21.47.mov

After

Screen.Recording.2025-11-24.at.14.20.37.mov

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Adds optimistic pending state for Gator permission revocations, disabling the button and showing "Pending..." immediately with timeout cleanup.

  • UI — Gator Permissions
    • review-permissions/review-gator-permissions-page.tsx:
      • Introduces local pending state via pendingRevokeClicks (Set) and revokeTimeoutsRef (Map) with unmount cleanup.
      • Updates handleRevokeClick to optimistically set pending, call revokeGatorPermission, then clear after 800ms; clears immediately on error.
      • Passes hasRevokeBeenClicked to children; simplifies empty-state check to gatorPermissions.length.
    • components/review-gator-permission-item.tsx:
      • Adds optional hasRevokeBeenClicked prop and integrates it into isPendingRevocation alongside global pendingRevocations.
      • Disables Revoke button and switches label to pending when either local or global pending is true.

Written by Cursor Bugbot for commit f675d88. This will update automatically on new commits. Configure here.

@github-actions
Copy link
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbot metamaskbot added the team-delegation MetaMask Delegation Team label Nov 24, 2025
@metamaskbot
Copy link
Collaborator

metamaskbot commented Nov 24, 2025

✨ Files requiring CODEOWNER review ✨

👨‍🔧 @MetaMask/core-extension-ux (2 files, +98 -31)
  • 📁 ui/
    • 📁 components/
      • 📁 multichain/
        • 📁 pages/
          • 📁 gator-permissions/
            • 📁 components/
              • 📄 review-gator-permission-item.tsx +12 -3
            • 📁 review-permissions/
              • 📄 review-gator-permissions-page.tsx +86 -28

@metamaskbot metamaskbot added the INVALID-PR-TEMPLATE PR's body doesn't match template label Nov 24, 2025
@MoMannn MoMannn marked this pull request as ready for review November 24, 2025 13:35
@MoMannn MoMannn requested a review from a team as a code owner November 24, 2025 13:35
@metamaskbot
Copy link
Collaborator

Builds ready [33ffe79]
UI Startup Metrics (1239 ± 121 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup12391009167312113031461
load1049856140011211121264
domContentLoaded1042851139211111061243
domInteractive2614118242192
firstPaint54292140541710241205
backgroundConnect22019629917230248
firstReactRender30195483646
getState3317100123855
initialActions105113
loadScripts82965611281028811012
setupStore1272841422
numNetworkReqs1257720573
BrowserifyPower User HomeuiStartup18021543290719418732194
load95784418991679371392
domContentLoaded94383618821679191378
domInteractive37161894328165
firstPaint64011215303619141269
backgroundConnect20718730615213231
firstReactRender79431301687103
getState17714024524193229
initialActions103112
loadScripts75265315991607291178
setupStore1885392137
numNetworkReqs92642033392194
WebpackStandard HomeuiStartup8126871198978311040
load628557103584635830
domContentLoaded623551102883630824
domInteractive2715120232194
firstPaint23888828164220601
backgroundConnect952541017
firstReactRender27194763235
getState271353103645
initialActions103112
loadScripts620548102282628816
setupStore1143351322
numNetworkReqs1257720574
WebpackPower User HomeuiStartup14351208187915615531701
load6725811103108671960
domContentLoaded6625751099109661954
domInteractive40172184334159
firstPaint24792898153239657
backgroundConnect1574671733
firstReactRender83441091492103
getState14912419914157182
initialActions104112
loadScripts6595731090107659944
setupStore20953112551
numNetworkReqs1446632155184296
FirefoxBrowserifyStandard HomeuiStartup11711038163211212291422
load98288811997110411121
domContentLoaded98188811997210391121
domInteractive53282794060142
firstPaint------
backgroundConnect3422132163667
firstReactRender22175662235
getState96717921
initialActions102012
loadScripts96287111686810221074
setupStore105609924
numNetworkReqs1156414652
BrowserifyPower User HomeuiStartup23951884298624325602865
load1116948159916311261496
domContentLoaded1116942159916311261495
domInteractive11535544110102452
firstPaint------
backgroundConnect962944855114177
firstReactRender83361452196120
getState27376845228446773
initialActions2020327
loadScripts1080932155815510931454
setupStore1237786163116597
numNetworkReqs92602043889190
WebpackStandard HomeuiStartup14391286181710814911679
load1224109114278412861362
domContentLoaded1223109014208312861362
domInteractive58261813478129
firstPaint------
backgroundConnect391999164378
firstReactRender28197393039
getState12692121045
initialActions103022
loadScripts1199107513968012641327
setupStore13687141152
numNetworkReqs1156716660
WebpackPower User HomeuiStartup26312259350623427423070
load13641170190615713841765
domContentLoaded13641169190615713841764
domInteractive1103255310896416
firstPaint------
backgroundConnect1023243766119225
firstReactRender82431381792119
getState24479793211345750
initialActions4151637
loadScripts13301104175315013511671
setupStore1229705157110552
numNetworkReqs905317934113157
📊 Page Load Benchmark Results

Current Commit: 33ffe79 | Date: 11/24/2025

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.03s (±37ms) 🟡 | historical mean value: 1.04s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 718ms (±36ms) 🟢 | historical mean value: 723ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 76ms (±9ms) 🟢 | historical mean value: 78ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.03s 37ms 1.01s 1.32s 1.05s 1.32s
domContentLoaded 718ms 36ms 698ms 990ms 741ms 990ms
firstPaint 76ms 9ms 60ms 144ms 84ms 144ms
firstContentfulPaint 76ms 9ms 60ms 144ms 84ms 144ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 53 Bytes (0%)
  • ui: 760 Bytes (0.01%)
  • common: 20 Bytes (0%)

jeffsmale90
jeffsmale90 previously approved these changes Nov 27, 2025
Copy link
Contributor

@jeffsmale90 jeffsmale90 left a comment

Choose a reason for hiding this comment

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

Looks good - I'm not sure why we need the timeout complexity, but if it's definitely necessary then I'm happy with it as is 🎉🐊

@MoMannn
Copy link
Contributor Author

MoMannn commented Nov 27, 2025

Looks good - I'm not sure why we need the timeout complexity, but if it's definitely necessary then I'm happy with it as is 🎉🐊

Its because of delays in the UI, the transaction window takes up so much time to load that the return statements gets processed the button flicks back to Revoke then the transaction window opens. It causes like a quick flick which is noticeable. So with the delay we make sure it does not happen.

@metamaskbot
Copy link
Collaborator

Builds ready [a6c5876]
UI Startup Metrics (1315 ± 120 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup13151062164312013781565
load104687912949811011244
domContentLoaded103987612789710801233
domInteractive2615137222290
firstPaint56489128342210361213
backgroundConnect22120127812228243
firstReactRender412699134666
getState69251583379148
initialActions107115
loadScripts8256651064958701016
setupStore1574071930
numNetworkReqs41281473430142
BrowserifyPower User HomeuiStartup21071796308324721762650
load1049910173414210481411
domContentLoaded1034904169214110361400
domInteractive36181593132124
firstPaint55310116974209811359
backgroundConnect263205685101239543
firstReactRender62441271566103
getState19013378167202237
initialActions107112
loadScripts82170014771408121185
setupStore21116082537
numNetworkReqs104662865298253
WebpackStandard HomeuiStartup8417281174918681047
load62655487576638827
domContentLoaded62155087175634820
domInteractive2615131232294
firstPaint20689777121201575
backgroundConnect1164871229
firstReactRender37202032737116
getState4815114236294
initialActions103112
loadScripts61854886274632812
setupStore1445081437
numNetworkReqs41291483332140
WebpackPower User HomeuiStartup16661252273524218432095
load7005971304107699940
domContentLoaded6905901298108687933
domInteractive36171773033123
firstPaint32594967218456790
backgroundConnect97862316548527
firstReactRender6647137127385
getState17013124422185202
initialActions103112
loadScripts6875881288107685924
setupStore231071142751
numNetworkReqs103672965898283
FirefoxBrowserifyStandard HomeuiStartup14921188220019416341847
load1149925150312312361390
domContentLoaded1148925150312312361390
domInteractive69311633491142
firstPaint------
backgroundConnect69213354885170
firstReactRender301889123353
getState2610112162673
initialActions203123
loadScripts1114910147311611971331
setupStore30102153324123
numNetworkReqs40281292734122
BrowserifyPower User HomeuiStartup26182030363730127563212
load12061001175715512121580
domContentLoaded12061000175715512111580
domInteractive13340584116120453
firstPaint------
backgroundConnect1282667299134362
firstReactRender6237122146596
getState31557979238402883
initialActions3046523
loadScripts1172985160314811741541
setupStore18217792203226728
numNetworkReqs100603216179241
WebpackStandard HomeuiStartup16701376217016617771975
load13101117161311213611562
domContentLoaded13091117161311213611562
domInteractive69282393995132
firstPaint------
backgroundConnect70202144085153
firstReactRender3222106103645
getState2711150222679
initialActions207124
loadScripts12791102157010613361516
setupStore27101722922109
numNetworkReqs41281242638122
WebpackPower User HomeuiStartup30562193626178031844809
load14831126316937615812457
domContentLoaded14831126316937615812457
domInteractive12231578124104464
firstPaint------
backgroundConnect184431382233159965
firstReactRender76403825666225
getState29373901215371818
initialActions2059623
loadScripts14021101298328614951854
setupStore12781045174124551
numNetworkReqs101622475982242
📊 Page Load Benchmark Results

Current Commit: a6c5876 | Date: 11/27/2025

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±41ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 728ms (±37ms) 🟢 | historical mean value: 723ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 77ms (±14ms) 🟢 | historical mean value: 78ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 41ms 1.02s 1.35s 1.08s 1.35s
domContentLoaded 728ms 37ms 703ms 1.01s 756ms 1.01s
firstPaint 77ms 14ms 60ms 204ms 88ms 204ms
firstContentfulPaint 77ms 14ms 60ms 204ms 88ms 204ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 820 Bytes (0.01%)
  • common: 20 Bytes (0%)

Copy link
Contributor

@jeffsmale90 jeffsmale90 left a comment

Choose a reason for hiding this comment

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

Let's go!

@github-project-automation github-project-automation bot moved this to Needs dev review in PR review queue Nov 28, 2025
@MoMannn MoMannn moved this from Needs dev review to Has approvals, needs CODEOWNER in PR review queue Nov 28, 2025
@github-project-automation github-project-automation bot moved this from Has approvals, needs CODEOWNER to Review finalised - Ready to be merged in PR review queue Nov 28, 2025
@MoMannn MoMannn enabled auto-merge November 28, 2025 12:15
@metamaskbot
Copy link
Collaborator

Builds ready [f675d88]
UI Startup Metrics (1259 ± 109 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup12591034163710913311422
load1048866139710210931234
domContentLoaded1042864138610210891228
domInteractive261693192285
firstPaint55792124741710381183
backgroundConnect21719626313221248
firstReactRender301981123562
getState39161272244101
initialActions105114
loadScripts8316661156978901012
setupStore1273651324
numNetworkReqs1257821573
BrowserifyPower User HomeuiStartup21561761301826423812694
load1059929167913710581434
domContentLoaded1045909167013710431420
domInteractive37191762935111
firstPaint5479114894089981335
backgroundConnect266206810111242567
firstReactRender62441171667101
getState18813432631204238
initialActions103112
loadScripts82770414471378201203
setupStore21126082437
numNetworkReqs1526741075202323
WebpackStandard HomeuiStartup8166911089788471000
load63256683871663801
domContentLoaded62856183171660794
domInteractive2614109222188
firstPaint25785835190228679
backgroundConnect1063461125
firstReactRender2919125153139
getState261382123449
initialActions104111
loadScripts62555982269658785
setupStore1062131218
numNetworkReqs1257720572
WebpackPower User HomeuiStartup16501285254127519452129
load6805861241101682930
domContentLoaded6705781232102668924
domInteractive36171933433146
firstPaint297103695184381660
backgroundConnect41758210417216
firstReactRender61458886778
getState17313224918182203
initialActions103112
loadScripts6685761222100666916
setupStore201055102445
numNetworkReqs1676542383216402
FirefoxBrowserifyStandard HomeuiStartup13211073175216714041684
load1087921146812111331385
domContentLoaded1087921146812111331385
domInteractive64312003289123
firstPaint------
backgroundConnect48213404353116
firstReactRender24184862537
getState146177231131
initialActions103012
loadScripts1056906138611111081305
setupStore146246261236
numNetworkReqs1256817664
BrowserifyPower User HomeuiStartup26741987454249827664000
load1209982242924012151598
domContentLoaded1208982242824012151597
domInteractive12935906135110472
firstPaint------
backgroundConnect1112555689116289
firstReactRender62371251863112
getState29788893215367732
initialActions2040426
loadScripts1169961227922111801566
setupStore166111021208168696
numNetworkReqs99603156279238
WebpackStandard HomeuiStartup14681245207215215281796
load12071060149010312751436
domContentLoaded12071059149010312751436
domInteractive48261843061110
firstPaint------
backgroundConnect47201702948122
firstReactRender26206362935
getState157207281229
initialActions103012
loadScripts1182104314639712511392
setupStore13681131248
numNetworkReqs1257216763
WebpackPower User HomeuiStartup29062105544864630754353
load14721166289039514432526
domContentLoaded14721166289039514432526
domInteractive1193386014098474
firstPaint------
backgroundConnect152301188192142529
firstReactRender63402662863112
getState27682908226426775
initialActions4054837
loadScripts14021138274733113972439
setupStore1178779162105580
numNetworkReqs100562476177244
📊 Page Load Benchmark Results

Current Commit: f675d88 | Date: 11/28/2025

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±38ms) 🟡 | historical mean value: 1.05s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 729ms (±36ms) 🟢 | historical mean value: 728ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 78ms (±13ms) 🟢 | historical mean value: 78ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 38ms 1.02s 1.33s 1.09s 1.33s
domContentLoaded 729ms 36ms 707ms 991ms 757ms 991ms
firstPaint 78ms 13ms 60ms 188ms 88ms 188ms
firstContentfulPaint 78ms 13ms 60ms 188ms 88ms 188ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 820 Bytes (0.01%)
  • common: 20 Bytes (0%)

@MoMannn MoMannn added this pull request to the merge queue Nov 28, 2025
github-merge-queue bot pushed a commit that referenced this pull request Nov 28, 2025
## **Description**

This PR improves the user experience when revoking Gator permissions by
adding optimistic UI feedback.

**What is the reason for the change?**

When users clicked the "Revoke" button for a Gator permission, there was
a noticeable delay before the button appeared disabled and showed
"Pending..." text. This created a poor user experience where users might
click multiple times, unsure if their action was registered.

**What is the improvement/solution?**

The solution implements optimistic UI feedback by:
1. Immediately setting a pending state when the revoke button is clicked
2. Disabling the button and displaying "Pending..." text right away
3. Managing the pending state with a 800ms timeout after the revoke
operation completes to prevent visual flashing before the transaction
confirmation window appears
4. Properly cleaning up timeouts on component unmount to prevent memory
leaks
5. Immediately clearing the pending state if an error occurs

The implementation uses local component state (`pendingRevokeClicks`)
combined with the existing global pending revocations selector to
provide a smooth, responsive user experience.

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/38184?quickstart=1)

## **Changelog**

CHANGELOG entry: Improved responsiveness of revoke button in Gator
permissions by adding immediate UI feedback

## **Manual testing steps**

1. Go to all permissions
2. Find a permission
3. Click revoke -> the button should turn to pending revocation right
away, before the revoke transaction window shows.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->



https://github.com/user-attachments/assets/7e111d41-3857-44a0-84ea-7569af0f9e01



### **After**


https://github.com/user-attachments/assets/5c222555-8f0d-4dc5-b70e-247350024cdd


<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds optimistic pending state for Gator permission revocations,
disabling the button and showing "Pending..." immediately with timeout
cleanup.
> 
> - **UI — Gator Permissions**
>   - `review-permissions/review-gator-permissions-page.tsx`:
> - Introduces local pending state via `pendingRevokeClicks` (Set) and
`revokeTimeoutsRef` (Map) with unmount cleanup.
> - Updates `handleRevokeClick` to optimistically set pending, call
`revokeGatorPermission`, then clear after 800ms; clears immediately on
error.
> - Passes `hasRevokeBeenClicked` to children; simplifies empty-state
check to `gatorPermissions.length`.
>   - `components/review-gator-permission-item.tsx`:
> - Adds optional `hasRevokeBeenClicked` prop and integrates it into
`isPendingRevocation` alongside global `pendingRevocations`.
> - Disables Revoke button and switches label to pending when either
local or global pending is true.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f675d88. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 28, 2025
@MoMannn MoMannn added this pull request to the merge queue Nov 28, 2025
Merged via the queue into main with commit 17092a5 Nov 28, 2025
176 checks passed
@MoMannn MoMannn deleted the fix-show-pending-revocation-on-click branch November 28, 2025 14:22
@github-project-automation github-project-automation bot moved this from Review finalised - Ready to be merged to Merged, Closed or Archived in PR review queue Nov 28, 2025
@github-actions github-actions bot locked and limited conversation to collaborators Nov 28, 2025
@metamaskbot metamaskbot added the release-13.13.0 Issue or pull request that will be included in release 13.13.0 label Nov 28, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

INVALID-PR-TEMPLATE PR's body doesn't match template release-13.13.0 Issue or pull request that will be included in release 13.13.0 size-M team-delegation MetaMask Delegation Team

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

5 participants