PageSourceSearch

https://ppoagroup.com/js/dialer.js?v=2026-05-08-r2

js ppoagroup.com collected 2026-09-24 14:50:19 UTC 5,948 bytes, 141 lines download raw bytes

1// PPOAGroup browser dialer interop layer.
2// Wraps the @twilio/voice-sdk Device API for Blazor WASM JSInterop.
3//
4// Twilio Voice SDK is loaded from a pinned CDN (see index.html). When the SDK is present
5// the global `Twilio` object provides `Device`. We expose a minimal facade matching the
6// plan's contract: register / connect / disconnect / mute / refreshToken / unregister.
7//
8// All calls are fire-and-forget where possible. Errors are surfaced to .NET via the
9// captured DotNetObjectReference's reportError method.
10
11window.ppoaDialer = (function () {
12    let device = null;
13    let activeCall = null;
14    let dotNetRef = null;
15
16    function reportEvent(name, payload) {
17        if (!dotNetRef) return;
18        try {
19            dotNetRef.invokeMethodAsync('OnDialerEvent', name, payload || {});
20        } catch (e) {
21            // swallow — Blazor disposed before event reached
22        }
23    }
24
25    // True when index.html's Twilio script tag fired onerror, OR when onload fired
26    // but window.Twilio.Device is missing (corrupted bundle). Either way, the SDK
27    // is unusable and the dialer must show a clear error instead of failing silently.
28    function isSdkUnavailable() {
29        if (window.__ppoaTwilioSdkLoadFailed === true) return true;
30        if (typeof window.Twilio === 'undefined') return true;
31        if (!window.Twilio.Device) return true;
32        return false;
33    }
34
35    function attachCallEvents(call) {
36        call.on('accept', () => reportEvent('accept', {}));
37        call.on('disconnect', () => {
38            const sid = call.parameters && call.parameters.CallSid;
39            reportEvent('disconnect', { callSid: sid });
40            activeCall = null;
41        });
42        call.on('cancel', () => reportEvent('cancel', {}));
43        call.on('error', (err) => {
44            reportEvent('error', { message: (err && err.message) || 'Unknown call error' });
45        });
46        call.on('reject', () => reportEvent('reject', {}));
47    }
48
49    return {
50        // Public synchronous probe Blazor can call before attempting register/connect.
51        // Cheaper than spinning up the JSInterop register path just to discover the SDK is missing.
52        isReady: function () { return !isSdkUnavailable(); },
53
54        // Initial Device.register(token). Defer this until first dialer-expand to avoid
55        // per-page-load WebSocket overhead on users who never open the dialer.
56        register: function (token, dotNet) {
57            dotNetRef = dotNet;
58            if (isSdkUnavailable()) {
59                console.error('[ppoa] register() called but Twilio Voice SDK is not loaded.');
60                reportEvent('sdkUnavailable', { message: 'Voice library failed to load. Hard refresh the page (Ctrl+Shift+R), or contact support.' });
61                return false;
62            }
63            try {
64                if (device) {
65                    // Already registered — refresh token in place.
66                    device.updateToken(token);
67                    reportEvent('ready', {});
68                    return true;
69                }
70                device = new window.Twilio.Device(token, {
71                    logLevel: 'warn',
72                    closeProtection: true,
73                    edge: 'ashburn'
74                });
75                device.on('registered', () => reportEvent('ready', {}));
76                device.on('error', (err) => {
77                    reportEvent('error', { message: (err && err.message) || 'Device error' });
78                });
79                device.on('tokenWillExpire', () => reportEvent('tokenWillExpire', {}));
80                device.on('incoming', () => { /* Phase 2 only — ignore inbound */ });
81                device.register();
82                return true;
83            } catch (e) {
84                reportEvent('error', { message: e.message || 'register failed' });
85                return false;
86            }
87        },
88
89        connect: async function (toNumber, callLogId, initiatedByUserId) {
90            if (isSdkUnavailable()) {
91                reportEvent('sdkUnavailable', { message: 'Voice library failed to load. Hard refresh the page (Ctrl+Shift+R), or contact support.' });
92                return false;
93            }
94            if (!device) {
95                reportEvent('error', { message: 'Device not registered' });
96                return false;
97            }
98            try {
99                activeCall = await device.connect({
100                    params: {
101                        To: toNumber,
102                        callLogId: String(callLogId),
103                        initiatedByUserId: initiatedByUserId || ''
104                    }
105                });
106                attachCallEvents(activeCall);
107                return true;
108            } catch (e) {
109                reportEvent('error', { message: e.message || 'connect failed' });
110                return false;
111            }
112        },
113
114        disconnect: function () {
115            if (activeCall) {
116                try { activeCall.disconnect(); } catch { /* ignore */ }
117            }
118        },
119
120        mute: function (shouldMute) {
121            if (activeCall) {
122                try { activeCall.mute(!!shouldMute); } catch { /* ignore */ }
123            }
124        },
125
126        refreshToken: function (token) {
127            if (device) {
128                try { device.updateToken(token); reportEvent('ready', {}); } catch (e) {
129                    reportEvent('error', { message: e.message || 'updateToken failed' });
130                }
131            }
132        },
133
134        unregister: function () {
135            try { if (activeCall) activeCall.disconnect(); } catch { /* ignore */ }
136            try { if (device) device.unregister(); } catch { /* ignore */ }
137            activeCall = null;
138            device = null;
139        }
140    };
141})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.