Add TWP Softphone Flutter app and complete mobile backend API
All checks were successful
Create Release / build (push) Successful in 4s

Backend: Add /voice/token endpoint with AccessToken + VoiceGrant for
mobile VoIP, implement unhold_call() with call leg detection, wire FCM
push notifications into call queue and webhook missed call handlers,
add data-only FCM message support for Android background wake, and add
Twilio API Key / Push Credential settings fields.

Flutter app: Full softphone with Twilio Voice SDK integration, JWT auth
with auto-refresh, SSE real-time queue updates, FCM push notifications,
Material 3 UI with dashboard, active call screen, dialpad, and call
controls (mute/speaker/hold/transfer).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-03-06 13:01:23 -08:00
parent 03692608cc
commit 5c6932f1d1
49 changed files with 3243 additions and 28 deletions

View File

@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
class Dialpad extends StatelessWidget {
final void Function(String digit) onDigit;
final VoidCallback onClose;
const Dialpad({super.key, required this.onDigit, required this.onClose});
static const _keys = [
['1', '2', '3'],
['4', '5', '6'],
['7', '8', '9'],
['*', '0', '#'],
];
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 48),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
..._keys.map((row) => Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: row
.map((key) => Padding(
padding: const EdgeInsets.all(4),
child: InkWell(
onTap: () => onDigit(key),
borderRadius: BorderRadius.circular(40),
child: Container(
width: 64,
height: 64,
alignment: Alignment.center,
child: Text(
key,
style: Theme.of(context)
.textTheme
.headlineSmall,
),
),
),
))
.toList(),
)),
const SizedBox(height: 8),
TextButton(
onPressed: onClose,
child: const Text('Close'),
),
],
),
);
}
}