mirror of
https://github.com/we-promise/sure.git
synced 2026-04-08 14:54:49 +00:00
* Wire ui layout and AI flags into mobile auth Include ui_layout and ai_enabled in mobile login/signup/SSO payloads, add an authenticated endpoint to enable AI from Flutter, and gate mobile navigation based on intro layout and AI consent flow. * Linter * Ensure write scope on enable_ai * Make sure AI is available before enabling it * Test improvements * PR comment * Fix review issues: test assertion bug, missing coverage, and Dart defaults (#985) - Fix login test to use ai_enabled? (method) instead of ai_enabled (column) to match what mobile_user_payload actually serializes - Add test for enable_ai when ai_available? returns false (403 path) - Default aiEnabled to false when user is null in AuthProvider to avoid showing AI as available before authentication completes - Remove extra blank lines in auth_provider.dart and auth_service.dart https://claude.ai/code/session_01LEYYmtsDBoqizyihFtkye4 Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
164 lines
4.2 KiB
Dart
164 lines
4.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../providers/auth_provider.dart';
|
|
import 'dashboard_screen.dart';
|
|
import 'chat_list_screen.dart';
|
|
import 'more_screen.dart';
|
|
import 'settings_screen.dart';
|
|
|
|
class MainNavigationScreen extends StatefulWidget {
|
|
const MainNavigationScreen({super.key});
|
|
|
|
@override
|
|
State<MainNavigationScreen> createState() => _MainNavigationScreenState();
|
|
}
|
|
|
|
class _MainNavigationScreenState extends State<MainNavigationScreen> {
|
|
int _currentIndex = 0;
|
|
final _dashboardKey = GlobalKey<DashboardScreenState>();
|
|
|
|
List<Widget> _buildScreens(bool introLayout) {
|
|
final screens = <Widget>[];
|
|
|
|
if (!introLayout) {
|
|
screens.add(DashboardScreen(key: _dashboardKey));
|
|
}
|
|
|
|
screens.add(const ChatListScreen());
|
|
|
|
if (!introLayout) {
|
|
screens.add(const MoreScreen());
|
|
}
|
|
|
|
screens.add(const SettingsScreen());
|
|
|
|
return screens;
|
|
}
|
|
|
|
List<NavigationDestination> _buildDestinations(bool introLayout) {
|
|
final destinations = <NavigationDestination>[];
|
|
|
|
if (!introLayout) {
|
|
destinations.add(
|
|
const NavigationDestination(
|
|
icon: Icon(Icons.home_outlined),
|
|
selectedIcon: Icon(Icons.home),
|
|
label: 'Home',
|
|
),
|
|
);
|
|
}
|
|
|
|
destinations.add(
|
|
const NavigationDestination(
|
|
icon: Icon(Icons.chat_bubble_outline),
|
|
selectedIcon: Icon(Icons.chat_bubble),
|
|
label: 'AI Chat',
|
|
),
|
|
);
|
|
|
|
if (!introLayout) {
|
|
destinations.add(
|
|
const NavigationDestination(
|
|
icon: Icon(Icons.more_horiz),
|
|
selectedIcon: Icon(Icons.more_horiz),
|
|
label: 'More',
|
|
),
|
|
);
|
|
}
|
|
|
|
destinations.add(
|
|
const NavigationDestination(
|
|
icon: Icon(Icons.settings_outlined),
|
|
selectedIcon: Icon(Icons.settings),
|
|
label: 'Settings',
|
|
),
|
|
);
|
|
|
|
return destinations;
|
|
}
|
|
|
|
Future<bool> _showEnableAiPrompt() async {
|
|
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
|
|
|
final shouldEnable = await showDialog<bool>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Turn on AI Chat?'),
|
|
content: const Text('AI Chat is currently disabled in your account settings. Would you like to turn it on now?'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(false),
|
|
child: const Text('Not now'),
|
|
),
|
|
FilledButton(
|
|
onPressed: () => Navigator.of(context).pop(true),
|
|
child: const Text('Turn on AI'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
|
|
if (shouldEnable != true) {
|
|
return false;
|
|
}
|
|
|
|
final enabled = await authProvider.enableAi();
|
|
|
|
if (!enabled && mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(authProvider.errorMessage ?? 'Unable to enable AI right now.'),
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Consumer<AuthProvider>(
|
|
builder: (context, authProvider, _) {
|
|
final introLayout = authProvider.isIntroLayout;
|
|
final screens = _buildScreens(introLayout);
|
|
final destinations = _buildDestinations(introLayout);
|
|
|
|
if (_currentIndex >= screens.length) {
|
|
_currentIndex = 0;
|
|
}
|
|
|
|
final chatIndex = introLayout ? 0 : 1;
|
|
final homeIndex = 0;
|
|
|
|
return Scaffold(
|
|
body: IndexedStack(
|
|
index: _currentIndex,
|
|
children: screens,
|
|
),
|
|
bottomNavigationBar: NavigationBar(
|
|
selectedIndex: _currentIndex,
|
|
onDestinationSelected: (index) async {
|
|
if (index == chatIndex && !authProvider.aiEnabled) {
|
|
final enabled = await _showEnableAiPrompt();
|
|
if (!enabled) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
setState(() {
|
|
_currentIndex = index;
|
|
});
|
|
|
|
if (!introLayout && index == homeIndex) {
|
|
_dashboardKey.currentState?.reloadPreferences();
|
|
}
|
|
},
|
|
destinations: destinations,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|