-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement on/off event methods in cip30.experimental
- Loading branch information
1 parent
15b68e6
commit fe0925d
Showing
10 changed files
with
270 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
packages/dapp-connector/src/WalletApi/Cip30EventRegistry.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { Cardano } from '@cardano-sdk/core'; | ||
import { Observable, Subscription } from 'rxjs'; | ||
|
||
export type AccountChangeCb = (addresses: Cardano.BaseAddress[]) => unknown; | ||
export type NetworkChangeCb = (network: Cardano.NetworkId) => unknown; | ||
export enum Cip30EventName { | ||
'accountChange' = 'accountChange', | ||
'networkChange' = 'networkChange' | ||
} | ||
export type Cip30EventMethod = (eventName: Cip30EventName, callback: AccountChangeCb | NetworkChangeCb) => void; | ||
export type Cip30Event = { eventName: Cip30EventName; data: Cardano.NetworkId | Cardano.BaseAddress[] }; | ||
type Cip30NetworkChangeEvent = { eventName: Cip30EventName.networkChange; data: Cardano.NetworkId }; | ||
type Cip30AccountChangeEvent = { eventName: Cip30EventName.accountChange; data: Cardano.BaseAddress[] }; | ||
type Cip30EventRegistryMap = { | ||
accountChange: AccountChangeCb[]; | ||
networkChange: NetworkChangeCb[]; | ||
}; | ||
|
||
const isNetworkChangeEvent = (event: Cip30Event): event is Cip30NetworkChangeEvent => | ||
event.eventName === Cip30EventName.networkChange; | ||
|
||
const isAccountChangeEvent = (event: Cip30Event): event is Cip30AccountChangeEvent => | ||
event.eventName === Cip30EventName.accountChange; | ||
|
||
/** | ||
* This class is responsible for registering and deregistering callbacks for specific events. | ||
* It also handles calling the registered callbacks. | ||
*/ | ||
export class Cip30EventRegistry { | ||
#cip30Event$: Observable<Cip30Event>; | ||
#registry: Cip30EventRegistryMap; | ||
#subscription: Subscription; | ||
|
||
constructor(cip30Event$: Observable<Cip30Event>) { | ||
this.#cip30Event$ = cip30Event$; | ||
this.#registry = { | ||
accountChange: [], | ||
networkChange: [] | ||
}; | ||
|
||
this.#subscription = this.#cip30Event$.subscribe((event) => { | ||
if (isNetworkChangeEvent(event)) { | ||
const { data } = event; | ||
for (const callback of this.#registry.networkChange) callback(data); | ||
} else if (isAccountChangeEvent(event)) { | ||
const { data } = event; | ||
for (const callback of this.#registry.accountChange) callback(data); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Register a callback for a specific event name. | ||
* | ||
* @param eventName - The event name to register the callback for. | ||
* @param callback - The callback to be called when the event is triggered. | ||
*/ | ||
register(eventName: Cip30EventName, callback: AccountChangeCb | NetworkChangeCb) { | ||
if (this.#subscription.closed) return; | ||
|
||
if (eventName === Cip30EventName.accountChange) { | ||
this.#registry.accountChange.push(callback as AccountChangeCb); | ||
} else if (eventName === Cip30EventName.networkChange) { | ||
this.#registry.networkChange.push(callback as NetworkChangeCb); | ||
} | ||
} | ||
|
||
/** | ||
* Deregister a callback for a specific event name. The callback must be the same reference used on registration. | ||
* | ||
* @param eventName - The event name to deregister the callback from. | ||
* @param callback - The callback to be deregistered. | ||
*/ | ||
deregister(eventName: Cip30EventName, callback: AccountChangeCb | NetworkChangeCb) { | ||
if (this.#subscription.closed) return; | ||
|
||
if (eventName === Cip30EventName.accountChange) { | ||
this.#registry.accountChange = this.#registry.accountChange.filter((cb) => cb !== callback); | ||
} else if (eventName === Cip30EventName.networkChange) { | ||
this.#registry.networkChange = this.#registry.networkChange.filter((cb) => cb !== callback); | ||
} | ||
} | ||
|
||
/** Unsubscribe from the event stream. Once called, the registry can no longer be used. */ | ||
shutdown() { | ||
if (this.#subscription.closed) return; | ||
this.#subscription.unsubscribe(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
packages/dapp-connector/test/WalletApi/CIp30EventRegistry.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { Cardano } from '@cardano-sdk/core'; | ||
import { Cip30Event, Cip30EventName, Cip30EventRegistry } from '../../src/WalletApi/Cip30EventRegistry'; | ||
import { Subject } from 'rxjs'; | ||
|
||
describe('Cip30EventRegistry', () => { | ||
let cip30Event$: Subject<Cip30Event>; | ||
let registry: Cip30EventRegistry; | ||
|
||
beforeEach(() => { | ||
cip30Event$ = new Subject(); | ||
registry = new Cip30EventRegistry(cip30Event$); | ||
}); | ||
|
||
afterEach(() => { | ||
registry.shutdown(); | ||
}); | ||
|
||
it('should register and trigger networkChange callback', () => { | ||
const callback = jest.fn(); | ||
registry.register(Cip30EventName.networkChange, callback); | ||
|
||
const networkId: Cardano.NetworkId = 1; | ||
cip30Event$.next({ data: networkId, eventName: Cip30EventName.networkChange }); | ||
|
||
expect(callback).toHaveBeenCalledWith(networkId); | ||
}); | ||
|
||
it('should register and trigger accountChange callback', () => { | ||
const callback = jest.fn(); | ||
registry.register(Cip30EventName.accountChange, callback); | ||
|
||
const addresses: Cardano.BaseAddress[] = [{} as unknown as Cardano.BaseAddress]; | ||
cip30Event$.next({ data: addresses, eventName: Cip30EventName.accountChange }); | ||
|
||
expect(callback).toHaveBeenCalledWith(addresses); | ||
}); | ||
|
||
it('should deregister networkChange callback', () => { | ||
const callback = jest.fn(); | ||
registry.register(Cip30EventName.networkChange, callback); | ||
registry.deregister(Cip30EventName.networkChange, callback); | ||
|
||
const networkId: Cardano.NetworkId = 1; | ||
cip30Event$.next({ data: networkId, eventName: Cip30EventName.networkChange }); | ||
|
||
expect(callback).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should deregister accountChange callback', () => { | ||
const callback = jest.fn(); | ||
registry.register(Cip30EventName.accountChange, callback); | ||
registry.deregister(Cip30EventName.accountChange, callback); | ||
|
||
const addresses: Cardano.BaseAddress[] = [{} as unknown as Cardano.BaseAddress]; | ||
cip30Event$.next({ data: addresses, eventName: Cip30EventName.accountChange }); | ||
|
||
expect(callback).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should handle multiple callbacks for the same event', () => { | ||
const callback1 = jest.fn(); | ||
const callback2 = jest.fn(); | ||
registry.register(Cip30EventName.networkChange, callback1); | ||
registry.register(Cip30EventName.networkChange, callback2); | ||
|
||
const networkId: Cardano.NetworkId = 1; | ||
cip30Event$.next({ data: networkId, eventName: Cip30EventName.networkChange }); | ||
|
||
expect(callback1).toHaveBeenCalledWith(networkId); | ||
expect(callback2).toHaveBeenCalledWith(networkId); | ||
}); | ||
|
||
it('should not trigger callbacks after shutdown', () => { | ||
const callback = jest.fn(); | ||
registry.register(Cip30EventName.networkChange, callback); | ||
|
||
registry.shutdown(); | ||
|
||
const networkId: Cardano.NetworkId = 1; | ||
cip30Event$.next({ data: networkId, eventName: Cip30EventName.networkChange }); | ||
|
||
expect(callback).not.toHaveBeenCalled(); | ||
expect(cip30Event$.observed).toBeFalsy(); | ||
}); | ||
|
||
it('should not register callbacks after shutdown', () => { | ||
const callback = jest.fn(); | ||
registry.shutdown(); | ||
registry.register(Cip30EventName.networkChange, callback); | ||
|
||
const networkId: Cardano.NetworkId = 1; | ||
cip30Event$.next({ data: networkId, eventName: Cip30EventName.networkChange }); | ||
|
||
expect(callback).not.toHaveBeenCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.