import 'package:flutter/material.dart'; import 'package:flutter_countdown_timer/index.dart'; import 'package:jiffy/jiffy.dart'; import 'package:lottie/lottie.dart'; import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/lottie_consts.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; import 'package:mohem_flutter_app/models/itg/advertisement.dart' as ads; import 'package:mohem_flutter_app/models/itg/itg_response_model.dart' as itgres; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/my_attendance/services_menu_list_screen.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/my_video_progress_indicator.dart'; import 'package:provider/provider.dart'; import 'package:sizer/sizer.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:video_player/video_player.dart'; class ITGAdsScreen extends StatefulWidget { const ITGAdsScreen({Key? key}) : super(key: key); @override _ITGAdsScreenState createState() => _ITGAdsScreenState(); } class _ITGAdsScreenState extends State { late Future _futureController; VideoPlayerController? _controller; bool skip = false; bool isVideo = false; bool isImage = false; bool isAudio = false; String ext = ''; bool isTextURL = false; // late File imageFile; late String imageUrl; itgres.ItgResponseData? advertisementPayload; ads.Advertisement? advertisementData; dynamic data; String? masterID; int videoDuration = 0; bool hasTimerEndedBool = false; final ValueNotifier hasTimerEnded = ValueNotifier(false); late CountdownTimerController timerController; CountdownTimerController? deadlineTimerController; void checkFileType() { if (advertisementData!.viewAttachFileColl!.first.contentType!.toLowerCase() == "text") { isTextURL = true; } else { String? rFile = advertisementData!.viewAttachFileColl!.first.base64String; try { rFile = advertisementData!.viewAttachFileColl!.where((element) => element.languageId == AppState().getLanguageID(context)).toList().first.base64String; } catch (ex) {} String? rFileExt = advertisementData!.viewAttachFileColl!.first.fileName; ext = "." + rFileExt!.split(".").last.toLowerCase(); if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif") { // await processImage(); imageUrl = rFile!; isImage = true; } else { if (ext == ".aac") { isAudio = true; } isVideo = true; _futureController = createVideoPlayer(rFile!); } } // advertisementData?.actionButtonsColl!.forEach((element) { advertisementData?.actionButtonsColl!.removeWhere((element1) => element1.actionButtonId == advertisementData?.skipButtonId); //}); // setState(() {}); } void changeTimer(bool isTimerEnd) { hasTimerEndedBool = isTimerEnd; hasTimerEnded.value = !hasTimerEnded.value; } void onTimerEnd() { changeTimer(true); timerController.disposeTimer(); timerController.dispose(); } // // Future processImage(String encodedBytes) async { // try { // Uint8List decodedBytes = base64Decode(encodedBytes.split("base64,").last); // Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); // 1 // imageFile = Io.File("${appDocumentsDirectory.path}/addImage$ext"); // imageFile.writeAsBytesSync(decodedBytes); // } catch (e) { // logger.d(e); // } // } Future createVideoPlayer(String encodedBytes) async { try { VideoPlayerController controller = VideoPlayerController.network(advertisementData!.viewAttachFileColl!.first.base64String!); await controller.initialize(); await controller.play(); await controller.setVolume(1.0); await controller.setLooping(false); return controller; } catch (e) { print(e); return VideoPlayerController.network("https://apimohemmweb.cloudsolutions.com.sa/ErmAttachment/compressedvideo.mp4"); } } void initTimer() { Future.delayed(const Duration(seconds: 5), () { skip = true; // setState(() {}); }); } String getDeadlineText() { if (advertisementPayload?.deadline == null) { return ""; } try { // Parse the deadline string using Jiffy (format: MM/dd/yyyy hh:mm:ss aa) Jiffy deadlineJiffy = Jiffy.parse(advertisementPayload!.deadline!, pattern: "MM/dd/yyyy hh:mm:ss aa"); Jiffy nowJiffy = Jiffy.now(); // Calculate days left int daysLeft = deadlineJiffy.diff(nowJiffy, unit: Unit.day).toInt(); if (daysLeft < 0) { // Deadline has passed int daysAgo = daysLeft.abs(); if (AppState().isArabic(context)) { return "انتهى الموعد النهائي منذ $daysAgo ${daysAgo == 1 ? 'يوم' : 'أيام'}"; } else { return "Deadline passed $daysAgo ${daysAgo == 1 ? 'day' : 'days'} ago"; } } else if (daysLeft == 0) { // Deadline is today if (AppState().isArabic(context)) { return "الموعد النهائي اليوم!"; } else { return "Deadline is today!"; } } else if (daysLeft == 1) { // Deadline is tomorrow if (AppState().isArabic(context)) { return "الموعد النهائي غداً"; } else { return "Deadline is tomorrow"; } } else { // Deadline is in the future if (AppState().isArabic(context)) { return "متبقي $daysLeft ${daysLeft <= 10 ? 'أيام' : 'يوم'}"; } else { return "$daysLeft ${daysLeft == 1 ? 'day' : 'days'} left"; } } } catch (e) { logger.e("Error parsing deadline: $e"); return "Deadline: ${Utils.formatDate(advertisementPayload!.deadline)}"; } } void initDeadlineTimer() { if (advertisementPayload?.deadline == null) return; try { // Parse the deadline string using Jiffy (format: MM/dd/yyyy hh:mm:ss aa) Jiffy deadlineJiffy = Jiffy.parse(advertisementPayload!.deadline!, pattern: "MM/dd/yyyy hh:mm:ss aa"); DateTime deadlineDate = deadlineJiffy.dateTime; int endTime = deadlineDate.millisecondsSinceEpoch; int currentTime = DateTime.now().millisecondsSinceEpoch; // Only create timer if deadline is in the future if (endTime > currentTime) { deadlineTimerController = CountdownTimerController( endTime: endTime, onEnd: () { // Timer ended, could trigger a refresh or notification logger.d("Deadline timer ended"); }, ); } } catch (e) { logger.e("Error initializing deadline timer: $e"); } } @override void dispose() { _controller?.dispose(); deadlineTimerController?.dispose(); super.dispose(); } // Helper method to get localized text String _getLocalizedText(String arabic, String english) { return AppState().isArabic(context) ? arabic : english; } // Build advertisement content text Widget _buildAdvertisementContent() { String content = AppState().isArabic(context) ? advertisementData!.viewAttachFileColl![0].base64String! : advertisementData!.viewAttachFileColl![1].base64String!; return content.toText16(color: MyColors.darkTextColor); } // Build deadline section with timer Widget _buildDeadlineSection() { if (deadlineTimerController != null) { return Center( child: Column(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [_buildEndDateText(), 4.height, _buildTimeRemainingRow(), 8.height]), ); } else { return Center(child: _buildEndDateText(isLargeText: true)); } } // Build end date text Widget _buildEndDateText({bool isLargeText = false}) { String label = _getLocalizedText("تاريخ الانتهاء: ", "End Date: "); String fullText = label + advertisementPayload!.deadline!; return isLargeText ? fullText.toText16(color: MyColors.darkTextColor) : fullText.toText14(color: MyColors.darkTextColor); } // Build time remaining row with timer Widget _buildTimeRemainingRow() { return Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [_getLocalizedText("الوقت المتبقي", "Time Remaining").toText16(color: MyColors.darkTextColor, isBold: true), 8.width, _buildCountdownTimer()], ); } // Build countdown timer widget Widget _buildCountdownTimer() { return CountdownTimer( controller: deadlineTimerController, endWidget: _getLocalizedText("انتهى الموعد النهائي!", "Deadline Expired!").toText16(color: MyColors.redColor, isBold: true), textStyle: const TextStyle(color: MyColors.darkTextColor, fontSize: 16, letterSpacing: -0.48, fontWeight: FontWeight.bold), ); } // Build action buttons list Widget _buildActionButtons() { return ListView.separated( itemCount: advertisementData!.actionButtonsColl?.length ?? 0, separatorBuilder: (cxt, index) => 16.height, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemBuilder: (cxt, index) { var actionButton = advertisementData!.actionButtonsColl![index]; String btnText = _getLocalizedText(actionButton.btnTextAr, actionButton.btnTextEn); return DefaultButton(btnText, () => _handleButtonAction(actionButton)); }, ); } // Handle button action based on type Future _handleButtonAction(actionButton) async { String actionValue = actionButton.actionValue.toLowerCase(); if (actionValue == "skip") { Navigator.pop(context); return; } String actionType = actionButton.actionType?.toLowerCase() ?? ''; if (actionType == 'redirect' && actionValue == 'external') { await _handleExternalRedirect(actionButton.url); } else if (actionType == 'redirect' && actionValue == 'internal') { await _handleInternalRedirect(actionButton.url); } else if (actionType == 'eit' && actionValue == 'internal') { await _handleEitInternal(actionButton.url); } else { await _handleExternalRedirect(actionButton.iconOrImage); } } // Handle external URL redirect Future _handleExternalRedirect(String url) async { Uri uri = Uri.parse(url); await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) { Utils.showToast('Could not launch'); }); Navigator.pop(context); } // Handle internal route navigation Future _handleInternalRedirect(String url) async { await Navigator.pushNamed(context, url); } // Handle EIT internal navigation Future _handleEitInternal(String url) async { var pro = Provider.of(context, listen: false); List menuList = pro.getMenuEntriesList?.where((element) => element.parentMenuName == url && (element.menuEntryType == "FUNCTION" || element.menuEntryType == "MENU")).toList() ?? []; Navigator.pushNamed(context, AppRoutes.servicesMenuListScreen, arguments: ServicesMenuListScreenParams(advertisementData!.advertisementTitle!, menuList)); } @override Widget build(BuildContext context) { data = ModalRoute.of(context)!.settings.arguments; advertisementPayload ??= data["advertisement"] as itgres.ItgResponseData; advertisementData = advertisementPayload!.advertisement; masterID ??= data["masterId"]; if (advertisementData != null) { checkFileType(); if (isTextURL) { if (deadlineTimerController == null) { initDeadlineTimer(); } } else { videoDuration = advertisementData?.durationInSeconds ?? 0; timerController = CountdownTimerController(endTime: DateTime.now().millisecondsSinceEpoch + 1000 * videoDuration, onEnd: onTimerEnd); } } return Scaffold( backgroundColor: isTextURL ? Colors.white : Colors.black, appBar: isTextURL ? AppBar( centerTitle: true, automaticallyImplyLeading: false, backgroundColor: Colors.white, title: (AppState().isArabic(context) ? advertisementData!.advertisementTitleAr! : advertisementData!.advertisementTitle!).toText24(color: MyColors.darkTextColor, isBold: true), ) : null, body: Stack( children: [ if (isVideo) FutureBuilder( future: _futureController, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) { _controller = snapshot.data as VideoPlayerController; return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Center( child: isAudio ? Lottie.asset(MyLottieConsts.audioPlaybackLottie) : AspectRatio( aspectRatio: _controller!.value.aspectRatio, child: Stack( alignment: Alignment.bottomCenter, children: [ VideoPlayer(_controller!), if (advertisementData?.isOptional ?? false) Positioned( top: 4, right: 8, child: Container( padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4), decoration: BoxDecoration(color: Colors.white.withOpacity(0.5), borderRadius: BorderRadius.circular(30)), child: Row(mainAxisSize: MainAxisSize.min, children: [Text(AppState().isArabic(context) ? "يتخطى" : "Skip"), Icon(Icons.skip_next)]), ).onPress(() { Navigator.pop(context); DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Skip").then((value) { logger.d(value); }); }), ), ], ), ), ), 30.height, CountdownTimer( controller: timerController, endTime: DateTime.now().millisecondsSinceEpoch + 1000 * videoDuration, endWidget: "00:00:00".toText16(color: Colors.white, isBold: true), textStyle: const TextStyle(color: Colors.white, fontSize: 16, letterSpacing: -0.48, fontWeight: FontWeight.bold), ), 50.height, // if (advertisementData?.isOptional ?? false) // DefaultButton(AppState().isArabic(context) ? "يتخطى" : "Skip", () async { // Navigator.pop(context); // DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Skip").then((value) { // logger.d(value); // }); // }).paddingOnly(left: 60.0, right: 60.0, top: 8, bottom: 8), ValueListenableBuilder( valueListenable: hasTimerEnded, builder: (context, val, child) { // if (hasTimerEndedBool) { return ListView.builder( padding: EdgeInsets.zero, itemCount: advertisementData?.actionButtonsColl!.length, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) { String? btnText = AppState().isArabic(context) ? advertisementData?.actionButtonsColl![index].btnTextAr : advertisementData?.actionButtonsColl![index].btnTextEn; // bool isAcknowledge = advertisementData?.actionButtonsColl![index].actionButtonId == 11; bool isAcknowledge = advertisementData?.actionButtonsColl![index].actionButtonId == 17; if (isAcknowledge) { return Stack( alignment: Alignment.center, children: [ MyVideoProgressIndicator( _controller!, allowScrubbing: false, //padding: EdgeInsets.only(left: 60, right: 60), ), DefaultButton( btnText!, hasTimerEndedBool ? () async { Navigator.pop(context); DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, advertisementData?.actionButtonsColl![index].actionValue).then(( value, ) { logger.d(value); }); } : null, disabledColor: Colors.transparent, textColor: !hasTimerEndedBool ? Colors.white.withOpacity(.5) : Colors.white, ).paddingOnly(left: 60.0, right: 60.0, top: 8, bottom: 8), ], ); } return DefaultButton(btnText!, () async { Navigator.pop(context); DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, advertisementData?.actionButtonsColl![index].actionValue).then((value) { logger.d(value); }); }).paddingOnly(left: 60.0, right: 60.0, top: 8, bottom: 8); }, // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( // crossAxisCount: 1, // childAspectRatio: (7.0), // ), ); // Row( // mainAxisAlignment: MainAxisAlignment.center, // children: [ // Container(padding: const EdgeInsets.all(16), decoration: Utils.containerRadius(MyColors.white, 10), child: const Icon(Icons.thumb_up, color: MyColors.gradiantEndColor)) // .onPress(() { // try { // Navigator.pop(context); // DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Like").then((value) { // logger.d(value); // }); // } catch (ex) { // logger.wtf(ex); // Utils.handleException(ex, context, null); // } // }), // 20.width, // Container( // padding: const EdgeInsets.all(16), decoration: Utils.containerRadius(MyColors.white, 10), child: const Icon(Icons.thumb_down, color: MyColors.gradiantEndColor)) // .onPress(() { // try { // Navigator.pop(context); // DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Dislike").then((value) { // logger.d(value); // }); // } catch (ex) { // logger.wtf(ex); // Utils.handleException(ex, context, null); // } // }), // ], // ); // } else { // return Container(); // } }, ), 20.height, // if (advertisementData?.isOptional ?? false) // GridView.builder( // padding: EdgeInsets.zero, // itemCount: advertisementData?.actionButtonsColl!.length, // shrinkWrap: true, // physics: const NeverScrollableScrollPhysics(), // itemBuilder: (context, index) { // String? btnText = AppState().isArabic(context) ? advertisementData?.actionButtonsColl![index].btnTextAr : advertisementData?.actionButtonsColl![index].btnTextEn; // return DefaultButton(btnText!, () async { // Navigator.pop(context); // DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, advertisementData?.actionButtonsColl![index].actionValue).then((value) { // logger.d(value); // }); // }).paddingAll(8); // }, // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( // crossAxisCount: 2, // childAspectRatio: (4.0), // ), // ) // DefaultButton(AppState().isArabic(context) ? advertisementData?.skipButtonTextAr ?? "Skip" : advertisementData?.skipButtonTextEn ?? "Skip", () async { // Navigator.pop(context); // DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Skip").then((value) { // logger.d(value); // }); // }).paddingOnly(left: 100, right: 100) ], ); } else { return const Center(child: CircularProgressIndicator()); } }, ), if (isImage) SingleChildScrollView( padding: const EdgeInsets.only(top: 60, left: 12, right: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Image.network(imageUrl), 50.height, Container(padding: const EdgeInsets.all(16), decoration: Utils.containerRadius(MyColors.white, 10), child: const Icon(Icons.thumb_up, color: MyColors.gradiantEndColor)).onPress(() { try { Navigator.pop(context); DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, "Like").then((value) { logger.d(value); }); } catch (ex) { logger.wtf(ex); Utils.handleException(ex, context, null); } }), ], ), ), if (isTextURL) SingleChildScrollView( padding: const EdgeInsets.all(21), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [_buildAdvertisementContent(), 24.height, _buildDeadlineSection(), 24.height, _buildActionButtons()], ), ), ], ), ); } }