|
|
|
|
@ -6,7 +6,7 @@ import 'package:diplomaticquarterapp/core/service/client/base_app_client.dart';
|
|
|
|
|
import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart';
|
|
|
|
|
import 'package:diplomaticquarterapp/models/course/education_journey_insert_model.dart';
|
|
|
|
|
import 'package:diplomaticquarterapp/models/course/education_journey_list_model.dart';
|
|
|
|
|
import 'package:diplomaticquarterapp/models/course/education_journey_model.dart';
|
|
|
|
|
import 'package:diplomaticquarterapp/models/course/education_journey_model.dart' as ejm;
|
|
|
|
|
import 'package:diplomaticquarterapp/pages/learning/question_sheet.dart';
|
|
|
|
|
import 'package:diplomaticquarterapp/routes.dart';
|
|
|
|
|
import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart';
|
|
|
|
|
@ -15,6 +15,7 @@ import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
|
|
|
|
|
import 'package:diplomaticquarterapp/uitl/utils.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:video_player/video_player.dart';
|
|
|
|
|
import 'package:visibility_detector/visibility_detector.dart';
|
|
|
|
|
|
|
|
|
|
class CourseServiceProvider with ChangeNotifier {
|
|
|
|
|
AppSharedPreferences sharedPref = AppSharedPreferences();
|
|
|
|
|
@ -22,48 +23,56 @@ class CourseServiceProvider with ChangeNotifier {
|
|
|
|
|
AuthenticatedUser authUser = AuthenticatedUser();
|
|
|
|
|
AuthProvider authProvider = AuthProvider();
|
|
|
|
|
bool isDataLoaded = false;
|
|
|
|
|
int? selectedJourney;
|
|
|
|
|
int? _selectedJourney;
|
|
|
|
|
|
|
|
|
|
int get getSelectedJourney => _selectedJourney!;
|
|
|
|
|
|
|
|
|
|
set setSelectedJourney(int value) {
|
|
|
|
|
_selectedJourney = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String? _pageTitle;
|
|
|
|
|
|
|
|
|
|
String? get getPageTitle => _pageTitle;
|
|
|
|
|
|
|
|
|
|
set setPageTitle(String? value) {
|
|
|
|
|
_pageTitle = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<Datum>? data;
|
|
|
|
|
PatientEducationJourneyListModel? nabedJourneyResponse;
|
|
|
|
|
PatientEducationJourneyModel? courseData;
|
|
|
|
|
List<Topic>? courseTopics;
|
|
|
|
|
Consultation? consultation;
|
|
|
|
|
|
|
|
|
|
List<Data>? playedData = [];
|
|
|
|
|
ejm.PatientEducationJourneyModel? courseData;
|
|
|
|
|
List<ejm.Topic>? courseTopics;
|
|
|
|
|
ejm.Consultation? consultation;
|
|
|
|
|
List<ejm.ContentClass>? contentClasses;
|
|
|
|
|
|
|
|
|
|
//Main Video Controller & Timer
|
|
|
|
|
VideoPlayerState _videoState = VideoPlayerState.paused;
|
|
|
|
|
List<Data> nabedInsertDataPayload = [];
|
|
|
|
|
ejm.Content? playedContent;
|
|
|
|
|
|
|
|
|
|
VideoPlayerState get videoState => _videoState;
|
|
|
|
|
VideoPlayerController? controller;
|
|
|
|
|
|
|
|
|
|
Timer? timer;
|
|
|
|
|
|
|
|
|
|
// Learning Page
|
|
|
|
|
|
|
|
|
|
Future<dynamic> fetchPatientCoursesList() async {
|
|
|
|
|
print("====== Api Initiated =========");
|
|
|
|
|
void getCourses(BuildContext context) async {
|
|
|
|
|
GifLoaderDialogUtils.showMyDialog(context);
|
|
|
|
|
Map<String, dynamic> request;
|
|
|
|
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
|
|
|
|
var data = AuthenticatedUser.fromJson(await this.sharedPref.getObject(USER_PROFILE));
|
|
|
|
|
authUser = data;
|
|
|
|
|
}
|
|
|
|
|
request = {"Channel": 3, "TokenID": "@dm!n", "PatientID": 22335};
|
|
|
|
|
dynamic localRes;
|
|
|
|
|
await BaseAppClient().post(GET_PATIENT_COURSES_LIST, onSuccess: (response, statusCode) async {
|
|
|
|
|
print("====== Api Response =========");
|
|
|
|
|
print("${response["NabedJourneyResponseResult"]}");
|
|
|
|
|
localRes = PatientEducationJourneyListModel.fromJson(response);
|
|
|
|
|
dynamic response;
|
|
|
|
|
//{"Channel": 3, "TokenID": "@dm!n", "PatientID": 22335};
|
|
|
|
|
request = {"Channel": 3};
|
|
|
|
|
await BaseAppClient().post(GET_PATIENT_COURSES_LIST, onSuccess: (res, statusCode) async {
|
|
|
|
|
response = PatientEducationJourneyListModel.fromJson(res);
|
|
|
|
|
}, onFailure: (String error, int statusCode) {
|
|
|
|
|
GifLoaderDialogUtils.hideDialog(context);
|
|
|
|
|
Utils.showErrorToast(error);
|
|
|
|
|
throw error;
|
|
|
|
|
}, body: request);
|
|
|
|
|
return localRes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void getCourses(BuildContext context) async {
|
|
|
|
|
GifLoaderDialogUtils.showMyDialog(context);
|
|
|
|
|
dynamic response = await fetchPatientCoursesList();
|
|
|
|
|
GifLoaderDialogUtils.hideDialog(context);
|
|
|
|
|
if (response.nabedJourneyResponseResult != null) {
|
|
|
|
|
GifLoaderDialogUtils.hideDialog(context);
|
|
|
|
|
nabedJourneyResponse = response;
|
|
|
|
|
isDataLoaded = true;
|
|
|
|
|
data = nabedJourneyResponse!.nabedJourneyResponseResult!.data;
|
|
|
|
|
@ -72,40 +81,64 @@ class CourseServiceProvider with ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Detailed Page
|
|
|
|
|
|
|
|
|
|
Future<dynamic> fetchPatientCourseById() async {
|
|
|
|
|
print("====== Api Initiated =========");
|
|
|
|
|
void getCourseById(BuildContext context) async {
|
|
|
|
|
GifLoaderDialogUtils.showMyDialog(context);
|
|
|
|
|
Map<String, dynamic> request;
|
|
|
|
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
|
|
|
|
var data = AuthenticatedUser.fromJson(await this.sharedPref.getObject(USER_PROFILE));
|
|
|
|
|
authUser = data;
|
|
|
|
|
}
|
|
|
|
|
request = {"Channel": 3, "TokenID": "@dm!n", "JourneyID": selectedJourney};
|
|
|
|
|
dynamic localRes;
|
|
|
|
|
await BaseAppClient().post(GET_PATIENT_COURSE_BY_ID, onSuccess: (response, statusCode) async {
|
|
|
|
|
print("====== Api Response =========");
|
|
|
|
|
print("${response}");
|
|
|
|
|
localRes = response;
|
|
|
|
|
dynamic response;
|
|
|
|
|
// "TokenID": "@dm!n",
|
|
|
|
|
request = {"Channel": 3, "JourneyID": getSelectedJourney};
|
|
|
|
|
await BaseAppClient().post(GET_PATIENT_COURSE_BY_ID, onSuccess: (res, statusCode) async {
|
|
|
|
|
response = res;
|
|
|
|
|
}, onFailure: (String error, int statusCode) {
|
|
|
|
|
GifLoaderDialogUtils.hideDialog(context);
|
|
|
|
|
Utils.showErrorToast(error);
|
|
|
|
|
throw error;
|
|
|
|
|
}, body: request);
|
|
|
|
|
return localRes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void getCourseById(BuildContext context) async {
|
|
|
|
|
GifLoaderDialogUtils.showMyDialog(context);
|
|
|
|
|
dynamic response = await fetchPatientCourseById();
|
|
|
|
|
GifLoaderDialogUtils.hideDialog(context);
|
|
|
|
|
if (response != null) {
|
|
|
|
|
courseData = PatientEducationJourneyModel.fromRawJson(jsonEncode(response));
|
|
|
|
|
courseData = ejm.PatientEducationJourneyModel.fromRawJson(jsonEncode(response));
|
|
|
|
|
courseTopics = courseData!.nabedJourneyByIdResponseResult!.contentClasses!.first.topics;
|
|
|
|
|
contentClasses = courseData!.nabedJourneyByIdResponseResult!.contentClasses;
|
|
|
|
|
consultation = courseData!.nabedJourneyByIdResponseResult!.consultation;
|
|
|
|
|
|
|
|
|
|
// Future.delayed(Duration(seconds: 1), () {
|
|
|
|
|
// insertDetailedJourneyListData();
|
|
|
|
|
// });
|
|
|
|
|
}
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Learning Page
|
|
|
|
|
Future<void> postNabedJourneyData() async {
|
|
|
|
|
// GifLoaderDialogUtils.showMyDialog(context);
|
|
|
|
|
Map<String, dynamic> request;
|
|
|
|
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
|
|
|
|
var data = AuthenticatedUser.fromJson(await this.sharedPref.getObject(USER_PROFILE));
|
|
|
|
|
authUser = data;
|
|
|
|
|
}
|
|
|
|
|
dynamic response;
|
|
|
|
|
print(jsonEncode(nabedInsertDataPayload));
|
|
|
|
|
//"PatientID": 22335,
|
|
|
|
|
//"TokenID": "@dm!n",
|
|
|
|
|
request = {"LanguageID": 1, "data": nabedInsertDataPayload};
|
|
|
|
|
await BaseAppClient().post(INSERT_PATIENT_COURSE_VIEW_STATS, onSuccess: (res, statusCode) async {
|
|
|
|
|
print(res);
|
|
|
|
|
//response = PatientEducationJourneyListModel.fromJson(res);
|
|
|
|
|
}, onFailure: (String error, int statusCode) {
|
|
|
|
|
// GifLoaderDialogUtils.hideDialog(context);
|
|
|
|
|
Utils.showErrorToast(error);
|
|
|
|
|
throw error;
|
|
|
|
|
}, body: request);
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future navigate(BuildContext context, int journey, {required Function(Object? val) onBack}) async {
|
|
|
|
|
selectedJourney = journey;
|
|
|
|
|
Future navigate(BuildContext context, int journey, String title, {required Function(Object? val) onBack}) async {
|
|
|
|
|
setSelectedJourney = journey;
|
|
|
|
|
setPageTitle = title;
|
|
|
|
|
Navigator.of(context).pushNamed(COURSES_DETAILED_PAGE).then((val) {
|
|
|
|
|
onBack(val);
|
|
|
|
|
});
|
|
|
|
|
@ -118,84 +151,116 @@ class CourseServiceProvider with ChangeNotifier {
|
|
|
|
|
return "$minutes:${seconds.toString().padLeft(2, '0')} mins";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void playVideo(BuildContext context, {required Content content}) async {
|
|
|
|
|
print("OnTap");
|
|
|
|
|
if (_videoState == VideoPlayerState.playing) {
|
|
|
|
|
controller?.pause();
|
|
|
|
|
setVideoState(VideoPlayerState.paused);
|
|
|
|
|
} else {
|
|
|
|
|
setVideoState(VideoPlayerState.loading);
|
|
|
|
|
notifyListeners();
|
|
|
|
|
void play(BuildContext context, {required ejm.Content content}) async {
|
|
|
|
|
switch (content.videoState) {
|
|
|
|
|
case ejm.VideoPlayerState.loading:
|
|
|
|
|
notifyListeners();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final videoUrl = content.video?.flavor?.downloadable ?? "";
|
|
|
|
|
if (videoUrl.isEmpty) {
|
|
|
|
|
Utils.showErrorToast("No video URL provided.");
|
|
|
|
|
setVideoState(VideoPlayerState.paused);
|
|
|
|
|
return;
|
|
|
|
|
case ejm.VideoPlayerState.playing:
|
|
|
|
|
controller?.pause();
|
|
|
|
|
content.videoState = ejm.VideoPlayerState.paused;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ejm.VideoPlayerState.paused:
|
|
|
|
|
if (controller != null && content.id == playedContent!.id) {
|
|
|
|
|
controller!.play();
|
|
|
|
|
playedContent!.videoState = ejm.VideoPlayerState.playing;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
} else {
|
|
|
|
|
playVideo(context, content: content);
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
playVideo(context, content: content);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
controller = VideoPlayerController.networkUrl(
|
|
|
|
|
Uri.parse(videoUrl),
|
|
|
|
|
//formatHint: VideoFormat.hls,
|
|
|
|
|
);
|
|
|
|
|
void playVideo(BuildContext context, {required ejm.Content content}) async {
|
|
|
|
|
if (controller != null && controller!.value.isPlaying) {
|
|
|
|
|
controller!.pause();
|
|
|
|
|
if (playedContent != null) {
|
|
|
|
|
playedContent!.videoState = ejm.VideoPlayerState.paused;
|
|
|
|
|
}
|
|
|
|
|
removeVideoListener(context);
|
|
|
|
|
controller = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await controller!.initialize();
|
|
|
|
|
controller!.play();
|
|
|
|
|
setVideoState(VideoPlayerState.playing);
|
|
|
|
|
notifyListeners();
|
|
|
|
|
content.videoState = ejm.VideoPlayerState.loading;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
final videoUrl = content.video?.flavor?.downloadable ?? "";
|
|
|
|
|
if (videoUrl.isEmpty) {
|
|
|
|
|
Utils.showErrorToast("Something went wrong.");
|
|
|
|
|
content.videoState = ejm.VideoPlayerState.paused;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
controller = await VideoPlayerController.networkUrl(Uri.parse(videoUrl));
|
|
|
|
|
controller!.initialize()..then((value) => notifyListeners());
|
|
|
|
|
print("Controller Value = "+controller!.value.toString());
|
|
|
|
|
controller!.play();
|
|
|
|
|
addVideoListener(context);
|
|
|
|
|
content.videoState = ejm.VideoPlayerState.playing;
|
|
|
|
|
playedContent = content;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
content.controller = controller;
|
|
|
|
|
void addVideoListener(BuildContext context) {
|
|
|
|
|
controller?.removeListener(() => videoListener(context));
|
|
|
|
|
controller?.addListener(() => videoListener(context));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
controller!.addListener(() {
|
|
|
|
|
if (controller!.value.isPlaying && timer == null) {
|
|
|
|
|
_startTimer(context, content: content);
|
|
|
|
|
} else if (!controller!.value.isPlaying && timer != null) {
|
|
|
|
|
stopTimer();
|
|
|
|
|
}
|
|
|
|
|
void removeVideoListener(BuildContext context) {
|
|
|
|
|
controller?.removeListener(() => videoListener(context));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (controller!.value.hasError) {
|
|
|
|
|
Utils.showErrorToast("Failed to load video.");
|
|
|
|
|
controller = null;
|
|
|
|
|
setVideoState(VideoPlayerState.paused);
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
Utils.showErrorToast("Error loading video: $e");
|
|
|
|
|
controller = null;
|
|
|
|
|
setVideoState(VideoPlayerState.paused);
|
|
|
|
|
int? lastProcessedSecond;
|
|
|
|
|
|
|
|
|
|
void videoListener(BuildContext context) async {
|
|
|
|
|
final currentSecond = controller!.value.position.inSeconds;
|
|
|
|
|
if (lastProcessedSecond != currentSecond) {
|
|
|
|
|
lastProcessedSecond = currentSecond;
|
|
|
|
|
Duration currentWatchedTime = controller!.value.position;
|
|
|
|
|
Duration totalVideoTime = controller!.value.duration;
|
|
|
|
|
if (totalVideoTime != null) {
|
|
|
|
|
playedContent!.viewedPercentage = await calculateWatchedPercentage(currentWatchedTime, totalVideoTime);
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
|
|
|
|
print(playedContent!.viewedPercentage);
|
|
|
|
|
if (currentSecond == playedContent!.question!.triggerAt) {
|
|
|
|
|
controller!.pause();
|
|
|
|
|
playedContent!.videoState = ejm.VideoPlayerState.paused;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
|
|
QuestionSheetAction? action = await showQuestionSheet(context, content: playedContent!);
|
|
|
|
|
if (action == QuestionSheetAction.skip) {
|
|
|
|
|
controller!.play();
|
|
|
|
|
playedContent!.videoState = ejm.VideoPlayerState.playing;
|
|
|
|
|
} else if (action == QuestionSheetAction.rewatch) {
|
|
|
|
|
playVideo(context, content: playedContent!);
|
|
|
|
|
}
|
|
|
|
|
notifyListeners();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setVideoState(VideoPlayerState state) {
|
|
|
|
|
_videoState = state;
|
|
|
|
|
notifyListeners();
|
|
|
|
|
double calculateWatchedPercentage(Duration currentWatchedTime, Duration totalVideoTime) {
|
|
|
|
|
if (totalVideoTime.inSeconds == 0) {
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
double percentage = (currentWatchedTime.inSeconds / totalVideoTime.inSeconds);
|
|
|
|
|
return percentage.clamp(0.0, 1.0); // Clamp the value between 0.0 and 1.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _startTimer(BuildContext context, {required Content content}) {
|
|
|
|
|
timer = Timer.periodic(Duration(seconds: 1), (timer) async {
|
|
|
|
|
if (controller != null && _videoState == VideoPlayerState.playing) {
|
|
|
|
|
final position = await controller!.position;
|
|
|
|
|
if (position != null) {
|
|
|
|
|
print("Current position: ${position.inSeconds} seconds");
|
|
|
|
|
notifyListeners();
|
|
|
|
|
if (position.inSeconds == content.question!.triggerAt) {
|
|
|
|
|
controller!.pause();
|
|
|
|
|
setVideoState(VideoPlayerState.paused);
|
|
|
|
|
QuestionSheetAction? action = await showQuestionSheet(context, content: content);
|
|
|
|
|
if (action == QuestionSheetAction.skip) {
|
|
|
|
|
controller!.play();
|
|
|
|
|
setVideoState(VideoPlayerState.playing);
|
|
|
|
|
} else if (action == QuestionSheetAction.rewatch) {
|
|
|
|
|
playVideo(context, content: content);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
int convertToIntegerPercentage(double percentage) {
|
|
|
|
|
return (percentage.clamp(0.0, 1.0) * 99 + 1).toInt();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double convertToDoublePercentage(int value) {
|
|
|
|
|
int clampedValue = value.clamp(1, 100);
|
|
|
|
|
return (clampedValue - 1) / 99.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stopTimer() {
|
|
|
|
|
@ -204,148 +269,163 @@ class CourseServiceProvider with ChangeNotifier {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> uploadStats() async {
|
|
|
|
|
// if(courseTopics != null) {
|
|
|
|
|
// Future.forEach(courseTopics!, (topic) {
|
|
|
|
|
// Future.forEach(topic.contents!, (content){
|
|
|
|
|
// content.controller
|
|
|
|
|
// });
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
if (courseTopics != null) {
|
|
|
|
|
await Future.forEach(courseTopics!, (topic) async {
|
|
|
|
|
await Future.forEach(topic.contents!, (content) {
|
|
|
|
|
print(content.controller?.value);
|
|
|
|
|
playedData!.add(new Data(
|
|
|
|
|
type: "content_watch",
|
|
|
|
|
consultationId: 2660,
|
|
|
|
|
contentClassId: 46,
|
|
|
|
|
contentId: 322,
|
|
|
|
|
topicId: 6,
|
|
|
|
|
percentage: content.controller?.value.position.inSeconds ?? 0,
|
|
|
|
|
flavorId: 1,
|
|
|
|
|
srcType: "educate-external",
|
|
|
|
|
screenType: "journey_screen",
|
|
|
|
|
));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
nabedInsertDataPayload = [];
|
|
|
|
|
}
|
|
|
|
|
print("===============");
|
|
|
|
|
print(playedData.toString());
|
|
|
|
|
print(nabedInsertDataPayload.toString());
|
|
|
|
|
print("===============");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// void playVideo(BuildContext context, {required Content content}) async {
|
|
|
|
|
// print("OnTap");
|
|
|
|
|
// if (_videoState == VideoPlayerState.playing) {
|
|
|
|
|
// controller?.pause();
|
|
|
|
|
// setVideoState(VideoPlayerState.paused);
|
|
|
|
|
// } else {
|
|
|
|
|
// try {
|
|
|
|
|
// controller = VideoPlayerController.networkUrl(
|
|
|
|
|
// Uri.parse(
|
|
|
|
|
// Platform.isIOS ? content.video!.flavor!.downloadable! : content.video!.flavor!.downloadable!,
|
|
|
|
|
// ),
|
|
|
|
|
// formatHint: VideoFormat.hls)
|
|
|
|
|
// ..initialize()
|
|
|
|
|
// ..play();
|
|
|
|
|
// notifyListeners();
|
|
|
|
|
// content.controller = controller;
|
|
|
|
|
// notifyListeners();
|
|
|
|
|
// controller!.addListener(() {
|
|
|
|
|
// if (controller!.value.isPlaying && timer == null) {
|
|
|
|
|
// startTimer(context, content: content);
|
|
|
|
|
// notifyListeners();
|
|
|
|
|
// } else if (!controller!.value.isPlaying && timer != null) {
|
|
|
|
|
// stopTimer();
|
|
|
|
|
// notifyListeners();
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
//
|
|
|
|
|
// controller!.addListener(() {
|
|
|
|
|
// if (controller!.value.hasError) {
|
|
|
|
|
// Utils.showErrorToast("Failed to load video.");
|
|
|
|
|
// controller = null;
|
|
|
|
|
// setVideoState(VideoPlayerState.paused);
|
|
|
|
|
// notifyListeners();
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// notifyListeners();
|
|
|
|
|
// } catch (e) {
|
|
|
|
|
// Utils.showErrorToast("Error loading video: $e");
|
|
|
|
|
// controller = null;
|
|
|
|
|
// setVideoState(VideoPlayerState.paused);
|
|
|
|
|
// notifyListeners();
|
|
|
|
|
// }
|
|
|
|
|
// controller?.play();
|
|
|
|
|
// setVideoState(VideoPlayerState.playing);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// void setVideoState(VideoPlayerState state) {
|
|
|
|
|
// _videoState = state;
|
|
|
|
|
// notifyListeners();
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// void startTimer(BuildContext context, {required Content content}) {
|
|
|
|
|
// timer = Timer.periodic(Duration(seconds: 1), (timer) async {
|
|
|
|
|
// if (controller != null && _videoState == VideoPlayerState.playing) {
|
|
|
|
|
// final position = await controller!.position;
|
|
|
|
|
// if (position != null) {
|
|
|
|
|
// print("Current position: ${position.inSeconds} seconds");
|
|
|
|
|
// if (position.inSeconds == content.question!.triggerAt) {
|
|
|
|
|
// print("position: ${position.inSeconds} - ${content.question!.triggerAt} seconds");
|
|
|
|
|
// controller!.pause();
|
|
|
|
|
// setVideoState(VideoPlayerState.paused);
|
|
|
|
|
// QuestionSheetAction? action = await showQuestionSheet(context, content: content);
|
|
|
|
|
// if (action == QuestionSheetAction.skip) {
|
|
|
|
|
// print("Skip");
|
|
|
|
|
// controller!.play();
|
|
|
|
|
// } else if (action == QuestionSheetAction.rewatch) {
|
|
|
|
|
// print("Re-watch");
|
|
|
|
|
// playVideo(context, content: content);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// notifyListeners();
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// void stopTimer() {
|
|
|
|
|
// timer?.cancel();
|
|
|
|
|
// timer = null;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// void onComplete() {
|
|
|
|
|
// stopTimer();
|
|
|
|
|
// setVideoState(VideoPlayerState.completed);
|
|
|
|
|
// }
|
|
|
|
|
void insertJourneyListData(int index, bool isClicked) {
|
|
|
|
|
if (index >= 0 && index < data!.length) {
|
|
|
|
|
var datum = data![index];
|
|
|
|
|
bool journeyDisplayed = nabedInsertDataPayload.any((dataItem) => dataItem.type == UserAction.journeyDisplayed.toJson() && dataItem.consultationId == datum.id);
|
|
|
|
|
if (!journeyDisplayed) {
|
|
|
|
|
nabedInsertDataPayload.add(
|
|
|
|
|
Data(
|
|
|
|
|
type: UserAction.journeyDisplayed.toJson(),
|
|
|
|
|
consultationId: datum.id,
|
|
|
|
|
srcType: "educate-external",
|
|
|
|
|
screenType: "journey_listing_screen",
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (isClicked) {
|
|
|
|
|
bool journeyClick = nabedInsertDataPayload.any((dataItem) => dataItem.type == UserAction.journeyClick.toJson() && dataItem.consultationId == datum.id);
|
|
|
|
|
if (!journeyClick) {
|
|
|
|
|
nabedInsertDataPayload.add(
|
|
|
|
|
Data(
|
|
|
|
|
type: UserAction.journeyClick.toJson(),
|
|
|
|
|
consultationId: datum.id,
|
|
|
|
|
srcType: "educate-external",
|
|
|
|
|
screenType: "journey_listing_screen",
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
print(jsonEncode(nabedInsertDataPayload));
|
|
|
|
|
} else {
|
|
|
|
|
print("Index $index is out of bounds. Please provide a valid index.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onVisibilityChange(VisibilityInfo? visibilityInfo, ejm.Content content, int topicId, {bool isClicked = false}) {
|
|
|
|
|
int? contentClassID;
|
|
|
|
|
for (var data in contentClasses!) {
|
|
|
|
|
for (var topic in data.topics!) {
|
|
|
|
|
for (var con in topic.contents!) {
|
|
|
|
|
if (con.id == content.id) {
|
|
|
|
|
contentClassID = data.id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isClicked) {
|
|
|
|
|
var visiblePercentage = visibilityInfo!.visibleFraction * 100;
|
|
|
|
|
if (visiblePercentage == 100) {
|
|
|
|
|
bool isAddedBefore = nabedInsertDataPayload.any((dataItem) => dataItem.type == UserAction.contentDisplayed.toJson() && dataItem.contentId == content.id);
|
|
|
|
|
if (!isAddedBefore) {
|
|
|
|
|
nabedInsertDataPayload.add(
|
|
|
|
|
Data(
|
|
|
|
|
type: UserAction.contentDisplayed.toJson(),
|
|
|
|
|
consultationId: consultation!.id,
|
|
|
|
|
srcType: "educate-external",
|
|
|
|
|
screenType: "journey_details_screen",
|
|
|
|
|
flavorId: content.video!.flavor!.flavorId,
|
|
|
|
|
topicId: topicId,
|
|
|
|
|
contentId: content.id,
|
|
|
|
|
contentClassId: contentClassID ?? null,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (isClicked) {
|
|
|
|
|
bool journeyClick = nabedInsertDataPayload.any((item) => item.type == UserAction.contentClick.toJson() && item.contentId == content.id);
|
|
|
|
|
if (!journeyClick) {
|
|
|
|
|
double percentage = content.viewedPercentage ?? 0.0;
|
|
|
|
|
nabedInsertDataPayload.add(
|
|
|
|
|
Data(
|
|
|
|
|
type: UserAction.contentClick.toJson(),
|
|
|
|
|
consultationId: consultation!.id,
|
|
|
|
|
srcType: "educate-external",
|
|
|
|
|
screenType: "journey_details_screen",
|
|
|
|
|
flavorId: content.video!.flavor!.flavorId,
|
|
|
|
|
topicId: topicId,
|
|
|
|
|
contentId: content.id,
|
|
|
|
|
percentage: convertToIntegerPercentage(percentage),
|
|
|
|
|
contentClassId: contentClassID ?? null,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
bool isWatchBefore = nabedInsertDataPayload.any((item) => item.type == UserAction.contentWatch.toJson() && item.contentId == content.id);
|
|
|
|
|
if (!isWatchBefore) {
|
|
|
|
|
nabedInsertDataPayload.add(
|
|
|
|
|
Data(
|
|
|
|
|
type: UserAction.contentWatch.toJson(),
|
|
|
|
|
consultationId: consultation!.id,
|
|
|
|
|
srcType: "educate-external",
|
|
|
|
|
screenType: "journey_details_screen",
|
|
|
|
|
flavorId: content.video!.flavor!.flavorId,
|
|
|
|
|
topicId: topicId,
|
|
|
|
|
contentId: content.id,
|
|
|
|
|
contentClassId: contentClassID ?? null,
|
|
|
|
|
percentage: convertToIntegerPercentage(content.viewedPercentage!),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
for (var dataItem in nabedInsertDataPayload) {
|
|
|
|
|
if (dataItem.contentId == content.id && dataItem.type == UserAction.contentWatch.toJson()) {
|
|
|
|
|
dataItem.percentage = convertToIntegerPercentage(content.viewedPercentage!);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
print("======= Updated Data =============");
|
|
|
|
|
print(jsonEncode(nabedInsertDataPayload));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void getContentClassID() {}
|
|
|
|
|
|
|
|
|
|
Future<void> clearData() async {
|
|
|
|
|
data = courseData = nabedJourneyResponse = selectedJourney = courseTopics = consultation = timer = controller = null;
|
|
|
|
|
print("======== Clear Data ======");
|
|
|
|
|
data = courseData = nabedJourneyResponse = courseTopics = consultation = timer = controller = null;
|
|
|
|
|
setSelectedJourney = 0;
|
|
|
|
|
nabedInsertDataPayload = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> clear() async {
|
|
|
|
|
print("==== Clear ======");
|
|
|
|
|
if (controller != null) controller!.dispose();
|
|
|
|
|
courseData = courseTopics = timer = controller = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum VideoPlayerState { playing, paused, completed, loading }
|
|
|
|
|
|
|
|
|
|
IconData getVideoIcon(VideoPlayerState state) {
|
|
|
|
|
switch (state) {
|
|
|
|
|
case VideoPlayerState.loading:
|
|
|
|
|
return Icons.hourglass_top;
|
|
|
|
|
case VideoPlayerState.playing:
|
|
|
|
|
return Icons.pause;
|
|
|
|
|
case VideoPlayerState.paused:
|
|
|
|
|
return Icons.play_arrow;
|
|
|
|
|
case VideoPlayerState.completed:
|
|
|
|
|
return Icons.replay;
|
|
|
|
|
default:
|
|
|
|
|
return Icons.play_arrow;
|
|
|
|
|
enum UserAction {
|
|
|
|
|
contentDisplayed,
|
|
|
|
|
contentClick,
|
|
|
|
|
contentWatch,
|
|
|
|
|
journeyClick,
|
|
|
|
|
journeyDisplayed,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension UserActionExtension on UserAction {
|
|
|
|
|
String toJson() {
|
|
|
|
|
switch (this) {
|
|
|
|
|
case UserAction.contentDisplayed:
|
|
|
|
|
return "content_displayed";
|
|
|
|
|
case UserAction.contentClick:
|
|
|
|
|
return "content_click";
|
|
|
|
|
case UserAction.contentWatch:
|
|
|
|
|
return "content_watch";
|
|
|
|
|
case UserAction.journeyClick:
|
|
|
|
|
return "journey_click";
|
|
|
|
|
case UserAction.journeyDisplayed:
|
|
|
|
|
return "journey_displayed";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|