Conversation
f45e0de to
97dedd4
Compare
|
Follow-up: the initial fix in Added 2 commits (
Verified manually: Reject → spinner returns to Vote button immediately; Approve → proceeds as before. Happy to squash into a single commit on merge if you prefer — kept them separate so the evolution is visible. |
0416de3 to
52b2ae6
Compare
|
Force-pushed with a storage-based approach. Previous commits replaced entirely. See updated PR description. |
|
Routed popup approve through |
Reject previously wrote AppConnect{status:'rejected'} to storage,
so the next connect() saw the cached rejection and never reopened
the popup — user was stuck until manual cleanup.
Record presence is now the source of truth: reject deletes the
record. ConnectAppHandler tracks open popup sessions in memory and
detects rejection when a session's record disappears, returning
status='rejected' once so the signer polling loop exits.
Preserves AppConnectStatus enum and signer polling (no protocol
change). Minimal alternative to #118.
fix (appConnect): minimal patch for reject cache (alternative to #118)
Closes #117
Problem
AppConnectstoredstatus: pending | approved | rejected | error, used as a state machine between popup and content-script via storage. Side effects:signer.connect()calls return the cachedrejectedstatus without opening a popup (Rejected app connection status is cached permanently, preventing retry #117).ExtensionSigner.connect()polled the storage status every 500ms for up toMESSAGING_TIMEOUT(3 min) — fragile and slow to react.Solution
Treat presence of an
AppConnectrecord as the single source of truth:appConnects_*→ connection is approved.Flow changes:
AppConnectrecord directly viaAppConnectRepository(storage is cross-context in MV3 extensions).ConnectAppHandlerin the content-script opens the popup, subscribes tochrome.storage.onChanged, and pollspopup.closed. First signal wins: record appears → resolve withWalletInfo; popup closed without a record → reject withApp connection was rejected.Reject cache bug disappears by design: rejection never writes to storage, so the next
connect()call starts a fresh flow.Changes
Removed — state machine artifacts:
AppConnectStatusenumApproveAppConnectPayload,RejectAppConnectPayloadApproveAppConnectHandler,RejectAppConnectHandler(popup writes directly now)APPROVE_APP_CONNECT,REJECT_APP_CONNECTfromMessagingMethodsapproveAppConnect/rejectAppConnectfromPrivateAPIClientSimplified:
AppConnect,AppConnectStorageSchema,ConnectAppResponse— nostatusfieldExtensionSigner.connect()— single awaited call, no polling loopAppConnectRepository.create()— idempotent, overwrites if already approvedRewritten:
ConnectAppHandler.handle()— storage-event-driven resolutionAppConnectState.tsx— reads URL fromuseSearchParams(), writes record via repository on Approve, just closes on RejectAdded:
0010_drop_app_connect_status— stripsstatusfield from existing records, removes non-approved entries, bumpsSCHEMA_VERSIONto 10Verification
npm run lintcleannpm test— 104 tests green, including new storage-event coveragenpm run build— successfulFollow-up
During implementation we found that
chrome.runtime.onMessage.dispatch— the undocumented method used acrossPrivateAPIClient/PrivateAPI— does not reliably cross contexts in Chrome 130+. This PR works around it by using storage-based signaling for the connect flow specifically (documented MV3 API). A separate issue will be opened proposing a refactor of the messaging layer to an MV3 service worker pattern.