Files
siprouter/ts_web/elements/sipproxy-app.ts
Juergen Kunz f3e1c96872 initial commit — SIP B2BUA + WebRTC bridge with Rust codec engine
Full-featured SIP router with multi-provider trunking, browser softphone
via WebRTC, real-time Opus/G.722/PCM transcoding in Rust, RNNoise ML
noise suppression, Kokoro neural TTS announcements, and a Lit-based
web dashboard with live call monitoring and REST API.
2026-04-09 23:03:55 +00:00

91 lines
3.3 KiB
TypeScript

import { DeesElement, customElement, html, css, cssManager, state, type TemplateResult } from '../plugins.js';
import { deesCatalog } from '../plugins.js';
import { NotificationManager } from '../state/notification-manager.js';
import { appRouter } from '../router.js';
import { SipproxyViewOverview } from './sipproxy-view-overview.js';
import { SipproxyViewCalls } from './sipproxy-view-calls.js';
import { SipproxyViewPhone } from './sipproxy-view-phone.js';
import { SipproxyViewContacts } from './sipproxy-view-contacts.js';
import { SipproxyViewProviders } from './sipproxy-view-providers.js';
import { SipproxyViewLog } from './sipproxy-view-log.js';
const VIEW_TABS = [
{ name: 'Overview', iconName: 'lucide:layoutDashboard', element: SipproxyViewOverview },
{ name: 'Calls', iconName: 'lucide:phone', element: SipproxyViewCalls },
{ name: 'Phone', iconName: 'lucide:headset', element: SipproxyViewPhone },
{ name: 'Contacts', iconName: 'lucide:contactRound', element: SipproxyViewContacts },
{ name: 'Providers', iconName: 'lucide:server', element: SipproxyViewProviders },
{ name: 'Log', iconName: 'lucide:scrollText', element: SipproxyViewLog },
];
// Map slug -> tab for routing.
const SLUG_TO_TAB = new Map(VIEW_TABS.map((t) => [t.name.toLowerCase(), t]));
@customElement('sipproxy-app')
export class SipproxyApp extends DeesElement {
private notificationManager = new NotificationManager();
private appdash: InstanceType<typeof deesCatalog.DeesSimpleAppDash> | null = null;
public static styles = [
cssManager.defaultStyles,
css`
:host { display: block; height: 100%; }
dees-simple-appdash { height: 100%; }
`,
];
private suppressViewSelectEvent = false;
async firstUpdated() {
this.appdash = this.shadowRoot?.querySelector('dees-simple-appdash') as InstanceType<typeof deesCatalog.DeesSimpleAppDash>;
if (this.appdash) {
this.notificationManager.init(this.appdash);
// Listen for user tab selections — sync URL.
this.appdash.addEventListener('view-select', ((e: CustomEvent) => {
if (this.suppressViewSelectEvent) return;
const viewName: string = e.detail?.view?.name || e.detail?.name || '';
const slug = viewName.toLowerCase();
if (slug && slug !== appRouter.getCurrentView()) {
appRouter.navigateTo(slug as any, true);
}
}) as EventListener);
// Wire up router -> appdash (for browser back/forward).
appRouter.setNavigateHandler((view) => {
const tab = SLUG_TO_TAB.get(view);
if (tab && this.appdash) {
this.suppressViewSelectEvent = true;
this.appdash.loadView(tab);
this.suppressViewSelectEvent = false;
}
});
// Deep link: if URL isn't "overview", navigate to the right tab.
const initial = appRouter.getCurrentView();
if (initial !== 'overview') {
const tab = SLUG_TO_TAB.get(initial);
if (tab) {
this.suppressViewSelectEvent = true;
this.appdash.loadView(tab);
this.suppressViewSelectEvent = false;
}
}
}
}
disconnectedCallback() {
super.disconnectedCallback();
this.notificationManager.destroy();
}
public render(): TemplateResult {
return html`
<dees-simple-appdash
.name=${'SipRouter'}
.viewTabs=${VIEW_TABS}
></dees-simple-appdash>
`;
}
}