fix(nwc): avoid initializeService racing node-switch teardown

This commit is contained in:
ajaysehwal
2026-06-04 10:52:40 -04:00
committed by Evan Kaloudis
parent 1570b01845
commit 6faee403fe
+49 -32
View File
@@ -159,6 +159,7 @@ export default class NostrWalletConnectStore {
private appStateListener: AppStateSubscription | null = null;
private androidLogListener: EmitterSubscription | null = null;
private _scheduledSave: ReturnType<typeof setTimeout> | null = null;
private resetInFlight: Promise<void> | null = null;
settingsStore: SettingsStore;
balanceStore: BalanceStore;
@@ -194,9 +195,8 @@ export default class NostrWalletConnectStore {
this.modalStore = modalStore;
this.paymentsStore = paymentsStore;
// When a node switch begins (connecting flips to true), tear down the
// current service so initializeService() will fully reinitialize for
// the new node instead of being skipped by the isInitialized guard.
// Node switch: `reset(true)` clears `isInitialized` synchronously and exposes
// `resetInFlight` so `initializeService` waits for teardown before starting.
reaction(
() => this.settingsStore.connecting,
(connecting) => {
@@ -210,6 +210,9 @@ export default class NostrWalletConnectStore {
@action
public initializeService = async () => {
if (this.resetInFlight) {
await this.resetInFlight;
}
if (this.isInitialized) {
console.log('NWC: skipping initialization - already initialized');
return;
@@ -368,36 +371,50 @@ export default class NostrWalletConnectStore {
(Platform.OS === 'android' &&
this.persistentNWCServiceEnabled));
if (!skipTeardown) {
console.log('NWC: resetting service');
await this.flushScheduledSave();
if (Platform.OS === 'ios') {
this.teardownIOSAppStateMonitor();
await this.stopIOSAudioKeepAlive();
}
if (this.androidReconnectionListener) {
this.androidReconnectionListener.remove();
this.androidReconnectionListener = null;
}
if (this.appStateListener) {
this.appStateListener.remove();
this.appStateListener = null;
}
if (this.androidLogListener) {
this.androidLogListener.remove();
this.androidLogListener = null;
}
this.error = false;
this.walletServiceKeys = null;
this.errorMessage = '';
this.waitingForConnection = false;
this.currentConnectionId = undefined;
this.connectionJustSucceeded = false;
this.lastConnectionAttempt = 0;
this.cashuEnabled = false;
this.nwcWalletServices.clear();
this.publishedRelays.clear();
this.isInitialized = false;
await this.unsubscribeFromAllConnections();
const previousReset = this.resetInFlight;
const teardown = (async () => {
if (previousReset) {
await previousReset;
}
console.log('NWC: resetting service');
await this.flushScheduledSave();
if (Platform.OS === 'ios') {
this.teardownIOSAppStateMonitor();
await this.stopIOSAudioKeepAlive();
}
if (this.androidReconnectionListener) {
this.androidReconnectionListener.remove();
this.androidReconnectionListener = null;
}
if (this.appStateListener) {
this.appStateListener.remove();
this.appStateListener = null;
}
if (this.androidLogListener) {
this.androidLogListener.remove();
this.androidLogListener = null;
}
this.error = false;
this.walletServiceKeys = null;
this.errorMessage = '';
this.waitingForConnection = false;
this.currentConnectionId = undefined;
this.connectionJustSucceeded = false;
this.lastConnectionAttempt = 0;
this.cashuEnabled = false;
this.nwcWalletServices.clear();
this.publishedRelays.clear();
await this.unsubscribeFromAllConnections();
})();
this.resetInFlight = teardown;
try {
await teardown;
} finally {
if (this.resetInFlight === teardown) {
this.resetInFlight = null;
}
}
}
runInAction(() => {
this.loading = false;