Files
twilio-wp-plugin/mobile/lib/providers/agent_provider.dart
Claude 5c6932f1d1
All checks were successful
Create Release / build (push) Successful in 4s
Add TWP Softphone Flutter app and complete mobile backend API
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>
2026-03-06 13:01:23 -08:00

89 lines
2.1 KiB
Dart

import 'dart:async';
import 'package:flutter/foundation.dart';
import '../models/agent_status.dart';
import '../models/queue_state.dart';
import '../services/api_client.dart';
import '../services/sse_service.dart';
class AgentProvider extends ChangeNotifier {
final ApiClient _api;
final SseService _sse;
AgentStatus? _status;
List<QueueInfo> _queues = [];
bool _sseConnected = false;
StreamSubscription? _sseSub;
StreamSubscription? _connSub;
AgentStatus? get status => _status;
List<QueueInfo> get queues => _queues;
bool get sseConnected => _sseConnected;
AgentProvider(this._api, this._sse) {
_connSub = _sse.connectionState.listen((connected) {
_sseConnected = connected;
notifyListeners();
});
_sseSub = _sse.events.listen(_handleSseEvent);
}
Future<void> fetchStatus() async {
try {
final response = await _api.dio.get('/agent/status');
_status = AgentStatus.fromJson(response.data);
notifyListeners();
} catch (_) {}
}
Future<void> updateStatus(AgentStatusValue newStatus) async {
final statusStr = newStatus.name;
try {
await _api.dio.post('/agent/status', data: {
'status': statusStr,
'is_logged_in': true,
});
_status = AgentStatus(
status: newStatus,
isLoggedIn: true,
currentCallSid: _status?.currentCallSid,
);
notifyListeners();
} catch (_) {}
}
Future<void> fetchQueues() async {
try {
final response = await _api.dio.get('/queues/state');
final data = response.data;
_queues = (data['queues'] as List)
.map((q) => QueueInfo.fromJson(q as Map<String, dynamic>))
.toList();
notifyListeners();
} catch (_) {}
}
Future<void> refresh() async {
await Future.wait([fetchStatus(), fetchQueues()]);
}
void _handleSseEvent(SseEvent event) {
switch (event.event) {
case 'call_enqueued':
case 'call_dequeued':
fetchQueues();
break;
case 'agent_status_changed':
fetchStatus();
break;
}
}
@override
void dispose() {
_sseSub?.cancel();
_connSub?.cancel();
super.dispose();
}
}