You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cloudsolutions-atoms/lib/new_views/pages/land_page/land_page.dart

325 lines
13 KiB
Dart

import 'dart:convert';
import 'dart:developer' as dev;
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:test_sa/controllers/notification/firebase_notification_manger.dart';
import 'package:test_sa/controllers/notification/notification_manger.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/controllers/providers/settings/app_settings.dart';
import 'package:test_sa/dashboard_latest/dashboard_view.dart';
import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/models/enums/user_types.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/new_views/pages/land_page/create_request_type_bottom_sheet.dart';
import 'package:test_sa/new_views/pages/land_page/my_request/my_requests_page.dart';
import 'package:test_sa/new_views/pages/login_page.dart';
import 'package:test_sa/new_views/pages/settings_page.dart';
import 'package:test_sa/providers/work_order/vendor_provider.dart';
import 'package:test_sa/views/widgets/equipment/my_assets_page.dart';
import 'package:test_sa/modules/cx_module/chat/services/call_manager.dart';
import 'package:test_sa/modules/cx_module/chat/services/signalr_service.dart';
import 'package:test_sa/modules/cx_module/chat/chat_api_client.dart';
import 'package:test_sa/core/storage/auth_storage.dart';
import '../../../controllers/providers/settings/setting_provider.dart';
import '../../../views/widgets/dialogs/dialog.dart';
import '../../common_widgets/app_bottom_nav_bar.dart';
import '../../common_widgets/app_drawer.dart';
import 'contact_us_bottom_sheet.dart';
class LandPage extends StatefulWidget {
static const String routeName = "/land-page";
const LandPage({Key? key}) : super(key: key);
@override
State<LandPage> createState() => _LandPageState();
}
class _LandPageState extends State<LandPage> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
int currentPageIndex = 0;
bool showAppbar = true;
late List<Widget> _pages;
UserProvider? _userProvider;
bool isFCM = true;
@override
void initState() {
_pages = <Widget>[];
super.initState();
}
void checkLocalAuth() async {
if (await Provider.of<SettingProvider>(context, listen: false).auth.isDeviceSupported()) {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey(ASettings.localAuth)) {
(await Provider.of<SettingProvider>(context, listen: false)).setAuth(false);
showModalBottomSheet(
context: context,
useSafeArea: true,
backgroundColor: Colors.transparent,
builder: (context) => SafeArea(
child: Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: AppColor.bottomSheetColor(context),
borderRadius: const BorderRadius.only(topRight: Radius.circular(20), topLeft: Radius.circular(20)),
),
padding: EdgeInsets.symmetric(horizontal: 24.toScreenWidth, vertical: 24.toScreenHeight),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 40.toScreenWidth,
height: 5.toScreenHeight,
decoration: BoxDecoration(color: AppColor.neutral40, borderRadius: BorderRadius.circular(30)),
),
16.height,
Align(
alignment: AlignmentDirectional.centerStart,
child: (Platform.isAndroid ? "Fingerprint" : "Face ID").addTranslation.heading3(context).custom(fontWeight: FontWeight.w600),
),
16.height,
(Platform.isAndroid ? "fingerprint" : "faceid").toLottieAsset(height: 180),
16.height,
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(
"Let's set up Face ID/Fingerprint for quick and secure logins in the future".addTranslation,
style: AppTextStyles.bodyText,
)),
24.height,
AppFilledButton(
onPressed: () {
Navigator.pop(context);
Navigator.of(context).pushNamed(SettingsPage.id);
},
label: "Continue".addTranslation),
],
)),
),
);
}
}
}
@override
Widget build(BuildContext context) {
if (_userProvider == null) {
_userProvider = Provider.of<UserProvider>(context, listen: false);
if (_userProvider!.user != null && _userProvider!.user!.employeeIsHMG == false) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_userProvider!.getSwipeLastTransaction(userId: _userProvider!.user!.userID!);
_userProvider!.getSiteContactInfo();
if(!_userProvider!.isHouseKeeper) {
Provider.of<VendorProvider>(context, listen: false).getData();
}
});
}
_pages = <Widget>[
DashboardView(onDrawerPress: (() {
_scaffoldKey.currentState!.isDrawerOpen ? _scaffoldKey.currentState!.closeDrawer() : _scaffoldKey.currentState!.openDrawer();
})),
const MyRequestsPage(),
const SizedBox(),
const MyAssetsPage(fromBottomBar: true),
];
if (isFCM) {
FirebaseNotificationManger.initialized(context);
NotificationManger.initialisation((notificationDetails) {
FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!));
}, (id, title, body, payload) async {});
// ⭐ Initialize SignalR & CallManager ONCE at app startup
WidgetsBinding.instance.addPostFrameCallback((_) async {
await _initializeCallingSystem();
});
isFCM = false;
}
checkLocalAuth();
}
return WillPopScope(
onWillPop: () async {
bool result = await showDialog(
context: context,
builder: (_) => AAlertDialog(title: context.translation.signOut, content: context.translation.logoutAlert),
);
if (result) {
// Cleanup calling system before logout
await _cleanupCallingSystem();
bool isSuccess = await Provider.of<UserProvider>(context, listen: false).logout(context);
if (isSuccess) {
Provider.of<SettingProvider>(context, listen: false).resetSettings();
Provider.of<UserProvider>(context, listen: false).reset();
Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.routeName, (routes) => true);
}
}
return false;
},
child: Scaffold(
key: _scaffoldKey,
appBar: _pages.isEmpty
? PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: AppBarWidget(
onDrawerPress: () {
_scaffoldKey.currentState!.isDrawerOpen ? _scaffoldKey.currentState!.closeDrawer() : _scaffoldKey.currentState!.openDrawer();
},
))
: null,
drawer: const AppDrawer(),
body: _pages[currentPageIndex],
bottomNavigationBar: _pages.isEmpty
? null
: AppBottomNavigationBar(
selectedIndex: currentPageIndex,
onPressed: (index) {
if (index == 2) {
showModalBottomSheet(
context: context,
useSafeArea: true,
backgroundColor: Colors.white,
builder: (context) => CreateRequestTypeBottomSheet(),
);
} else if (index == 4) {
showModalBottomSheet(
context: context,
useSafeArea: true,
backgroundColor: Colors.transparent,
builder: (context) => const ContactUsBottomSheet(),
);
} else {
setState(() {
currentPageIndex = index;
});
if (index == 1) {
setState(() {
showAppbar = false;
});
} else {
setState(() {
showAppbar = true;
});
}
}
},
),
),
);
}
/// Initialize SignalR and CallManager once at app startup
/// This ensures calls work from ANY screen without CMDetailPage dependency
Future<void> _initializeCallingSystem() async {
try {
final user = _userProvider?.user;
if (user?.username == null) {
dev.log('⚠️ [LAND PAGE] User not logged in, skipping call initialization', name: 'LandPage');
return;
}
final myEmployeeNumber = user!.username!; // Use username as employee number
final userId = user.userID?.toString() ?? user.username!;
dev.log('🚀 [LAND PAGE] Initializing calling system...', name: 'LandPage');
final chatLoginResponse = await ChatApiClient().getChatLoginToken(
1002, // moduleId - using default
0, // referenceId - using default for app-level init
'App Init',
myEmployeeNumber,
myEmployeeNumber,
);
if (chatLoginResponse == null || chatLoginResponse.token == null) {
dev.log('❌ [LAND PAGE] Failed to get chat credentials', name: 'LandPage');
dev.log(' Response: ${chatLoginResponse?.toJson()}', name: 'LandPage');
return;
}
dev.log('✅ [LAND PAGE] Got chat credentials', name: 'LandPage');
dev.log(' User ID: ${chatLoginResponse.userId}', name: 'LandPage');
dev.log(' Token: ${chatLoginResponse.token!.substring(0, 20)}...', name: 'LandPage');
// CRITICAL: Save credentials to SharedPreferences for background/terminated app states
dev.log('💾 [LAND PAGE] Saving credentials to SharedPreferences...', name: 'LandPage');
final savedCredentials = await AuthStorage.saveCredentials(
accessToken: chatLoginResponse.token!,
userId: chatLoginResponse.userId.toString(),
employeeNumber: myEmployeeNumber,
);
if (savedCredentials) {
dev.log(' Background calls will now work', name: 'LandPage');
} else {
dev.log('⚠️ [LAND PAGE] Failed to save credentials', name: 'LandPage');
}
// Step 2: Initialize SignalR Service (SINGLETON - only once)
dev.log('🔌 [LAND PAGE] Initializing SignalR Service...', name: 'LandPage');
final signalRService = SignalRService();
final connected = await signalRService.initialize(
userId: chatLoginResponse.userId.toString(),
authToken: chatLoginResponse.token!,
conversationId: null, // No conversation at app level
);
if (!connected) {
dev.log('❌ [LAND PAGE] SignalR initialization failed', name: 'LandPage');
return;
}
dev.log('✅ [LAND PAGE] SignalR Service initialized', name: 'LandPage');
await CallManager().initialize(
userId: chatLoginResponse.userId.toString(),
authToken: chatLoginResponse.token!,
conversationId: null,
moduleId: '1002',
referenceId: '0',
employeeNumber: myEmployeeNumber,
);
dev.log('✅ [LAND PAGE] CallManager initialized', name: 'LandPage');
} catch (e, stackTrace) {
dev.log('❌ [LAND PAGE] Error initializing calling system: $e',
name: 'LandPage', error: e, stackTrace: stackTrace);
// Don't crash the app, just log the error
// User can still use the app, calls just won't work
}
}
/// Cleanup SignalR and CallManager on logout
Future<void> _cleanupCallingSystem() async {
try {
dev.log('🧹 [LAND PAGE] Cleaning up calling system...', name: 'LandPage');
// Reset CallManager
await CallManager().reset();
// Disconnect SignalR
await SignalRService().disconnect();
// CRITICAL: Clear stored credentials from SharedPreferences
dev.log('🗑️ [LAND PAGE] Clearing stored credentials...', name: 'LandPage');
await AuthStorage.clearCredentials();
dev.log('✅ [LAND PAGE] Calling system cleaned up', name: 'LandPage');
} catch (e) {
dev.log('⚠️ [LAND PAGE] Error during cleanup: $e', name: 'LandPage');
}
}
}