Add call history, dark mode toggle, caller ID persistence, and refactor phone page

Phone page improvements:
- Clear caller number display after call disconnects
- Add "Recent" tab with session call history (tap to call back)
- Persist outbound caller ID selection in localStorage
- Fix button overlap with proper z-index layering
- Add manual dark mode toggle (System/Light/Dark) in Settings
- Improve dark mode CSS for all UI elements

Refactor phone page into separate files:
- assets/mobile/phone.css (848 lines) — all CSS
- assets/mobile/phone.js (1065 lines) — all JavaScript
- assets/mobile/phone-template.php (267 lines) — HTML template
- includes/class-twp-mobile-phone-page.php (211 lines) — PHP controller
- PHP values passed to JS via window.twpConfig bridge

Flutter app:
- Replace FAB with slim AppBar (refresh + menu buttons)
- Fix dark mode colors using theme-aware colorScheme

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-03-10 10:02:04 -07:00
parent 621b0890a9
commit d00a906d07
5 changed files with 2231 additions and 1828 deletions

View File

@@ -194,35 +194,6 @@ class _PhoneScreenState extends State<PhoneScreen> with WidgetsBindingObserver {
return result ?? false;
}
void _showMenu() {
showModalBottomSheet(
context: context,
builder: (context) => SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.refresh),
title: const Text('Reload'),
onTap: () {
Navigator.pop(context);
_controller.reload();
},
),
ListTile(
leading: const Icon(Icons.logout),
title: const Text('Logout'),
onTap: () {
Navigator.pop(context);
_confirmLogout();
},
),
],
),
),
);
}
void _confirmLogout() async {
final result = await showDialog<bool>(
context: context,
@@ -256,7 +227,45 @@ class _PhoneScreenState extends State<PhoneScreen> with WidgetsBindingObserver {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: _hasError
? null
: AppBar(
toolbarHeight: 40,
titleSpacing: 12,
title: Text(
'TWP Softphone',
style: Theme.of(context).textTheme.titleSmall,
),
actions: [
IconButton(
icon: const Icon(Icons.refresh, size: 20),
tooltip: 'Reload',
visualDensity: VisualDensity.compact,
onPressed: () => _controller.reload(),
),
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert, size: 20),
tooltip: 'Menu',
padding: EdgeInsets.zero,
onSelected: (value) {
if (value == 'logout') _confirmLogout();
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'logout',
child: ListTile(
leading: Icon(Icons.logout),
title: Text('Logout'),
dense: true,
contentPadding: EdgeInsets.zero,
),
),
],
),
],
),
body: SafeArea(
top: _hasError, // AppBar already handles top safe area when visible
child: Stack(
children: [
if (!_hasError) WebViewWidget(controller: _controller),
@@ -266,34 +275,33 @@ class _PhoneScreenState extends State<PhoneScreen> with WidgetsBindingObserver {
],
),
),
floatingActionButton: (!_hasError && !_loading)
? FloatingActionButton.small(
onPressed: _showMenu,
child: const Icon(Icons.more_vert),
)
: null,
),
);
}
Widget _buildErrorView() {
final colorScheme = Theme.of(context).colorScheme;
return Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.wifi_off, size: 64, color: Colors.grey),
Icon(Icons.wifi_off, size: 64, color: colorScheme.onSurfaceVariant),
const SizedBox(height: 16),
const Text(
Text(
'Connection Error',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
const SizedBox(height: 8),
Text(
_errorMessage ?? 'Could not load the phone page.',
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.grey),
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
const SizedBox(height: 24),
FilledButton.icon(