Skip to content

fix: reset listener state on endConnection for proper reconnection#3151

Merged
hyochan merged 5 commits intomainfrom
fix/listener-reset-on-disconnect
Feb 18, 2026
Merged

fix: reset listener state on endConnection for proper reconnection#3151
hyochan merged 5 commits intomainfrom
fix/listener-reset-on-disconnect

Conversation

@hyochan
Copy link
Owner

@hyochan hyochan commented Feb 18, 2026

Summary

  • Reset listenersAttached flag (Android) and isInitializing flag (iOS) during endConnection so listeners can be re-registered after reconnection
  • Add tests verifying purchase and error listeners work correctly after endConnection → initConnection cycle

Changes

Android (HybridRnIap.kt)

  • Reset listenersAttached = false in endConnection to allow re-attachment on next initConnection

iOS (HybridRnIap.swift)

  • Reset isInitializing = false in endConnection to allow re-initialization
  • Clean up extra blank lines

Tests (index.test.ts)

  • Add reconnection test: purchase updated listeners work after endConnection → initConnection
  • Add reconnection test: error listeners work after endConnection → initConnection

Test plan

  • yarn typecheck passes
  • yarn lint passes
  • yarn test passes (new reconnection tests included)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Clarified Hook API semantics: removed certain examples, added guidance that purchase results arrive via callbacks (onPurchaseSuccess), and adjusted header formatting.
  • Bug Fixes

    • Improved connection cleanup to fully reset listeners and initialization state for reliable reconnection.
  • Breaking Changes

    • requestPurchase now returns Promise; purchase results are delivered via callbacks.
  • Tests

    • Added reconnection and purchase-request tests verifying listeners and void return behavior.

hyochan and others added 2 commits February 17, 2026 23:09
- Change requestPurchase return type from Promise<RequestPurchaseResult | null> to Promise<void>
- This aligns with hook design where purchase results come through onPurchaseSuccess callback
- Update CLAUDE.md documentation to clarify hook vs root API semantics
- Fix markdownlint warning in CLAUDE.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reset `listenersAttached` (Android) and `isInitializing` (iOS) flags
during endConnection so that listeners can be re-registered after a
subsequent initConnection call. Add reconnection listener tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @hyochan, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves an issue where in-app purchase event listeners would not re-establish correctly after the connection to the billing service was ended and then re-initialized. By resetting key internal state flags on both Android and iOS platforms during connection termination, the system now properly allows listeners to be re-attached. This ensures that applications reliably receive purchase and error events even after connection interruptions, enhancing the robustness of the in-app purchase flow. The changes are validated by new dedicated test cases.

Highlights

  • Listener Reconnection Fix: Implemented a fix to ensure in-app purchase listeners (purchase and error) can be properly re-registered and function correctly after an endConnection followed by an initConnection cycle.
  • Platform-Specific State Reset: Reset listenersAttached flag in Android's HybridRnIap.kt and isInitializing flag in iOS's HybridRnIap.swift within the endConnection method to facilitate proper re-initialization.
  • New Reconnection Tests: Added comprehensive test cases in index.test.ts to verify that both purchase updated and error listeners work as expected after a full endConnection then initConnection sequence.
  • Documentation and API Clarity: Updated CLAUDE.md to clarify the useIAP hook's requestPurchase behavior, emphasizing that results are delivered via event listeners rather than its return value. The requestPurchase signature in useIAP.ts was also updated to return Promise<void>.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • CLAUDE.md
    • Clarified requestPurchase behavior in useIAP hook documentation, noting that results are handled by callbacks/event listeners.
    • Updated heading style for 'Android & iOS (OpenIAP)' section.
  • android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt
    • Reset listenersAttached flag to false in endConnection to allow listener re-attachment.
  • ios/HybridRnIap.swift
    • Reset isInitializing flag to false in endConnection to allow re-initialization.
    • Removed extraneous blank lines.
  • src/tests/index.test.ts
    • Added a test case to verify purchase updated listeners work after endConnection followed by initConnection.
    • Added a test case to verify error listeners work after endConnection followed by initConnection.
  • src/hooks/useIAP.ts
    • Removed RequestPurchaseResult import as requestPurchase no longer returns it.
    • Modified requestPurchase method signature to return Promise<void>.
    • Updated requestPurchase implementation to be async and await the internal call, returning void.
Activity
  • The pull request was created by hyochan to address a bug related to listener state not resetting correctly, preventing proper reconnection.
  • The author provided a detailed summary, outlining the problem, proposed changes for Android and iOS, and new test cases.
  • The PR includes new tests specifically designed to validate the reconnection behavior of purchase and error listeners, demonstrating a thorough approach to the fix.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.67%. Comparing base (b903301) to head (dd3dd1c).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3151      +/-   ##
==========================================
+ Coverage   68.54%   68.67%   +0.13%     
==========================================
  Files           9        9              
  Lines        1777     1778       +1     
  Branches      587      587              
==========================================
+ Hits         1218     1221       +3     
+ Misses        554      552       -2     
  Partials        5        5              
Flag Coverage Δ
library 68.67% <100.00%> (+0.13%) ⬆️

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

Files with missing lines Coverage Δ
src/hooks/useIAP.ts 69.71% <100.00%> (+0.74%) ⬆️

... and 1 file with indirect coverage changes

🚀 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.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request correctly addresses the issue where listener state was not being reset during endConnection, which prevented proper re-registration of listeners upon reconnection. The changes in HybridRnIap.kt (Android) and HybridRnIap.swift (iOS) ensure that the initialization and listener flags are cleared. Additionally, the useIAP hook's requestPurchase method has been updated to return void, which aligns with the library's event-driven architecture where results are handled via listeners and hook state. The added tests provide good coverage for the reconnection scenario.

runCatching { openIap.endConnection() }
productTypeBySku.clear()
isInitialized = false
listenersAttached = false
Copy link
Contributor

Choose a reason for hiding this comment

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

high

While resetting listenersAttached is necessary to allow re-attaching the bridge listeners to the underlying openIap module, you should also clear the internal listener lists (purchaseUpdatedListeners, purchaseErrorListeners, etc.) in endConnection.

On iOS, cleanupExistingState (called by endConnection) clears these lists. On Android, if you don't clear them, any listeners registered from the JS side before endConnection will persist. When initConnection is called again, a new bridge listener is attached to openIap, but the old JS listeners are still in the lists. This will result in duplicate notifications for the same event after reconnection.

Clearing these lists ensures consistency between platforms and prevents duplicate event delivery.

            listenersAttached = false
            purchaseUpdatedListeners.clear()
            purchaseErrorListeners.clear()
            promotedProductListenersIOS.clear()
            userChoiceBillingListenersAndroid.clear()
            developerProvidedBillingListenersAndroid.clear()

@hyochan hyochan added the 🛠 bugfix All kinds of bug fixes label Feb 18, 2026
Clear all internal listener lists (purchaseUpdated, purchaseError,
promotedProduct, userChoiceBilling, developerProvidedBilling) in
endConnection to match iOS cleanupExistingState behavior and prevent
duplicate event delivery after reconnection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

📝 Walkthrough

Walkthrough

Documentation clarifies requestPurchase results are delivered via onPurchaseSuccess (not return value). requestPurchase signature changed to return Promise. Native endConnection/cleanup now clears initialization and all listener state. Tests added verifying listeners survive endConnection + reconnect cycles.

Changes

Cohort / File(s) Summary
API Contract & Documentation
CLAUDE.md, src/hooks/useIAP.ts, src/__tests__/hooks/useIAP.test.ts
Changed requestPurchase signature from `Promise<RequestPurchaseResult
Native Connection Lifecycle
android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt, ios/HybridRnIap.swift
Enhanced cleanup: endConnection / cleanupExistingState now clear listener collections and reset init flags (listenersAttached = false, clear purchase/error/promoted/developer/user-choice listeners; on iOS also sets isInitializing = false) to allow clean re-initialization.
Reconnection Test Coverage
src/__tests__/index.test.ts
Added two tests covering disconnect → reconnect behavior: verify purchase listener and purchase-error listener re-registration and event/error delivery after reconnection.

Sequence Diagram(s)

sequenceDiagram
participant App as App (UI)
participant Hook as useIAP (JS)
participant Native as HybridRnIap (Native)
participant Store as Billing Store (Google/Apple)

App->>Hook: call requestPurchase(sku)
Hook->>Native: requestPurchaseInternal(sku)
Native->>Store: initiate purchase flow
Store-->>Native: purchase success event
Native-->>Hook: emit onPurchaseSuccess(product)
Hook-->>App: onPurchaseSuccess callback invoked

App->>Hook: call endConnection()
Hook->>Native: endConnection()
Native-->>Native: clear listeners, set listenersAttached=false, isInitializing=false

App->>Hook: re-init connection
Hook->>Native: initConnection()
Native-->>Native: re-register native listeners
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • #2994 — aligns purchase flow to event-only semantics and moves error/result delivery to callbacks.
  • #3003 — changes endConnection and listener/initialization wiring, similar lifecycle fixes.
  • #3012 — refactors useIAP purchase handling and moves toward callback-driven semantics.

Suggested labels

📱 iOS, 🤖 android

Poem

🐰 I hopped through code and cleared the tracks,
Listeners reset, no tangled backs,
Reconnect, re-register, then cheer—
Purchases whisper, callbacks near,
A tidy hop — the logs look clear! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly summarizes the main change: resetting listener state on endConnection to enable proper reconnection, which is reflected across all three platform implementations (Android, iOS, and test coverage).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/listener-reset-on-disconnect

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hyochan
Copy link
Owner Author

hyochan commented Feb 18, 2026

Addressed Gemini's review feedback in 2e6a92c — now clearing all five internal listener lists (purchaseUpdatedListeners, purchaseErrorListeners, promotedProductListenersIOS, userChoiceBillingListenersAndroid, developerProvidedBillingListenersAndroid) in Android's endConnection to match iOS's cleanupExistingState behavior and prevent duplicate event delivery after reconnection.

Add test verifying useIAP's requestPurchase calls root API and
returns void, covering the async/await wrapper changed in the
requestPurchase return type update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/__tests__/hooks/useIAP.test.ts (1)

137-140: Move the expect assertion outside of act.

act() is intended to wrap operations that trigger React state changes. The assertion expect(result).toBeUndefined() is pure test logic and is more readable and conventional when placed after the act block.

♻️ Proposed refactor
-    await act(async () => {
-      const result = await api.requestPurchase({sku: 'product1'});
-      expect(result).toBeUndefined();
-    });
+    let result: unknown;
+    await act(async () => {
+      result = await api.requestPurchase({sku: 'product1'});
+    });
+    expect(result).toBeUndefined();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/hooks/useIAP.test.ts` around lines 137 - 140, The assertion is
inside the React testing act wrapper; move the pure test assertion outside of
act to improve readability: keep the async call to api.requestPurchase({sku:
'product1'}) inside await act(async () => { ... }) but capture its return (e.g.,
assign result) and then call expect(result).toBeUndefined() after the act block;
adjust the test around the api.requestPurchase call and the result variable so
the only code inside act is the operation that triggers state changes and the
expect runs immediately afterward.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/__tests__/hooks/useIAP.test.ts`:
- Around line 122-124: Replace the mock that resolves to null with one that
resolves to undefined so it matches the Promise<void> typing for
IAP.requestPurchase; locate the spy on IAP.requestPurchase (assigned to
mockRequestPurchase) in the useIAP.test.ts test and change its
mockResolvedValue(null as any) to mockResolvedValue(undefined) (or use
jest.fn(() => Promise.resolve()) pattern) to keep semantics consistent with the
project setup.

---

Nitpick comments:
In `@src/__tests__/hooks/useIAP.test.ts`:
- Around line 137-140: The assertion is inside the React testing act wrapper;
move the pure test assertion outside of act to improve readability: keep the
async call to api.requestPurchase({sku: 'product1'}) inside await act(async ()
=> { ... }) but capture its return (e.g., assign result) and then call
expect(result).toBeUndefined() after the act block; adjust the test around the
api.requestPurchase call and the result variable so the only code inside act is
the operation that triggers state changes and the expect runs immediately
afterward.

The root API requestPurchase returns Promise<Purchase | null>, so null
is the correct mock value without needing a type cast.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":401,"request":{"method":"PATCH","url":"https://api.github.com/repos/hyochan/react-native-iap/issues/comments/3920890256","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: failure by coderabbit.ai -->\n\n> [!CAUTION]\n> ## Review failed\n> \n> An error occurred during the review process. Please try again later.\n\n<!-- end of auto-generated comment: failure by coderabbit.ai -->\n\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nDocumentation clarifies requestPurchase results are delivered via onPurchaseSuccess (not return value). requestPurchase signature changed to return Promise<void>. Native endConnection/cleanup now clears initialization and all listener state. Tests added verifying listeners survive endConnection + reconnect cycles.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**API Contract & Documentation** <br> `CLAUDE.md`, `src/hooks/useIAP.ts`, `src/__tests__/hooks/useIAP.test.ts`|Changed `requestPurchase` signature from `Promise<RequestPurchaseResult | null>` to `Promise<void>`; removed `RequestPurchaseResult` import; updated docs to state results arrive via `onPurchaseSuccess`; added unit test asserting `requestPurchase` returns `undefined` and calls root API.|\n|**Native Connection Lifecycle** <br> `android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt`, `ios/HybridRnIap.swift`|Enhanced cleanup: `endConnection` / `cleanupExistingState` now clear listener collections and reset init flags (`listenersAttached = false`, clear purchase/error/promoted/developer/user-choice listeners; on iOS also sets `isInitializing = false`) to allow clean re-initialization.|\n|**Reconnection Test Coverage** <br> `src/__tests__/index.test.ts`|Added two tests covering disconnect → reconnect behavior: verify purchase listener and purchase-error listener re-registration and event/error delivery after reconnection.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\nparticipant App as App (UI)\nparticipant Hook as useIAP (JS)\nparticipant Native as HybridRnIap (Native)\nparticipant Store as Billing Store (Google/Apple)\n\nApp->>Hook: call requestPurchase(sku)\nHook->>Native: requestPurchaseInternal(sku)\nNative->>Store: initiate purchase flow\nStore-->>Native: purchase success event\nNative-->>Hook: emit onPurchaseSuccess(product)\nHook-->>App: onPurchaseSuccess callback invoked\n\nApp->>Hook: call endConnection()\nHook->>Native: endConnection()\nNative-->>Native: clear listeners, set listenersAttached=false, isInitializing=false\n\nApp->>Hook: re-init connection\nHook->>Native: initConnection()\nNative-->>Native: re-register native listeners\n```\n\n## Estimated code review effort\n\n🎯 3 (Moderate) | ⏱️ ~20 minutes\n\n## Possibly related PRs\n\n- `#2994` — aligns purchase flow to event-only semantics and moves error/result delivery to callbacks.  \n- `#3003` — changes endConnection and listener/initialization wiring, similar lifecycle fixes.  \n- `#3012` — refactors useIAP purchase handling and moves toward callback-driven semantics.\n\n## Suggested labels\n\n`📱 iOS`, `🤖 android`\n\n## Poem\n\n> 🐰 I hopped through code and cleared the tracks,  \n> Listeners reset, no tangled backs,  \n> Reconnect, re-register, then cheer—  \n> Purchases whisper, callbacks near,  \n> A tidy hop — the logs look clear! 🎉\n\n</details>\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n### ❌ Failed checks (1 warning)\n\n|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |\n| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                                                                                |\n| :---------------: | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                                                                                |\n|    Title check    | ✅ Passed | The PR title directly summarizes the main change: resetting listener state on endConnection to enable proper reconnection, which is reflected across all three platform implementations (Android, iOS, and test coverage). |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `fix/listener-reset-on-disconnect`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=hyochan/react-native-iap&utm_content=3151)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":1}},"response":{"url":"https://api.github.com/repos/hyochan/react-native-iap/issues/comments/3920890256","status":401,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","connection":"close","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Wed, 18 Feb 2026 14:18:43 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-media-type":"github.v3; format=json","x-github-request-id":"089D:9454F:4F98C50:15C08491:6995CA43","x-xss-protection":"0"},"data":{"message":"Bad credentials","documentation_url":"https://docs.github.com/rest","status":"401"}}}

@hyochan hyochan merged commit 6343b15 into main Feb 18, 2026
11 checks passed
@hyochan hyochan deleted the fix/listener-reset-on-disconnect branch February 18, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🛠 bugfix All kinds of bug fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant