Replace native Twilio Voice SDK with WebView-based softphone
Rewrites the mobile app from a native Twilio Voice SDK integration (Android Telecom/ConnectionService) to a thin WebView shell that loads a standalone browser phone page from WordPress. This eliminates the buggy Android phone account registration, fixes frequent logouts by using 7-day WP session cookies instead of JWT tokens, and maintains all existing call features (dialpad, queues, hold, transfer, requeue, recording, caller ID, agent status). Server-side: - Add class-twp-mobile-phone-page.php: standalone /twp-phone/ endpoint with mobile-optimized UI, dark mode, tab navigation, and Flutter WebView JS bridge - Extend auth cookie to 7 days for phone agents - Add WP AJAX handler for FCM token registration (cookie auth) Flutter app (v2.0.0): - Replace 18 native files with 5-file WebView shell - Login via wp-login.php in WebView (auto-detect redirect on success) - Full-screen WebView with auto microphone grant for WebRTC - FCM push notifications preserved for queue alerts - Remove: twilio_voice, dio, provider, JWT auth, SSE, native call UI Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'services/api_client.dart';
|
||||
import 'providers/auth_provider.dart';
|
||||
import 'providers/agent_provider.dart';
|
||||
import 'providers/call_provider.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'screens/login_screen.dart';
|
||||
import 'screens/dashboard_screen.dart';
|
||||
import 'screens/phone_screen.dart';
|
||||
|
||||
class TwpSoftphoneApp extends StatefulWidget {
|
||||
const TwpSoftphoneApp({super.key});
|
||||
@@ -15,51 +11,77 @@ class TwpSoftphoneApp extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _TwpSoftphoneAppState extends State<TwpSoftphoneApp> {
|
||||
final _apiClient = ApiClient();
|
||||
static const _storage = FlutterSecureStorage();
|
||||
String? _serverUrl;
|
||||
bool _loading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkSavedSession();
|
||||
}
|
||||
|
||||
Future<void> _checkSavedSession() async {
|
||||
final url = await _storage.read(key: 'server_url');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_serverUrl = url;
|
||||
_loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onLoginSuccess(String serverUrl) {
|
||||
setState(() {
|
||||
_serverUrl = serverUrl;
|
||||
});
|
||||
}
|
||||
|
||||
void _onLogout() async {
|
||||
await _storage.delete(key: 'server_url');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_serverUrl = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onSessionExpired() {
|
||||
// Server URL is still saved, but session cookie is gone.
|
||||
// Show login screen but keep the server URL pre-filled.
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_serverUrl = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) {
|
||||
final auth = AuthProvider(_apiClient);
|
||||
auth.tryRestoreSession();
|
||||
return auth;
|
||||
},
|
||||
child: MaterialApp(
|
||||
title: 'TWP Softphone',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
colorSchemeSeed: Colors.blue,
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
colorSchemeSeed: Colors.blue,
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
),
|
||||
home: Consumer<AuthProvider>(
|
||||
builder: (context, auth, _) {
|
||||
if (auth.state == AuthState.authenticated) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => AgentProvider(
|
||||
auth.apiClient,
|
||||
auth.sseService,
|
||||
)..refresh(),
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => CallProvider(auth.voiceService),
|
||||
),
|
||||
],
|
||||
child: const DashboardScreen(),
|
||||
);
|
||||
}
|
||||
return const LoginScreen();
|
||||
},
|
||||
),
|
||||
return MaterialApp(
|
||||
title: 'TWP Softphone',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
colorSchemeSeed: Colors.blue,
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
colorSchemeSeed: Colors.blue,
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
),
|
||||
home: _loading
|
||||
? const Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
)
|
||||
: _serverUrl != null
|
||||
? PhoneScreen(
|
||||
serverUrl: _serverUrl!,
|
||||
onLogout: _onLogout,
|
||||
onSessionExpired: _onSessionExpired,
|
||||
)
|
||||
: LoginScreen(onLoginSuccess: _onLoginSuccess),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user