import { singleton , inject } from "tsyringe" ; import { TYPES } from "../types" ; import { InteractionWaitingData } from "./types" ; import { Env , FnRequestInteractionOptions , MessageRequester , } from "@keplr-wallet/router" ; import { PushEventDataMsg , PushInteractionDataMsg } from "./foreground" ; import { RNG } from "@keplr-wallet/crypto" ; @ singleton ( ) export class InteractionService { protected waitingMap : Map < string , InteractionWaitingData > = new Map ( ) ; protected resolverMap : Map < string , { onApprove : ( result : unknown ) => void ; onReject : ( e : Error ) => void ; } > = new Map ( ) ; constructor ( @ inject ( TYPES . EventMsgRequester ) protected readonly eventMsgRequester , @ inject ( TYPES . RNG ) protected readonly rng ) { } dispatchEvent ( port , type , data ) { if ( ! type ) { throw new Error ( "Type should not be empty" ) ; } const msg = new PushEventDataMsg ( { type , data , } ) ; this . eventMsgRequester . sendMessage ( port , msg ) . catch ( ( e ) => { console . log ( `Failed to send the event to ${ port }: ${ e . message }` ) ; } ) ; } async waitApprove ( env , url , type , data , options ? ) { if ( ! type ) { throw new Error ( "Type should not be empty" ) ; } const interactionWaitingData = await this . addDataToMap ( type , env . isInternalMsg , data ) ; const msg = new PushInteractionDataMsg ( interactionWaitingData ) ; return await this . wait ( msg . data . id , ( ) => { env . requestInteraction ( url , msg , options ) ; } ) ; } protected async wait ( id , fn ) { if ( this . resolverMap . has ( id ) ) { throw new Error ( "Id is aleady in use" ) ; } return new Promise < unknown > ( ( resolve , reject ) => { this . resolverMap . set ( id , { onApprove : resolve , onReject : reject , } ) ; fn ( ) ; } ) ; } approve ( id , result ) { if ( this . resolverMap . has ( id ) ) { this . resolverMap . get ( id ) ! . onApprove ( result ) ; this . resolverMap . delete ( id ) ; } this . removeDataFromMap ( id ) ; } reject ( id ) { if ( this . resolverMap . has ( id ) ) { this . resolverMap . get ( id ) ! . onReject ( new Error ( "Request rejected" ) ) ; this . resolverMap . delete ( id ) ; } this . removeDataFromMap ( id ) ; } protected async addDataToMap ( type , isInternal , data ) { const bytes = new Uint8Array ( 8 ) ; const id = Array . from ( await this . rng ( bytes ) ) . map ( ( value ) => { return value . toString ( 16 ) ; } ) . join ( "" ) ; const interactionWaitingData = { id , type , isInternal , data , } ; if ( this . waitingMap . has ( id ) ) { throw new Error ( "Id is aleady in use" ) ; } this . waitingMap . set ( id , interactionWaitingData ) ; return interactionWaitingData ; } protected removeDataFromMap ( id ) { this . waitingMap . delete ( id ) ; } }