All files / src/services/handle dial-call-handler.ts

100% Statements 30/30
100% Branches 20/20
100% Functions 4/4
100% Lines 30/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119              15x     15x                                                                                                         15x                 22x     22x   10x 10x 10x       7x 7x 7x 4x 4x 1x 1x   3x 2x     2x 2x 1x     2x     3x 1x   2x 1x 1x   1x 1x 1x   4x        
import { DialCallData } from '../../models/dial-call-data';
import { CallRequest } from '../../models/infrasctructure/call-request';
import { Base64 } from '../../utils/base64';
import { Cryptography } from '../../utils/cryptography';
import { Logger } from '../../utils/logger';
import { Utf8 } from '../../utils/utf8';
import { ConnectionService } from '../connection-service';
import { ConnectionSagaState } from '../connection/connection-saga';
import { SessionService } from '../session-service';
import { TimeService } from '../time-service';
import { CallHandler, getCallHandler } from './call-handler';
 
/**
 * Configuration interface for the dial call handler.
 * Defines callbacks that allow the application to respond to incoming dial requests.
 */
export type DialCallHandlerConfig = {
    /**
     * Callback to focus the UI on an incoming dial request.
     * If provided, the application can show appropriate UI elements when a dial is received.
     *
     * @param publicKey - Public key of the peer initiating the dial
     * @returns Promise resolving to a boolean indicating if the focus was successful
     */
    focusOnDial?: (publicKey: string) => Promise<boolean>;
 
    /**
     * Callback to request user permission for an incoming dial.
     * If provided, allows the application to prompt the user to accept or reject the dial.
     *
     * @param publicKey - Public key of the peer initiating the dial
     * @returns Promise resolving to a boolean indicating if the dial was accepted
     */
    requestDial?: (publicKey: string) => Promise<boolean>;
};
 
/**
 * Interface for handling 'dial' call requests in the signaling process.
 * Extends the base CallHandler with dial-specific initialization.
 */
export interface DialCallHandler extends CallHandler<DialCallData> {
    /**
     * Initializes the dial call handler with the provided configuration.
     * Sets up callbacks for user interaction during dial events.
     *
     * @param config - Configuration with optional focus and request callbacks
     */
    initialize(config: DialCallHandlerConfig): void;
}
 
/**
 * Factory function that creates and returns an implementation of the DialCallHandler interface.
 * Handles incoming dial requests that initiate the WebRTC connection process.
 *
 * @param logger - Logger instance for error and debugging information
 * @param timeService - Service for managing time synchronization
 * @param sessionService - Service for accessing session information
 * @param base64 - Utility for Base64 encoding/decoding
 * @param utf8 - Utility for UTF-8 encoding/decoding
 * @param cryptography - Cryptography service for signature verification
 * @param connectionService - Service for managing peer connections
 * @returns An implementation of the DialCallHandler interface
 */
export function getDialCallHandler(
    logger: Logger,
    timeService: TimeService,
    sessionService: SessionService,
    base64: Base64,
    utf8: Utf8,
    cryptography: Cryptography,
    connectionService: ConnectionService,
): DialCallHandler {
    const handler = getCallHandler<DialCallData>(logger, timeService, sessionService, base64, utf8, cryptography);
    let focusOnDial: ((publicKey: string) => Promise<boolean>) | undefined;
    let requestDial: ((publicKey: string) => Promise<boolean>) | undefined;
    return {
        initialize(config: DialCallHandlerConfig) {
            focusOnDial = config.focusOnDial;
            requestDial = config.requestDial;
            logger.debug('[dial-call-handler] Initialized.');
        },
        ...handler,
        async handle(request: CallRequest<DialCallData>): Promise<boolean> {
            const peerPublicKey = request.b.a;
            let connection = connectionService.getConnection(peerPublicKey);
            if (connection) {
                connection.setIncomingEncryption(request.b.d);
                if (connection.incomingState === ConnectionSagaState.AwaitingDial) {
                    connection.continueIncoming();
                    return true;
                }
                if (connection.incomingState !== ConnectionSagaState.AwaitingAnswer) {
                    logger.debug(
                        `[dial-call-handler] Incoming call '${request.a}' from ${peerPublicKey} triggered connection re-open. Incoming saga is not in suitable state (${ConnectionSagaState[ConnectionSagaState.AwaitingDial]} expected, ${ConnectionSagaState[connection.incomingState]} found).`,
                    );
                    new Promise(async () => {
                        if (focusOnDial !== undefined && focusOnDial !== null) {
                            await focusOnDial(request.b.a);
                        }
                    });
                    await connection.openIncoming();
                }
            } else {
                if (focusOnDial !== undefined && focusOnDial !== null && !(await focusOnDial(request.b.a))) {
                    return false;
                }
                if (requestDial !== undefined && requestDial !== null && !(await requestDial(request.b.a))) {
                    logger.debug(`[dial-call-handler] Incoming call '${request.a}' from ${peerPublicKey} declined.`);
                    return true;
                }
                connection = connectionService.createIncoming(peerPublicKey);
                connection.setIncomingEncryption(request.b.d);
                await connection.openIncoming();
            }
            return true;
        },
    };
}