diff --git a/lib/app/app.dart b/lib/app/app.dart index 2ca880b..fca7101 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -25,7 +25,6 @@ import 'package:yimaru_app/services/secure_storage_service.dart'; import 'package:yimaru_app/services/dio_service.dart'; import 'package:yimaru_app/services/status_checker_service.dart'; import 'package:yimaru_app/ui/views/welcome/welcome_view.dart'; -import 'package:yimaru_app/ui/views/assessment/assessment_view.dart'; import 'package:yimaru_app/ui/views/learn_lesson/learn_lesson_view.dart'; import 'package:yimaru_app/services/permission_handler_service.dart'; import 'package:yimaru_app/services/image_picker_service.dart'; @@ -52,6 +51,7 @@ import 'package:yimaru_app/ui/views/course_practice_question/course_practice_que import 'package:yimaru_app/services/in_app_update_service.dart'; import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart'; import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart'; +import 'package:yimaru_app/ui/views/assessment/assessment_view.dart'; // @stacked-import @StackedApp( @@ -74,7 +74,6 @@ import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart'; MaterialRoute(page: LoginView), MaterialRoute(page: LearnModuleView), MaterialRoute(page: WelcomeView), - MaterialRoute(page: AssessmentView), MaterialRoute(page: LearnLessonView), MaterialRoute(page: ForgetPasswordView), MaterialRoute(page: LearnLessonDetailView), @@ -91,6 +90,7 @@ import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart'; MaterialRoute(page: CoursePracticeQuestionView), MaterialRoute(page: LearnProgramView), MaterialRoute(page: LearnCourseView), + MaterialRoute(page: AssessmentView), // @stacked-route ], dependencies: [ diff --git a/lib/app/app.router.dart b/lib/app/app.router.dart index 2764fc1..3c56080 100644 --- a/lib/app/app.router.dart +++ b/lib/app/app.router.dart @@ -20,43 +20,43 @@ import 'package:yimaru_app/models/subcategory.dart' as _i45; import 'package:yimaru_app/ui/common/enmus.dart' as _i41; import 'package:yimaru_app/ui/views/account_privacy/account_privacy_view.dart' as _i9; -import 'package:yimaru_app/ui/views/assessment/assessment_view.dart' as _i20; +import 'package:yimaru_app/ui/views/assessment/assessment_view.dart' as _i36; import 'package:yimaru_app/ui/views/call_support/call_support_view.dart' as _i12; -import 'package:yimaru_app/ui/views/course/course_view.dart' as _i33; +import 'package:yimaru_app/ui/views/course/course_view.dart' as _i32; import 'package:yimaru_app/ui/views/course_category/course_category_view.dart' - as _i27; -import 'package:yimaru_app/ui/views/course_lesson/course_lesson_view.dart' - as _i29; -import 'package:yimaru_app/ui/views/course_lesson_detail/course_lesson_detail_view.dart' - as _i30; -import 'package:yimaru_app/ui/views/course_payment/course_payment_view.dart' as _i26; -import 'package:yimaru_app/ui/views/course_practice/course_practice_view.dart' +import 'package:yimaru_app/ui/views/course_lesson/course_lesson_view.dart' + as _i28; +import 'package:yimaru_app/ui/views/course_lesson_detail/course_lesson_detail_view.dart' + as _i29; +import 'package:yimaru_app/ui/views/course_payment/course_payment_view.dart' as _i25; +import 'package:yimaru_app/ui/views/course_practice/course_practice_view.dart' + as _i24; import 'package:yimaru_app/ui/views/course_practice_question/course_practice_question_view.dart' - as _i34; + as _i33; import 'package:yimaru_app/ui/views/course_subcategory/course_subcategory_view.dart' - as _i32; + as _i31; import 'package:yimaru_app/ui/views/downloads/downloads_view.dart' as _i7; -import 'package:yimaru_app/ui/views/duolingo/duolingo_view.dart' as _i31; -import 'package:yimaru_app/ui/views/failure/failure_view.dart' as _i28; +import 'package:yimaru_app/ui/views/duolingo/duolingo_view.dart' as _i30; +import 'package:yimaru_app/ui/views/failure/failure_view.dart' as _i27; import 'package:yimaru_app/ui/views/forget_password/forget_password_view.dart' - as _i22; + as _i21; import 'package:yimaru_app/ui/views/home/home_view.dart' as _i2; import 'package:yimaru_app/ui/views/language/language_view.dart' as _i13; import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart' - as _i36; + as _i35; import 'package:yimaru_app/ui/views/learn_lesson/learn_lesson_view.dart' - as _i21; + as _i20; import 'package:yimaru_app/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart' - as _i23; + as _i22; import 'package:yimaru_app/ui/views/learn_module/learn_module_view.dart' as _i18; import 'package:yimaru_app/ui/views/learn_practice/learn_practice_view.dart' - as _i24; + as _i23; import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart' - as _i35; + as _i34; import 'package:yimaru_app/ui/views/login/login_view.dart' as _i17; import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart' as _i3; import 'package:yimaru_app/ui/views/privacy_policy/privacy_policy_view.dart' @@ -111,8 +111,6 @@ class Routes { static const welcomeView = '/welcome-view'; - static const assessmentView = '/assessment-view'; - static const learnLessonView = '/learn-lesson-view'; static const forgetPasswordView = '/forget-password-view'; @@ -145,6 +143,8 @@ class Routes { static const learnCourseView = '/learn-course-view'; + static const assessmentView = '/assessment-view'; + static const all = { homeView, onboardingView, @@ -164,7 +164,6 @@ class Routes { loginView, learnModuleView, welcomeView, - assessmentView, learnLessonView, forgetPasswordView, learnLessonDetailView, @@ -181,6 +180,7 @@ class Routes { coursePracticeQuestionView, learnProgramView, learnCourseView, + assessmentView, }; } @@ -258,73 +258,73 @@ class StackedRouter extends _i1.RouterBase { Routes.welcomeView, page: _i19.WelcomeView, ), - _i1.RouteDef( - Routes.assessmentView, - page: _i20.AssessmentView, - ), _i1.RouteDef( Routes.learnLessonView, - page: _i21.LearnLessonView, + page: _i20.LearnLessonView, ), _i1.RouteDef( Routes.forgetPasswordView, - page: _i22.ForgetPasswordView, + page: _i21.ForgetPasswordView, ), _i1.RouteDef( Routes.learnLessonDetailView, - page: _i23.LearnLessonDetailView, + page: _i22.LearnLessonDetailView, ), _i1.RouteDef( Routes.learnPracticeView, - page: _i24.LearnPracticeView, + page: _i23.LearnPracticeView, ), _i1.RouteDef( Routes.coursePracticeView, - page: _i25.CoursePracticeView, + page: _i24.CoursePracticeView, ), _i1.RouteDef( Routes.coursePaymentView, - page: _i26.CoursePaymentView, + page: _i25.CoursePaymentView, ), _i1.RouteDef( Routes.courseCategoryView, - page: _i27.CourseCategoryView, + page: _i26.CourseCategoryView, ), _i1.RouteDef( Routes.failureView, - page: _i28.FailureView, + page: _i27.FailureView, ), _i1.RouteDef( Routes.courseLessonView, - page: _i29.CourseLessonView, + page: _i28.CourseLessonView, ), _i1.RouteDef( Routes.courseLessonDetailView, - page: _i30.CourseLessonDetailView, + page: _i29.CourseLessonDetailView, ), _i1.RouteDef( Routes.duolingoView, - page: _i31.DuolingoView, + page: _i30.DuolingoView, ), _i1.RouteDef( Routes.courseSubcategoryView, - page: _i32.CourseSubcategoryView, + page: _i31.CourseSubcategoryView, ), _i1.RouteDef( Routes.courseView, - page: _i33.CourseView, + page: _i32.CourseView, ), _i1.RouteDef( Routes.coursePracticeQuestionView, - page: _i34.CoursePracticeQuestionView, + page: _i33.CoursePracticeQuestionView, ), _i1.RouteDef( Routes.learnProgramView, - page: _i35.LearnProgramView, + page: _i34.LearnProgramView, ), _i1.RouteDef( Routes.learnCourseView, - page: _i36.LearnCourseView, + page: _i35.LearnCourseView, + ), + _i1.RouteDef( + Routes.assessmentView, + page: _i36.AssessmentView, ), ]; @@ -490,143 +490,143 @@ class StackedRouter extends _i1.RouterBase { settings: data, ); }, - _i20.AssessmentView: (data) { - final args = data.getArgs(nullOk: false); - return _i37.MaterialPageRoute( - builder: (context) => - _i20.AssessmentView(key: args.key, data: args.data), - settings: data, - ); - }, - _i21.LearnLessonView: (data) { + _i20.LearnLessonView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => - _i21.LearnLessonView(key: args.key, module: args.module), + _i20.LearnLessonView(key: args.key, module: args.module), settings: data, ); }, - _i22.ForgetPasswordView: (data) { + _i21.ForgetPasswordView: (data) { final args = data.getArgs( orElse: () => const ForgetPasswordViewArguments(), ); return _i37.MaterialPageRoute( - builder: (context) => _i22.ForgetPasswordView(key: args.key), + builder: (context) => _i21.ForgetPasswordView(key: args.key), settings: data, ); }, - _i23.LearnLessonDetailView: (data) { + _i22.LearnLessonDetailView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => - _i23.LearnLessonDetailView(key: args.key, lesson: args.lesson), + _i22.LearnLessonDetailView(key: args.key, lesson: args.lesson), settings: data, ); }, - _i24.LearnPracticeView: (data) { + _i23.LearnPracticeView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( - builder: (context) => _i24.LearnPracticeView( + builder: (context) => _i23.LearnPracticeView( key: args.key, id: args.id, practice: args.practice), settings: data, ); }, - _i25.CoursePracticeView: (data) { + _i24.CoursePracticeView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => - _i25.CoursePracticeView(key: args.key, id: args.id), + _i24.CoursePracticeView(key: args.key, id: args.id), settings: data, ); }, - _i26.CoursePaymentView: (data) { + _i25.CoursePaymentView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => - _i26.CoursePaymentView(key: args.key, course: args.course), + _i25.CoursePaymentView(key: args.key, course: args.course), settings: data, ); }, - _i27.CourseCategoryView: (data) { + _i26.CourseCategoryView: (data) { final args = data.getArgs( orElse: () => const CourseCategoryViewArguments(), ); return _i37.MaterialPageRoute( - builder: (context) => _i27.CourseCategoryView(key: args.key), + builder: (context) => _i26.CourseCategoryView(key: args.key), settings: data, ); }, - _i28.FailureView: (data) { + _i27.FailureView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( - builder: (context) => - _i28.FailureView(key: args.key, label: args.label), + builder: (context) => _i27.FailureView( + key: args.key, onTap: args.onTap, label: args.label), settings: data, ); }, - _i29.CourseLessonView: (data) { + _i28.CourseLessonView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => - _i29.CourseLessonView(key: args.key, course: args.course), + _i28.CourseLessonView(key: args.key, course: args.course), settings: data, ); }, - _i30.CourseLessonDetailView: (data) { + _i29.CourseLessonDetailView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => - _i30.CourseLessonDetailView(key: args.key, lesson: args.lesson), + _i29.CourseLessonDetailView(key: args.key, lesson: args.lesson), settings: data, ); }, - _i31.DuolingoView: (data) { + _i30.DuolingoView: (data) { final args = data.getArgs( orElse: () => const DuolingoViewArguments(), ); return _i37.MaterialPageRoute( - builder: (context) => _i31.DuolingoView(key: args.key), + builder: (context) => _i30.DuolingoView(key: args.key), settings: data, ); }, - _i32.CourseSubcategoryView: (data) { + _i31.CourseSubcategoryView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => - _i32.CourseSubcategoryView(key: args.key, category: args.category), + _i31.CourseSubcategoryView(key: args.key, category: args.category), settings: data, ); }, - _i33.CourseView: (data) { + _i32.CourseView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => - _i33.CourseView(key: args.key, subcategory: args.subcategory), + _i32.CourseView(key: args.key, subcategory: args.subcategory), settings: data, ); }, - _i34.CoursePracticeQuestionView: (data) { + _i33.CoursePracticeQuestionView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( builder: (context) => - _i34.CoursePracticeQuestionView(key: args.key, id: args.id), + _i33.CoursePracticeQuestionView(key: args.key, id: args.id), settings: data, ); }, - _i35.LearnProgramView: (data) { + _i34.LearnProgramView: (data) { final args = data.getArgs( orElse: () => const LearnProgramViewArguments(), ); return _i37.MaterialPageRoute( - builder: (context) => _i35.LearnProgramView(key: args.key), + builder: (context) => _i34.LearnProgramView(key: args.key), settings: data, ); }, - _i36.LearnCourseView: (data) { + _i35.LearnCourseView: (data) { final args = data.getArgs(nullOk: false); return _i37.MaterialPageRoute( - builder: (context) => _i36.LearnCourseView(key: args.key, id: args.id), + builder: (context) => _i35.LearnCourseView(key: args.key, id: args.id), + settings: data, + ); + }, + _i36.AssessmentView: (data) { + final args = data.getArgs(nullOk: false); + return _i37.MaterialPageRoute( + builder: (context) => + _i36.AssessmentView(key: args.key, data: args.data), settings: data, ); }, @@ -1045,33 +1045,6 @@ class WelcomeViewArguments { } } -class AssessmentViewArguments { - const AssessmentViewArguments({ - this.key, - required this.data, - }); - - final _i37.Key? key; - - final Map data; - - @override - String toString() { - return '{"key": "$key", "data": "$data"}'; - } - - @override - bool operator ==(covariant AssessmentViewArguments other) { - if (identical(this, other)) return true; - return other.key == key && other.data == data; - } - - @override - int get hashCode { - return key.hashCode ^ data.hashCode; - } -} - class LearnLessonViewArguments { const LearnLessonViewArguments({ this.key, @@ -1257,27 +1230,30 @@ class CourseCategoryViewArguments { class FailureViewArguments { const FailureViewArguments({ this.key, + required this.onTap, required this.label, }); final _i37.Key? key; + final void Function() onTap; + final String label; @override String toString() { - return '{"key": "$key", "label": "$label"}'; + return '{"key": "$key", "onTap": "$onTap", "label": "$label"}'; } @override bool operator ==(covariant FailureViewArguments other) { if (identical(this, other)) return true; - return other.key == key && other.label == label; + return other.key == key && other.onTap == onTap && other.label == label; } @override int get hashCode { - return key.hashCode ^ label.hashCode; + return key.hashCode ^ onTap.hashCode ^ label.hashCode; } } @@ -1487,6 +1463,33 @@ class LearnCourseViewArguments { } } +class AssessmentViewArguments { + const AssessmentViewArguments({ + this.key, + required this.data, + }); + + final _i37.Key? key; + + final Map data; + + @override + String toString() { + return '{"key": "$key", "data": "$data"}'; + } + + @override + bool operator ==(covariant AssessmentViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && other.data == data; + } + + @override + int get hashCode { + return key.hashCode ^ data.hashCode; + } +} + extension NavigatorStateExtension on _i46.NavigationService { Future navigateToHomeView({ _i37.Key? key, @@ -1778,23 +1781,6 @@ extension NavigatorStateExtension on _i46.NavigationService { transition: transition); } - Future navigateToAssessmentView({ - _i37.Key? key, - required Map data, - int? routerId, - bool preventDuplicates = true, - Map? parameters, - Widget Function(BuildContext, Animation, Animation, Widget)? - transition, - }) async { - return navigateTo(Routes.assessmentView, - arguments: AssessmentViewArguments(key: key, data: data), - id: routerId, - preventDuplicates: preventDuplicates, - parameters: parameters, - transition: transition); - } - Future navigateToLearnLessonView({ _i37.Key? key, required _i39.LearnModule module, @@ -1916,6 +1902,7 @@ extension NavigatorStateExtension on _i46.NavigationService { Future navigateToFailureView({ _i37.Key? key, + required void Function() onTap, required String label, int? routerId, bool preventDuplicates = true, @@ -1924,7 +1911,7 @@ extension NavigatorStateExtension on _i46.NavigationService { transition, }) async { return navigateTo(Routes.failureView, - arguments: FailureViewArguments(key: key, label: label), + arguments: FailureViewArguments(key: key, onTap: onTap, label: label), id: routerId, preventDuplicates: preventDuplicates, parameters: parameters, @@ -2065,6 +2052,23 @@ extension NavigatorStateExtension on _i46.NavigationService { transition: transition); } + Future navigateToAssessmentView({ + _i37.Key? key, + required Map data, + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + }) async { + return navigateTo(Routes.assessmentView, + arguments: AssessmentViewArguments(key: key, data: data), + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + Future replaceWithHomeView({ _i37.Key? key, int? routerId, @@ -2355,23 +2359,6 @@ extension NavigatorStateExtension on _i46.NavigationService { transition: transition); } - Future replaceWithAssessmentView({ - _i37.Key? key, - required Map data, - int? routerId, - bool preventDuplicates = true, - Map? parameters, - Widget Function(BuildContext, Animation, Animation, Widget)? - transition, - }) async { - return replaceWith(Routes.assessmentView, - arguments: AssessmentViewArguments(key: key, data: data), - id: routerId, - preventDuplicates: preventDuplicates, - parameters: parameters, - transition: transition); - } - Future replaceWithLearnLessonView({ _i37.Key? key, required _i39.LearnModule module, @@ -2493,6 +2480,7 @@ extension NavigatorStateExtension on _i46.NavigationService { Future replaceWithFailureView({ _i37.Key? key, + required void Function() onTap, required String label, int? routerId, bool preventDuplicates = true, @@ -2501,7 +2489,7 @@ extension NavigatorStateExtension on _i46.NavigationService { transition, }) async { return replaceWith(Routes.failureView, - arguments: FailureViewArguments(key: key, label: label), + arguments: FailureViewArguments(key: key, onTap: onTap, label: label), id: routerId, preventDuplicates: preventDuplicates, parameters: parameters, @@ -2641,4 +2629,21 @@ extension NavigatorStateExtension on _i46.NavigationService { parameters: parameters, transition: transition); } + + Future replaceWithAssessmentView({ + _i37.Key? key, + required Map data, + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + }) async { + return replaceWith(Routes.assessmentView, + arguments: AssessmentViewArguments(key: key, data: data), + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } } diff --git a/lib/models/assessment.dart b/lib/models/assessment.dart new file mode 100644 index 0000000..de2120f --- /dev/null +++ b/lib/models/assessment.dart @@ -0,0 +1,37 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'assessment.g.dart'; + +@JsonSerializable() +class Assessment { + final int? id; + + final String? title; + + final String? status; + + final String? description; + + @JsonKey(name: 'set_type') + final String? setType; + + @JsonKey(name: 'passing_score') + final int? passingScore; + + @JsonKey(name: 'shuffle_questions') + final bool? shuffleQuestions; + + const Assessment( + {this.id, + this.title, + this.status, + this.setType, + this.description, + this.passingScore, + this.shuffleQuestions}); + + factory Assessment.fromJson(Map json) => + _$AssessmentFromJson(json); + + Map toJson() => _$AssessmentToJson(this); +} diff --git a/lib/models/assessment.g.dart b/lib/models/assessment.g.dart new file mode 100644 index 0000000..fe279b6 --- /dev/null +++ b/lib/models/assessment.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'assessment.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Assessment _$AssessmentFromJson(Map json) => Assessment( + id: (json['id'] as num?)?.toInt(), + title: json['title'] as String?, + status: json['status'] as String?, + setType: json['set_type'] as String?, + description: json['description'] as String?, + passingScore: (json['passing_score'] as num?)?.toInt(), + shuffleQuestions: json['shuffle_questions'] as bool?, + ); + +Map _$AssessmentToJson(Assessment instance) => + { + 'id': instance.id, + 'title': instance.title, + 'status': instance.status, + 'description': instance.description, + 'set_type': instance.setType, + 'passing_score': instance.passingScore, + 'shuffle_questions': instance.shuffleQuestions, + }; diff --git a/lib/models/question.dart b/lib/models/assessment_question.dart similarity index 60% rename from lib/models/question.dart rename to lib/models/assessment_question.dart index 3803fe2..43d957e 100644 --- a/lib/models/question.dart +++ b/lib/models/assessment_question.dart @@ -1,9 +1,9 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:yimaru_app/models/option.dart'; -part 'question.g.dart'; +part 'assessment_question.g.dart'; @JsonSerializable() -class Question { +class AssessmentQuestion { final int? id; final int? points; @@ -18,21 +18,17 @@ class Question { @JsonKey(name: 'question_text') final String? questionText; - @JsonKey(name: 'difficulty_level') - final String? difficultyLevel; - - const Question({ + const AssessmentQuestion({ this.id, this.points, this.status, this.options, this.questionText, this.questionType, - this.difficultyLevel, }); - factory Question.fromJson(Map json) => - _$QuestionFromJson(json); + factory AssessmentQuestion.fromJson(Map json) => + _$AssessmentQuestionFromJson(json); - Map toJson() => _$QuestionToJson(this); + Map toJson() => _$AssessmentQuestionToJson(this); } diff --git a/lib/models/question.g.dart b/lib/models/assessment_question.g.dart similarity index 74% rename from lib/models/question.g.dart rename to lib/models/assessment_question.g.dart index 6e1a743..e912fc1 100644 --- a/lib/models/question.g.dart +++ b/lib/models/assessment_question.g.dart @@ -1,12 +1,13 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'question.dart'; +part of 'assessment_question.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** -Question _$QuestionFromJson(Map json) => Question( +AssessmentQuestion _$AssessmentQuestionFromJson(Map json) => + AssessmentQuestion( id: (json['id'] as num?)?.toInt(), points: (json['points'] as num?)?.toInt(), status: json['status'] as String?, @@ -15,15 +16,14 @@ Question _$QuestionFromJson(Map json) => Question( .toList(), questionText: json['question_text'] as String?, questionType: json['question_type'] as String?, - difficultyLevel: json['difficulty_level'] as String?, ); -Map _$QuestionToJson(Question instance) => { +Map _$AssessmentQuestionToJson(AssessmentQuestion instance) => + { 'id': instance.id, 'points': instance.points, 'status': instance.status, 'options': instance.options, 'question_type': instance.questionType, 'question_text': instance.questionText, - 'difficulty_level': instance.difficultyLevel, }; diff --git a/lib/models/option.dart b/lib/models/option.dart index 4786b34..28e10f9 100644 --- a/lib/models/option.dart +++ b/lib/models/option.dart @@ -11,7 +11,10 @@ class Option { @JsonKey(name: 'option_text') final String? optionText; - const Option({this.id, this.optionText, this.isCorrect}); + @JsonKey(name: 'option_order') + final int? optionOrder; + + const Option({this.id, this.optionText, this.isCorrect, this.optionOrder}); factory Option.fromJson(Map json) => _$OptionFromJson(json); diff --git a/lib/models/option.g.dart b/lib/models/option.g.dart index 556d786..844b4c8 100644 --- a/lib/models/option.g.dart +++ b/lib/models/option.g.dart @@ -10,10 +10,12 @@ Option _$OptionFromJson(Map json) => Option( id: (json['id'] as num?)?.toInt(), optionText: json['option_text'] as String?, isCorrect: json['is_correct'] as bool?, + optionOrder: (json['option_order'] as num?)?.toInt(), ); Map _$OptionToJson(Option instance) => { 'id': instance.id, 'is_correct': instance.isCorrect, 'option_text': instance.optionText, + 'option_order': instance.optionOrder, }; diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 3952aca..a245536 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -3,7 +3,7 @@ import 'package:yimaru_app/models/learn_lesson.dart'; import 'package:yimaru_app/models/learn_practice.dart'; import 'package:yimaru_app/models/learn_program.dart'; import 'package:yimaru_app/models/level.dart'; -import 'package:yimaru_app/models/question.dart'; +import 'package:yimaru_app/models/assessment_question.dart'; import 'package:yimaru_app/models/subcategory.dart'; import 'package:yimaru_app/models/category.dart'; import 'package:yimaru_app/models/course_lesson.dart'; @@ -20,6 +20,7 @@ import '../models/learn_module.dart'; import '../models/learn_question.dart'; import '../models/lesson.dart'; import '../models/module.dart'; +import '../models/assessment.dart'; import '../models/submodule.dart'; import '../ui/common/enmus.dart'; @@ -354,23 +355,47 @@ class ApiService { } } - // Get assessments - Future> getAssessments() async { + // Get assessment question sets + Future> getAssessments() async { try { - List assessments = []; + List assessments = []; - final Response response = - await _service.dio.get('$kBaseUrl/$kAssessmentsUrl'); + final Response response = await _service.dio.get( + '$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl?set_type=INITIAL_ASSESSMENT&limit=10&offset=0'); + + if (response.statusCode == 200) { + var data = response.data; + var decodedData = data['data']['question_sets'] as List; + assessments = decodedData.map( + (e) { + return Assessment.fromJson(e); + }, + ).toList(); + return assessments; + } + return []; + } catch (e) { + return []; + } + } + + // Get assessment questions + Future> getAssessmentQuestions(int id) async { + try { + List questions = []; + + final Response response = await _service.dio.get( + '$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl'); if (response.statusCode == 200) { var data = response.data; var decodedData = data['data'] as List; - assessments = decodedData.map( + questions = decodedData.map( (e) { - return Question.fromJson(e); + return AssessmentQuestion.fromJson(e); }, ).toList(); - return assessments; + return questions; } return []; } catch (e) { @@ -741,9 +766,9 @@ class ApiService { } // Get course practic questions - Future> getCoursePracticeQuestions(int id) async { + Future> getCoursePracticeQuestions(int id) async { try { - List coursePracticeQuestions = []; + List coursePracticeQuestions = []; final Response response = await _service.dio .get('$kBaseUrl/$kPracticeBaseUrl/$id/$kCoursePracticeQuestions'); @@ -753,7 +778,7 @@ class ApiService { var decodedData = data['data'] as List; coursePracticeQuestions = decodedData.map( (e) { - return Question.fromJson(e); + return AssessmentQuestion.fromJson(e); }, ).toList(); return coursePracticeQuestions; @@ -765,13 +790,14 @@ class ApiService { } // Get course practice question - Future getCoursePracticeQuestion(int id) async { + Future getCoursePracticeQuestion(int id) async { try { final Response response = await _service.dio.get('$kBaseUrl/$kCoursePracticeQuestion/$id'); if (response.statusCode == 200) { - Question question = Question.fromJson(response.data['data']); + AssessmentQuestion question = + AssessmentQuestion.fromJson(response.data['data']); return question; } @@ -950,9 +976,9 @@ class ApiService { } // Questions - Future> getQuestions(int id) async { + Future> getQuestions(int id) async { try { - List questions = []; + List questions = []; final Response response = await _service.dio.get( '$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl'); @@ -962,7 +988,7 @@ class ApiService { var decodedData = data['data'] as List; questions = decodedData.map( (e) { - return Question.fromJson(e); + return AssessmentQuestion.fromJson(e); }, ).toList(); return questions; diff --git a/lib/services/authentication_service.dart b/lib/services/authentication_service.dart index 1896566..12928ca 100644 --- a/lib/services/authentication_service.dart +++ b/lib/services/authentication_service.dart @@ -173,5 +173,6 @@ class AuthenticationService with ListenableServiceMixin { _user = null; await _secureService.clear(); await setFirstTimeInstall(firstTimeInstall); + notifyListeners(); } } diff --git a/lib/services/dio_service.dart b/lib/services/dio_service.dart index 14caa98..2f57469 100644 --- a/lib/services/dio_service.dart +++ b/lib/services/dio_service.dart @@ -27,8 +27,8 @@ class DioService { DioService() { _dio.options ..baseUrl = kBaseUrl - ..connectTimeout = const Duration(seconds: 30) - ..receiveTimeout = const Duration(seconds: 30); + ..connectTimeout = const Duration(seconds: 5) + ..receiveTimeout = const Duration(seconds: 15); _dio.interceptors.add( InterceptorsWrapper( diff --git a/lib/ui/common/enmus.dart b/lib/ui/common/enmus.dart index a6c93ef..1276f5a 100644 --- a/lib/ui/common/enmus.dart +++ b/lib/ui/common/enmus.dart @@ -16,8 +16,8 @@ enum LearnPractices { course, module, lesson } // Voice recording state enum VoiceRecordingState { pending, recording } -// Levels -enum ProficiencyLevels { a1, a2, b1, b2, none } +// // Levels +// enum ProficiencyLevels { a1, a2, b1, b2, none } // Progress status enum ProgressStatuses { pending, started, completed } @@ -29,10 +29,11 @@ enum DuolingoAssessments { speaking, reading, writing, listening } enum StateObjects { none, courses, - startupView, register, verifyOtp, resendOtp, + assessments, + startupView, learnLessons, learnModules, learnCourses, @@ -56,6 +57,7 @@ enum StateObjects { learnPracticeSample, learnPracticeAnswer, loginWithPhoneNumber, + assessmentQuestions, learnPracticeQuestion, coursePracticeQuestion, coursePracticeQuestions, diff --git a/lib/ui/common/ui_helpers.dart b/lib/ui/common/ui_helpers.dart index 4a01dfe..f6ffad4 100644 --- a/lib/ui/common/ui_helpers.dart +++ b/lib/ui/common/ui_helpers.dart @@ -312,6 +312,10 @@ TextStyle style14MG400 = const TextStyle( TextStyle style14DG500 = const TextStyle(color: kcDarkGrey, fontWeight: FontWeight.w500); +TextStyle style18MG500 = +const TextStyle(fontSize: 18,color: kcMediumGrey, fontWeight: FontWeight.w500); + + TextStyle style14DG400 = const TextStyle( color: kcDarkGrey, ); diff --git a/lib/ui/views/account_privacy/account_privacy_view.dart b/lib/ui/views/account_privacy/account_privacy_view.dart index d68c7f8..8970ea5 100644 --- a/lib/ui/views/account_privacy/account_privacy_view.dart +++ b/lib/ui/views/account_privacy/account_privacy_view.dart @@ -56,7 +56,7 @@ class AccountPrivacyView extends StackedView { Widget _buildAppbar(AccountPrivacyViewModel viewModel) => SmallAppBar( showBackButton: true, - onTap: viewModel.pop, + onPop: viewModel.pop, title: 'Account Privacy', ); diff --git a/lib/ui/views/assessment/assessment_view.dart b/lib/ui/views/assessment/assessment_view.dart index 5cdaf28..d5210b8 100644 --- a/lib/ui/views/assessment/assessment_view.dart +++ b/lib/ui/views/assessment/assessment_view.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; +import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/views/assessment/screens/assessment_questions_screen.dart'; import 'package:yimaru_app/ui/views/assessment/screens/assessment_intro_screen.dart'; import 'package:yimaru_app/ui/views/assessment/screens/assessment_result_screen.dart'; import 'package:yimaru_app/ui/views/assessment/screens/start_lesson_screen.dart'; +import '../../widgets/assessment_loading_screen.dart'; import 'assessment_viewmodel.dart'; class AssessmentView extends StackedView { @@ -39,16 +41,29 @@ class AssessmentView extends StackedView { Widget _buildAssessmentScreens(AssessmentViewModel viewModel) => IndexedStack( index: viewModel.currentPage, - children: _buildScreens(), + children: _buildScreens(viewModel), ); - List _buildScreens() => [ - _buildAssessmentIntro(), + List _buildScreens(AssessmentViewModel viewModel) => [ + _buildAssessmentIntroWrapper(viewModel), _buildAssessment(), _buildAssessmentResult(), _buildStartLesson(), ]; + Widget _buildAssessmentIntroWrapper(AssessmentViewModel viewModel) => + viewModel.busy(StateObjects.assessments) || viewModel.assessments.isEmpty + ? _buildPageLoadingIndicator(viewModel) + : _buildAssessmentIntro(); + + Widget _buildPageLoadingIndicator(AssessmentViewModel viewModel) => + AssessmentLoadingScreen( + isEmpty: viewModel.assessments.isEmpty, + onTap: () async => await viewModel.getAssessments(), + isLoading: viewModel.busy(StateObjects.assessments), + onPop: viewModel.assessments.isEmpty ? viewModel.pop : null, + ); + Widget _buildAssessmentIntro() => const AssessmentIntroScreen(); Widget _buildAssessment() => const AssessmentQuestionsScreen(); diff --git a/lib/ui/views/assessment/assessment_viewmodel.dart b/lib/ui/views/assessment/assessment_viewmodel.dart index 2610005..785581f 100644 --- a/lib/ui/views/assessment/assessment_viewmodel.dart +++ b/lib/ui/views/assessment/assessment_viewmodel.dart @@ -1,13 +1,14 @@ import 'package:flutter/cupertino.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; +import 'package:yimaru_app/models/assessment_question.dart'; import 'package:yimaru_app/models/option.dart'; import 'package:yimaru_app/services/status_checker_service.dart'; import 'package:yimaru_app/ui/common/enmus.dart'; import '../../../app/app.locator.dart'; import '../../../app/app.router.dart'; -import '../../../models/question.dart'; +import '../../../models/assessment.dart'; import '../../../services/api_service.dart'; import '../../common/app_colors.dart'; import '../../common/ui_helpers.dart'; @@ -33,82 +34,78 @@ class AssessmentViewModel extends BaseViewModel { PageController get pageController => _pageController; // Assessment - int _currentQuestion = 0; - int get currentQuestion => _currentQuestion; + List _assessments = []; - List _assessments = []; + List get assessments => _assessments; - List get assessments => _assessments; + Assessment? _currentAssessment; - ProficiencyLevels _proficiencyLevel = ProficiencyLevels.none; + Assessment? get currentAssessment => _currentAssessment; - ProficiencyLevels get proficiencyLevel => _proficiencyLevel; + int _currentQuestionIndex = 0; + + int get currentQuestionIndex => _currentQuestionIndex; + + int _currentAssessmentIndex = 0; + + int get currentAssessmentIndex => _currentAssessmentIndex; + + String? _proficiencyLevel; + + String? get proficiencyLevel => _proficiencyLevel; final Map _selectedAnswers = {}; Map get selectedAnswers => _selectedAnswers; + List _assessmentQuestions = []; + + List get assessmentQuestions => _assessmentQuestions; + // User data final Map _userData = {}; Map get userData => _userData; // Assessment + Future setFirstAssessment()async{ + _proficiencyLevel = null; + _selectedAnswers.clear(); + _currentQuestionIndex = 0; + _pageController.jumpToPage(_currentQuestionIndex); + _currentAssessment = assessments[currentAssessmentIndex]; + await getAssessmentQuestions( + _currentAssessment?.id ?? 0); + + next(); + } Map evaluateAssessment() { - if (_currentQuestion == 5) { - // A1 - final correctCount = countCorrectAnswersUntil(5); - - if (correctCount > 3) { - return {'continue': true, 'level': ProficiencyLevels.a1}; - } else { - return {'continue': false, 'level': ProficiencyLevels.a1}; - } - } else if (_currentQuestion == 10) { - // A2 - - final correctCount = countCorrectAnswersUntil(10); - - if (correctCount > 3) { - return {'continue': true, 'level': ProficiencyLevels.a2}; - } else { - return {'continue': false, 'level': ProficiencyLevels.a2}; - } - } else if (_currentQuestion == 16) { - // B1 - final correctCount = countCorrectAnswersUntil(16); - - if (correctCount > 4) { - return {'continue': true, 'level': ProficiencyLevels.b1}; - } else { - return {'continue': false, 'level': ProficiencyLevels.b1}; - } - } else if (_currentQuestion == 22) { - final correctCount = countCorrectAnswersUntil(16); - - if (correctCount > 4) { - return {'continue': false, 'level': ProficiencyLevels.b2}; - } else { - return {'continue': false, 'level': ProficiencyLevels.b2}; - } + bool levelPassed = canPassLevel(); + if (levelPassed) { + return {'passed': true, 'level': _currentAssessment?.description}; } else { - return {'continue': true, 'level': ProficiencyLevels.none}; + return {'passed': false, 'level': _currentAssessment?.description}; } } - int countCorrectAnswersUntil(int untilQuestion) { + bool canPassLevel() { int count = 0; - for (int i = 1; i <= untilQuestion; i++) { + for (int i = 1; i <= _assessmentQuestions.length; i++) { final answer = _selectedAnswers[i.toString()]; if (answer is Map && answer['correct'] == true) { count++; } } + print('COUNT: $count'); + print('ASSESSMENT: ${_currentAssessment?.passingScore}'); + if (count >= (_currentAssessment?.passingScore ?? 0)) { + return true; + } - return count; + return false; } bool isSelectedAnswer({required int question, required String answer}) { @@ -125,7 +122,7 @@ class AssessmentViewModel extends BaseViewModel { question.toString(): { 'correct': correct, 'option': option?.optionText, - 'answer': _assessments[question - 1] + 'answer': _assessmentQuestions[question - 1] .options ?.firstWhere((e) => e.isCorrect ?? false) .optionText @@ -152,32 +149,41 @@ class AssessmentViewModel extends BaseViewModel { } // Question navigation - void nextQuestion() { - _currentQuestion++; + Future nextQuestion() async { + _currentQuestionIndex++; Map response = evaluateAssessment(); - - if (_currentQuestion == _assessments.length) { - _proficiencyLevel = response['level']; - next(); - } else { - if (response['level'] == ProficiencyLevels.none) { - _pageController.jumpToPage(_currentQuestion); + print('LEVEL: $response'); + print('LENGTH: ${_assessmentQuestions.length}'); + print('INDEX: $_currentQuestionIndex'); + if (_currentQuestionIndex == _assessmentQuestions.length) { + _currentAssessmentIndex = _currentAssessmentIndex + 1; + if (_currentAssessmentIndex == _assessments.length) { + _proficiencyLevel = response['level']; + next(); } else { - if (response['continue']) { - _pageController.jumpToPage(_currentQuestion); + if (response['passed']) { + _selectedAnswers.clear(); + _currentQuestionIndex = 0; + _proficiencyLevel = response['level']; + _currentAssessment = assessments[currentAssessmentIndex]; + await getAssessmentQuestions( + _currentAssessment?.id ?? 0); + _pageController.jumpToPage(_currentQuestionIndex); } else { _proficiencyLevel = response['level']; next(); } } + } else { + _pageController.jumpToPage(_currentQuestionIndex); } rebuildUi(); } void previousQuestion() { - if (_currentQuestion != 0) { - _currentQuestion--; + if (_currentQuestionIndex != 0) { + _currentQuestionIndex--; _pageController.previousPage( duration: const Duration(microseconds: 100), curve: Curves.linear); rebuildUi(); @@ -193,7 +199,7 @@ class AssessmentViewModel extends BaseViewModel { _currentPage = 0; rebuildUi(); } else if (_currentPage == 3) { - if (_proficiencyLevel != ProficiencyLevels.none) { + if (_proficiencyLevel != null) { _currentPage--; } else { _currentPage = 0; @@ -240,12 +246,12 @@ class AssessmentViewModel extends BaseViewModel { // Navigation void pop() => _navigationService.back(); - Future replaceWithStartUp() async => - await _navigationService.clearStackAndShow(Routes.startupView); - Future navigateToLanguage() async => await _navigationService.navigateToLanguageView(); + Future replaceWittStartUp() async => + await _navigationService.clearStackAndShow(Routes.startupView); + // Remote api call // Complete profile @@ -259,7 +265,7 @@ class AssessmentViewModel extends BaseViewModel { await _apiService.completeProfile(_userData); if (response['status'] == ResponseStatus.success) { clearUserData(); - await replaceWithStartUp(); + await replaceWittStartUp(); showSuccessToast(response['message']); } else { showErrorToast(response['message']); @@ -268,11 +274,23 @@ class AssessmentViewModel extends BaseViewModel { } // Assessments + Future getAssessments() async => + await runBusyFuture(_getAssessments(), + busyObject: StateObjects.assessments); + Future _getAssessments() async { if (await _statusChecker.checkConnection()) { _assessments = await _apiService.getAssessments(); } } - Future getAssessments() async => await runBusyFuture(_getAssessments()); + Future getAssessmentQuestions(int id) async => + await runBusyFuture(_getAssessmentQuestions(id), + busyObject: StateObjects.assessmentQuestions); + + Future _getAssessmentQuestions(int id) async { + if (await _statusChecker.checkConnection()) { + _assessmentQuestions = await _apiService.getAssessmentQuestions(id); + } + } } diff --git a/lib/ui/views/assessment/screens/assessment_intro_screen.dart b/lib/ui/views/assessment/screens/assessment_intro_screen.dart index 0ec35e7..2ee36b2 100644 --- a/lib/ui/views/assessment/screens/assessment_intro_screen.dart +++ b/lib/ui/views/assessment/screens/assessment_intro_screen.dart @@ -10,6 +10,9 @@ import '../assessment_viewmodel.dart'; class AssessmentIntroScreen extends ViewModelWidget { const AssessmentIntroScreen({super.key}); + Future _next(AssessmentViewModel viewModel) async => + viewModel.setFirstAssessment(); + @override Widget build(BuildContext context, AssessmentViewModel viewModel) => _buildScaffoldWrapper(viewModel); @@ -95,8 +98,8 @@ class AssessmentIntroScreen extends ViewModelWidget { text: 'Continue', borderRadius: 12, foregroundColor: kcWhite, - onTap: () => viewModel.next(), backgroundColor: kcPrimaryColor, + onTap: () async => await _next(viewModel), ); Widget _buildSkipButtonWrapper(AssessmentViewModel viewModel) => Padding( diff --git a/lib/ui/views/assessment/screens/assessment_questions_screen.dart b/lib/ui/views/assessment/screens/assessment_questions_screen.dart index 0f0ebef..79dce82 100644 --- a/lib/ui/views/assessment/screens/assessment_questions_screen.dart +++ b/lib/ui/views/assessment/screens/assessment_questions_screen.dart @@ -6,28 +6,15 @@ import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart'; import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart'; import 'package:yimaru_app/ui/widgets/large_app_bar.dart'; +import '../../../widgets/assessment_loading_screen.dart'; import '../assessment_viewmodel.dart'; -import 'assessment_loading_screen.dart'; class AssessmentQuestionsScreen extends ViewModelWidget { const AssessmentQuestionsScreen({super.key}); @override Widget build(BuildContext context, AssessmentViewModel viewModel) => - _buildAssessmentScreens(viewModel); - - Widget _buildAssessmentScreens(AssessmentViewModel viewModel) => - viewModel.isBusy || viewModel.assessments.isEmpty - ? _buildPageLoadingIndicator(viewModel) - : _buildAssessmentScreensWrapper(viewModel); - - Widget _buildPageLoadingIndicator(AssessmentViewModel viewModel) => - AssessmentLoadingScreen( - isLoading: viewModel.isBusy, - isEmpty: viewModel.assessments.isEmpty, - onTap: () async => await viewModel.getAssessments(), - onPop: viewModel.assessments.isEmpty ? viewModel.pop : null, - ); + _buildAssessmentScreensWrapper(viewModel); Widget _buildAssessmentScreensWrapper(AssessmentViewModel viewModel) => PopScope( @@ -52,7 +39,7 @@ class AssessmentQuestionsScreen extends ViewModelWidget { onClose: viewModel.abort, showLanguageSelection: false, onPop: viewModel.previousQuestion, - showBackButton: viewModel.currentQuestion == 0 ? false : true, + showBackButton: viewModel.currentQuestionIndex == 0 ? false : true, ); Widget _buildExpandedBody(AssessmentViewModel viewModel) => @@ -65,7 +52,7 @@ class AssessmentQuestionsScreen extends ViewModelWidget { Widget _buildAssessment(AssessmentViewModel viewModel) => PageView.builder( controller: viewModel.pageController, - itemCount: viewModel.assessments.length, + itemCount: viewModel.assessmentQuestions.length, physics: const NeverScrollableScrollPhysics(), itemBuilder: (cotext, index) => _buildBodyScroller(index: index, viewModel: viewModel), @@ -98,7 +85,7 @@ class AssessmentQuestionsScreen extends ViewModelWidget { Widget _buildTitle( {required int index, required AssessmentViewModel viewModel}) => Text( - 'Q${index + 1}. ${viewModel.assessments[index].questionText} ', + 'Q${index + 1}. ${viewModel.assessmentQuestions[index].questionText} ', style: style16DG600, ); @@ -107,15 +94,18 @@ class AssessmentQuestionsScreen extends ViewModelWidget { ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - itemCount: viewModel.assessments[index].options?.length, + itemCount: viewModel.assessmentQuestions[index].options?.length, itemBuilder: (context, inner) => _buildAnswer( onTap: () => viewModel.setSelectedAnswer( question: index + 1, - option: viewModel.assessments[index].options?[inner]), - title: viewModel.assessments[index].options?[inner].optionText ?? '', + option: viewModel.assessmentQuestions[index].options?[inner]), + title: + viewModel.assessmentQuestions[index].options?[inner].optionText ?? + '', selected: viewModel.isSelectedAnswer( question: index + 1, - answer: viewModel.assessments[index].options?[inner].optionText ?? + answer: viewModel + .assessmentQuestions[index].options?[inner].optionText ?? ''), ), ); @@ -150,8 +140,8 @@ class AssessmentQuestionsScreen extends ViewModelWidget { onTap: viewModel.selectedAnswers.containsKey(question.toString()) ? () => viewModel.nextQuestion() : null, - text: viewModel.currentQuestion == viewModel.assessments.length - 1 - ? 'Finish' + text: viewModel.currentQuestionIndex == viewModel.assessmentQuestions.length - 1 + ? 'Finish Level' : 'Continue', ); } diff --git a/lib/ui/views/assessment/screens/assessment_result_screen.dart b/lib/ui/views/assessment/screens/assessment_result_screen.dart index 8943478..bbdea59 100644 --- a/lib/ui/views/assessment/screens/assessment_result_screen.dart +++ b/lib/ui/views/assessment/screens/assessment_result_screen.dart @@ -75,7 +75,7 @@ class AssessmentResultScreen extends ViewModelWidget { ]; Widget _buildTitle(AssessmentViewModel viewModel) => Text( - 'You’re likely a ${viewModel.proficiencyLevel.name.toUpperCase()} speaker!', + 'You’re likely a ${viewModel.proficiencyLevel?.toUpperCase()} speaker!', style: style25DG600, textAlign: TextAlign.center, ); @@ -87,12 +87,12 @@ class AssessmentResultScreen extends ViewModelWidget { ); Widget _buildIconWrapper(AssessmentViewModel viewModel) => - viewModel.proficiencyLevel != ProficiencyLevels.none + viewModel.proficiencyLevel != null ? _buildIcon(viewModel) : Container(); Widget _buildIcon(AssessmentViewModel viewModel) => SvgPicture.asset( - 'assets/icons/${viewModel.proficiencyLevel.name.substring(0, 1)}_${viewModel.proficiencyLevel.name.substring(1)}.svg'); + 'assets/icons/${viewModel.proficiencyLevel?.substring(0, 1).toLowerCase()}_${viewModel.proficiencyLevel?.substring(1).toLowerCase()}.svg'); Widget _buildSecondarySubtitle() => Text( 'Let\'s start your practice', diff --git a/lib/ui/views/assessment/screens/start_lesson_screen.dart b/lib/ui/views/assessment/screens/start_lesson_screen.dart index ba342a7..fdf4873 100644 --- a/lib/ui/views/assessment/screens/start_lesson_screen.dart +++ b/lib/ui/views/assessment/screens/start_lesson_screen.dart @@ -14,9 +14,9 @@ class StartLessonScreen extends ViewModelWidget { const StartLessonScreen({super.key}); Future _start(AssessmentViewModel viewModel) async { - if (viewModel.proficiencyLevel != ProficiencyLevels.none) { + if (viewModel.proficiencyLevel != null) { Map data = { - 'knowledge_level': viewModel.proficiencyLevel.name.toUpperCase() + 'knowledge_level': viewModel.proficiencyLevel?.toUpperCase() }; viewModel.addUserData(data); diff --git a/lib/ui/views/call_support/call_support_view.dart b/lib/ui/views/call_support/call_support_view.dart index 816ee07..57c1c3e 100644 --- a/lib/ui/views/call_support/call_support_view.dart +++ b/lib/ui/views/call_support/call_support_view.dart @@ -49,7 +49,7 @@ class CallSupportView extends StackedView { Widget _buildAppbar(CallSupportViewModel viewModel) => SmallAppBar( showBackButton: true, - onTap: viewModel.pop, + onPop: viewModel.pop, title: 'Call Support', ); diff --git a/lib/ui/views/course/course_view.dart b/lib/ui/views/course/course_view.dart index 592c1a0..b341e13 100644 --- a/lib/ui/views/course/course_view.dart +++ b/lib/ui/views/course/course_view.dart @@ -56,7 +56,7 @@ class CourseView extends StackedView { ); Widget _buildAppBar(CourseViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, ); diff --git a/lib/ui/views/course_lesson/course_lesson_view.dart b/lib/ui/views/course_lesson/course_lesson_view.dart index b4e5a2d..b311cd5 100644 --- a/lib/ui/views/course_lesson/course_lesson_view.dart +++ b/lib/ui/views/course_lesson/course_lesson_view.dart @@ -57,7 +57,7 @@ class CourseLessonView extends StackedView { ); Widget _buildAppBar(CourseLessonViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, title: 'Course Detail', ); diff --git a/lib/ui/views/course_lesson_detail/course_lesson_detail_view.dart b/lib/ui/views/course_lesson_detail/course_lesson_detail_view.dart index e53d2a3..52da985 100644 --- a/lib/ui/views/course_lesson_detail/course_lesson_detail_view.dart +++ b/lib/ui/views/course_lesson_detail/course_lesson_detail_view.dart @@ -57,7 +57,7 @@ class CourseLessonDetailView extends StackedView { child: _buildAppBar(viewModel)); Widget _buildAppBar(CourseLessonDetailViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, title: lesson.title ?? '', ); diff --git a/lib/ui/views/course_payment/course_payment_view.dart b/lib/ui/views/course_payment/course_payment_view.dart index 85b8aac..5ca8630 100644 --- a/lib/ui/views/course_payment/course_payment_view.dart +++ b/lib/ui/views/course_payment/course_payment_view.dart @@ -50,7 +50,7 @@ class CoursePaymentView extends StackedView { ); Widget _buildAppBar(CoursePaymentViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, ); diff --git a/lib/ui/views/course_practice/course_practice_view.dart b/lib/ui/views/course_practice/course_practice_view.dart index 16b8020..ad4e19e 100644 --- a/lib/ui/views/course_practice/course_practice_view.dart +++ b/lib/ui/views/course_practice/course_practice_view.dart @@ -53,7 +53,7 @@ class CoursePracticeView extends StackedView { ); Widget _buildAppBar(CoursePracticeViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, ); diff --git a/lib/ui/views/course_practice_question/course_practice_question_viewmodel.dart b/lib/ui/views/course_practice_question/course_practice_question_viewmodel.dart index db08193..2b243de 100644 --- a/lib/ui/views/course_practice_question/course_practice_question_viewmodel.dart +++ b/lib/ui/views/course_practice_question/course_practice_question_viewmodel.dart @@ -4,7 +4,7 @@ import 'package:stacked_services/stacked_services.dart'; import '../../../app/app.locator.dart'; import '../../../models/option.dart'; -import '../../../models/question.dart'; +import '../../../models/assessment_question.dart'; import '../../../services/api_service.dart'; import '../../../services/status_checker_service.dart'; import '../../common/app_colors.dart'; @@ -38,13 +38,14 @@ class CoursePracticeQuestionViewModel extends FormViewModel { bool get focusAnswer => _focusAnswer; - Question? _currentQuestion; + AssessmentQuestion? _currentQuestion; - Question? get currentQuestion => _currentQuestion; + AssessmentQuestion? get currentQuestion => _currentQuestion; - List _coursePracticeQuestions = []; + List _coursePracticeQuestions = []; - List get coursePracticeQuestions => _coursePracticeQuestions; + List get coursePracticeQuestions => + _coursePracticeQuestions; int _currentQuestionIndex = 0; diff --git a/lib/ui/views/course_subcategory/course_subcategory_view.dart b/lib/ui/views/course_subcategory/course_subcategory_view.dart index 8a659f8..ac49a00 100644 --- a/lib/ui/views/course_subcategory/course_subcategory_view.dart +++ b/lib/ui/views/course_subcategory/course_subcategory_view.dart @@ -59,7 +59,7 @@ class CourseSubcategoryView extends StackedView { ); Widget _buildAppBar(CourseSubcategoryViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, ); diff --git a/lib/ui/views/downloads/downloads_view.dart b/lib/ui/views/downloads/downloads_view.dart index bba2779..4af46f2 100644 --- a/lib/ui/views/downloads/downloads_view.dart +++ b/lib/ui/views/downloads/downloads_view.dart @@ -53,7 +53,7 @@ class DownloadsView extends StackedView { ]; Widget _buildAppbar(DownloadsViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, title: 'Offline Downloads', ); diff --git a/lib/ui/views/failure/failure_view.dart b/lib/ui/views/failure/failure_view.dart index 0b1ffe4..297cceb 100644 --- a/lib/ui/views/failure/failure_view.dart +++ b/lib/ui/views/failure/failure_view.dart @@ -9,8 +9,10 @@ import 'failure_viewmodel.dart'; class FailureView extends StackedView { final String label; + final GestureTapCallback onTap; - const FailureView({Key? key, required this.label}) : super(key: key); + const FailureView({Key? key, required this.onTap, required this.label}) + : super(key: key); @override FailureViewModel viewModelBuilder(BuildContext context) => FailureViewModel(); @@ -48,19 +50,38 @@ class FailureView extends StackedView { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.stretch, - children: _buildUpperColumnChildren(), + children: _buildColumnChildren(), ); - List _buildUpperColumnChildren() => + List _buildColumnChildren() => [_buildIconWrapper(), _buildSafeWrapper()]; - Widget _buildSafeWrapper() => SafeArea(child: _buildLoadingTextContainer()); - - Widget _buildLoadingTextContainer() => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildLoadingTextWrapper(), + Widget _buildIconWrapper() => Padding( + padding: const EdgeInsets.only(top: 100), + child: _buildIcon(), ); + Widget _buildIcon() => SvgPicture.asset('assets/icons/logo.svg', height: 50); + + Widget _buildSafeWrapper() => SafeArea(child: _buildBottomSectionWrapper()); + + Widget _buildBottomSectionWrapper() => Padding( + padding: const EdgeInsets.only(bottom: 50), + child: _buildBottomSectionColumn(), + ); + + Widget _buildBottomSectionColumn() => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: _buildBottomSectionChildren(), + ); + + List _buildBottomSectionChildren() => [ + _buildLoadingTextWrapper(), + verticalSpaceSmall, + _buildRetryButtonWrapper() + ]; + Widget _buildLoadingTextWrapper() => Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, @@ -85,10 +106,14 @@ class FailureView extends StackedView { Widget _buildIndicator() => const CustomCircularProgressIndicator(color: kcWhite); - Widget _buildIconWrapper() => Padding( - padding: const EdgeInsets.only(top: 100), - child: _buildIcon(), + Widget _buildRetryButtonWrapper() => GestureDetector( + onTap: onTap, + child: _buildRetryButton(), ); - Widget _buildIcon() => SvgPicture.asset('assets/icons/logo.svg', height: 50); + Widget _buildRetryButton() => Text( + 'Retry', + style: style16W600.copyWith( + fontStyle: FontStyle.italic, decoration: TextDecoration.underline), + ); } diff --git a/lib/ui/views/home/home_view.dart b/lib/ui/views/home/home_view.dart index 18c141a..ce4e9f7 100644 --- a/lib/ui/views/home/home_view.dart +++ b/lib/ui/views/home/home_view.dart @@ -1,16 +1,21 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; -import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart'; import 'package:yimaru_app/ui/views/profile/profile_view.dart'; -import 'package:yimaru_app/ui/views/startup/startup_view.dart'; import 'package:yimaru_app/ui/widgets/coming_soon.dart'; +import '../../common/enmus.dart'; +import '../../widgets/page_loading_indicator.dart'; import 'home_viewmodel.dart'; class HomeView extends StackedView { const HomeView({Key? key}) : super(key: key); + @override + void onViewModelReady(HomeViewModel viewModel) async { + await viewModel.inAppUpdate(); + super.onViewModelReady(viewModel); + } @override HomeViewModel viewModelBuilder(BuildContext context) => HomeViewModel(); @@ -20,8 +25,6 @@ class HomeView extends StackedView { BuildContext context, HomeViewModel viewModel, Widget? child) => _buildScaffold(viewModel); - - Widget _buildScaffold(HomeViewModel viewModel) => Scaffold( body: getViewForIndex(viewModel.currentPage), bottomNavigationBar: _buildBottomNav(viewModel), @@ -56,22 +59,22 @@ class HomeView extends StackedView { label: 'Profile', icon: _buildProfileIcon(), ); -} -Widget _buildLearnIcon() => const Icon(Icons.school); + Widget _buildLearnIcon() => const Icon(Icons.school); -Widget _buildCourseIcon() => const Icon(Icons.book); + Widget _buildCourseIcon() => const Icon(Icons.book); -Widget _buildProfileIcon() => const Icon(Icons.person); + Widget _buildProfileIcon() => const Icon(Icons.person); -Widget getViewForIndex(int index) { - switch (index) { - case 0: - return const LearnProgramView(); - case 1: - return const ComingSoon(); + Widget getViewForIndex(int index) { + switch (index) { + case 0: + return const LearnProgramView(); + case 1: + return const ComingSoon(); - default: - return const ProfileView(); + default: + return const ProfileView(); + } } } diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart index 01b9c34..1c35e92 100644 --- a/lib/ui/views/home/home_viewmodel.dart +++ b/lib/ui/views/home/home_viewmodel.dart @@ -1,21 +1,19 @@ import 'package:yimaru_app/app/app.bottomsheets.dart'; import 'package:yimaru_app/app/app.locator.dart'; -import 'package:yimaru_app/app/app.router.dart'; import 'package:yimaru_app/models/user.dart'; import 'package:yimaru_app/services/status_checker_service.dart'; import 'package:yimaru_app/ui/common/app_strings.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; -import '../../../services/api_service.dart'; import '../../../services/authentication_service.dart'; -import '../../../services/image_downloader_service.dart'; -import '../../common/enmus.dart'; -import '../../common/ui_helpers.dart'; +import '../../../services/in_app_update_service.dart'; class HomeViewModel extends ReactiveViewModel { - + // Dependency injection + final _statusChecker = locator(); final _bottomSheetService = locator(); + final _inAppUpdateService = locator(); final _authenticationService = locator(); @override @@ -46,9 +44,12 @@ class HomeViewModel extends ReactiveViewModel { rebuildUi(); } + // Remote api calls - - - - + // In-app update + Future inAppUpdate() async { + if (await _statusChecker.checkConnection()) { + await _inAppUpdateService.checkForUpdate(); + } + } } diff --git a/lib/ui/views/language/language_view.dart b/lib/ui/views/language/language_view.dart index 791145e..35883b0 100644 --- a/lib/ui/views/language/language_view.dart +++ b/lib/ui/views/language/language_view.dart @@ -73,7 +73,7 @@ class LanguageView extends StackedView { Widget _buildAppbar(LanguageViewModel viewModel) => SmallAppBar( showBackButton: true, - onTap: viewModel.pop, + onPop: viewModel.pop, title: 'Language Preference', ); diff --git a/lib/ui/views/learn_course/learn_course_view.dart b/lib/ui/views/learn_course/learn_course_view.dart index 6625bbc..b36b9f8 100644 --- a/lib/ui/views/learn_course/learn_course_view.dart +++ b/lib/ui/views/learn_course/learn_course_view.dart @@ -55,7 +55,7 @@ class LearnCourseView extends StackedView { ); Widget _buildAppBar(LearnCourseViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, ); diff --git a/lib/ui/views/learn_lesson/learn_lesson_view.dart b/lib/ui/views/learn_lesson/learn_lesson_view.dart index de667d0..98ee354 100644 --- a/lib/ui/views/learn_lesson/learn_lesson_view.dart +++ b/lib/ui/views/learn_lesson/learn_lesson_view.dart @@ -77,7 +77,7 @@ class LearnLessonView extends StackedView { ); Widget _buildAppBar(LearnLessonViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, ); diff --git a/lib/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart b/lib/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart index eee89dc..261a808 100644 --- a/lib/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart +++ b/lib/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart @@ -17,7 +17,7 @@ class LearnLessonDetailView extends StackedView { Future _navigate(LearnLessonDetailViewModel viewModel) async { await viewModel.pause(); - await viewModel.navigateToLearnPractice(); + await viewModel.navigateToLearnPractice(lesson.id ?? 0); } @override @@ -60,7 +60,7 @@ class LearnLessonDetailView extends StackedView { child: _buildAppBar(viewModel)); Widget _buildAppBar(LearnLessonDetailViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, ); diff --git a/lib/ui/views/learn_lesson_detail/learn_lesson_detail_viewmodel.dart b/lib/ui/views/learn_lesson_detail/learn_lesson_detail_viewmodel.dart index 11979bc..697ca6f 100644 --- a/lib/ui/views/learn_lesson_detail/learn_lesson_detail_viewmodel.dart +++ b/lib/ui/views/learn_lesson_detail/learn_lesson_detail_viewmodel.dart @@ -4,6 +4,7 @@ import 'package:stacked_services/stacked_services.dart'; import 'package:yimaru_app/ui/common/enmus.dart'; import '../../../app/app.locator.dart'; +import '../../../app/app.router.dart'; import '../../../services/status_checker_service.dart'; class LearnLessonDetailViewModel extends BaseViewModel { @@ -44,6 +45,6 @@ class LearnLessonDetailViewModel extends BaseViewModel { // Navigation void pop() => _navigationService.back(); - Future navigateToLearnPractice() async {} - // await _navigationService.navigateToLearnPracticeView(); + Future navigateToLearnPractice(int id) async => await _navigationService + .navigateToLearnPracticeView(id: id, practice: LearnPractices.lesson); } diff --git a/lib/ui/views/learn_module/learn_module_view.dart b/lib/ui/views/learn_module/learn_module_view.dart index d1e33d9..8086d38 100644 --- a/lib/ui/views/learn_module/learn_module_view.dart +++ b/lib/ui/views/learn_module/learn_module_view.dart @@ -19,7 +19,7 @@ class LearnModuleView extends StackedView { @override void onViewModelReady(LearnModuleViewModel viewModel) async { - await viewModel.getLearnModules(1); + await viewModel.getLearnModules(course.id ?? 0); super.onViewModelReady(viewModel); } @@ -58,7 +58,7 @@ class LearnModuleView extends StackedView { ); Widget _buildAppBar(LearnModuleViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, ); @@ -116,10 +116,10 @@ class LearnModuleView extends StackedView { physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) => _buildTile( module: viewModel.modules[index], - onModuleTap: () async => - await viewModel.navigateToLearnLesson(viewModel.modules[index]), onPracticeTap: () async => await viewModel .navigateToLearnPractice(viewModel.modules[index].id ?? 0), + onModuleTap: () async => + await viewModel.navigateToLearnLesson(viewModel.modules[index]), ), ); diff --git a/lib/ui/views/learn_practice/learn_practice_view.dart b/lib/ui/views/learn_practice/learn_practice_view.dart index 564b884..99e4234 100644 --- a/lib/ui/views/learn_practice/learn_practice_view.dart +++ b/lib/ui/views/learn_practice/learn_practice_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/finish_learn_practice_screen.dart'; +import 'package:yimaru_app/ui/views/learn_practice/screens/learn_loading_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_completion_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_result_screen.dart'; import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_questions_screen.dart'; @@ -78,7 +79,20 @@ class LearnPracticeView extends StackedView { Widget _buildBodyState(LearnPracticeViewModel viewModel) => viewModel.busy(StateObjects.learnPractices) ? const PageLoadingIndicator() - : _buildBody(viewModel); + : viewModel.practices.isEmpty || viewModel.questions.isEmpty + ? _buildPageLoadingIndicator(viewModel) + : _buildBody(viewModel); + + Widget _buildPageLoadingIndicator(LearnPracticeViewModel viewModel) => + LearnLoadingScreen( + isLoading: viewModel.busy(StateObjects.learnPractices), + onTap: () async => + await viewModel.getLearnPractices(id: id, practice: practice), + onPop: viewModel.practices.isEmpty || viewModel.questions.isEmpty + ? viewModel.pop + : null, + isEmpty: viewModel.practices.isEmpty || viewModel.questions.isEmpty, + ); Widget _buildBody(LearnPracticeViewModel viewModel) => IndexedStack( index: viewModel.currentPage, children: _buildScreens(viewModel)); diff --git a/lib/ui/views/learn_practice/learn_practice_viewmodel.dart b/lib/ui/views/learn_practice/learn_practice_viewmodel.dart index f955ab3..364cc68 100644 --- a/lib/ui/views/learn_practice/learn_practice_viewmodel.dart +++ b/lib/ui/views/learn_practice/learn_practice_viewmodel.dart @@ -149,6 +149,10 @@ class LearnPracticeViewModel extends ReactiveViewModel { await _audioPlayerService.playUrl(question.voicePrompt ?? ''); } + Future replayVoicePrompt(LearnQuestion question) async { + await _audioPlayerService.playUrl(question.voicePrompt ?? ''); + } + Future playResult( {required Map answer, required Voice voice}) async { setBusyObject(playing: voice, object: answer['busy_object']); @@ -222,6 +226,7 @@ class LearnPracticeViewModel extends ReactiveViewModel { _questionSetController.nextPage( duration: const Duration(milliseconds: 350), curve: Curves.easeInOutCubic); + await playVoicePrompt(_questions[index]); } else { goTo(3); } diff --git a/lib/ui/views/learn_practice/screens/finish_learn_practice_screen.dart b/lib/ui/views/learn_practice/screens/finish_learn_practice_screen.dart index eafd866..540e06d 100644 --- a/lib/ui/views/learn_practice/screens/finish_learn_practice_screen.dart +++ b/lib/ui/views/learn_practice/screens/finish_learn_practice_screen.dart @@ -50,7 +50,7 @@ class FinishLearnPracticeScreen Widget _buildAppBar(LearnPracticeViewModel viewModel) => SmallAppBar( showBackButton: true, - onTap: viewModel.goBack, + onPop: viewModel.goBack, title: 'Practice Speaking', ); diff --git a/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart b/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart index ba8b00b..dffd20d 100644 --- a/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart +++ b/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart @@ -30,8 +30,8 @@ class InteractLearnPracticeScreen viewModel.stopRecording(); } - void _start(LearnPracticeViewModel viewModel) => - viewModel.playVoicePrompt(question); + void _reply(LearnPracticeViewModel viewModel) => + viewModel.replayVoicePrompt(question); Future _stop(LearnPracticeViewModel viewModel) async => await viewModel.nextQuestion(index: index, question: question); @@ -116,7 +116,7 @@ class InteractLearnPracticeScreen required LearnPracticeViewModel viewModel}) => SmallAppBar( showBackButton: true, - onTap: () async => + onPop: () async => await _showSheet(context: context, viewModel: viewModel), title: 'Practice Speaking ($index/${viewModel.questions.length})'); @@ -263,7 +263,7 @@ class InteractLearnPracticeScreen : kcLightGrey, onTap: viewModel.recordingState == VoiceRecordingState.pending && viewModel.player.state != PlayerState.playing - ? () => _start(viewModel) + ? () => _reply(viewModel) : null, ); @@ -272,15 +272,15 @@ class InteractLearnPracticeScreen Widget _buildMicButton(LearnPracticeViewModel viewModel) => ElevatedButton( style: ButtonStyle( + shadowColor: const WidgetStatePropertyAll(kcWhite), + shape: const WidgetStatePropertyAll(CircleBorder()), + padding: const WidgetStatePropertyAll(EdgeInsets.all(15)), backgroundColor: WidgetStatePropertyAll( viewModel.player.state == PlayerState.playing || viewModel.busy(StateObjects.recordLearnPracticeAnswer) ? kcVeryLightGrey : kcPrimaryColor, ), - shadowColor: const WidgetStatePropertyAll(kcWhite), - shape: const WidgetStatePropertyAll(CircleBorder()), - padding: const WidgetStatePropertyAll(EdgeInsets.all(15)), ), onPressed: () async => viewModel.player.state == PlayerState.playing || viewModel.busy(StateObjects.recordLearnPracticeAnswer) diff --git a/lib/ui/views/learn_practice/screens/learn_loading_screen.dart b/lib/ui/views/learn_practice/screens/learn_loading_screen.dart new file mode 100644 index 0000000..abeb8ad --- /dev/null +++ b/lib/ui/views/learn_practice/screens/learn_loading_screen.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:yimaru_app/ui/widgets/no_data_indicator.dart'; +import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart'; +import 'package:yimaru_app/ui/widgets/small_app_bar.dart'; + +import '../../../common/app_colors.dart'; +import '../../../common/ui_helpers.dart'; +import '../../../widgets/large_app_bar.dart'; +import '../../../widgets/refresh_button.dart'; + +class LearnLoadingScreen extends StatelessWidget { + final bool isEmpty; + final bool isLoading; + final GestureTapCallback? onPop; + final GestureTapCallback? onTap; + + const LearnLoadingScreen( + {super.key, + this.onTap, + this.onPop, + required this.isEmpty, + required this.isLoading}); + + @override + Widget build(BuildContext context) => _buildScaffoldWrapper(); + + Widget _buildScaffoldWrapper() => Scaffold( + backgroundColor: kcBackgroundColor, + body: _buildScaffold(), + ); + + Widget _buildScaffold() => SafeArea(child: _buildStack()); + + Widget _buildStack() => Stack( + children: [ + _buildColumn(), + if (isEmpty) _buildRefreshButtonWrapper(), + if (isLoading) _buildPageIndicator() + ], + ); + + Widget _buildColumn() => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildColumnChildren(), + ); + + List _buildColumnChildren() => + [verticalSpaceMedium, _buildAppBarWrapper(), _buildBody()]; + + Widget _buildAppBarWrapper() => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _buildAppBar(), + ); + + Widget _buildAppBar() => SmallAppBar(onPop: onPop, showBackButton: true); + + Widget _buildBody() => Expanded(child: Container()); + + Widget _buildPageIndicator() => const PageLoadingIndicator(); + + Widget _buildRefreshButtonWrapper() => Align( + alignment: Alignment.center, + child:_buildRefreshButton()); + + Widget _buildRefreshButton()=> NoDataIndicator(title:'No practice available!' ,onTap: onTap,); +} diff --git a/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart index 731bf6c..0e1bfe7 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart @@ -72,7 +72,7 @@ class LearnPracticeIntroScreen extends ViewModelWidget { SmallAppBar( showBackButton: true, title: 'Practice Speaking', - onTap: () async => + onPop: () async => await _showSheet(context: context, viewModel: viewModel), ); diff --git a/lib/ui/views/learn_practice/screens/learn_practice_questions_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_questions_screen.dart index 1dff69e..46362e2 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_questions_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_questions_screen.dart @@ -40,7 +40,7 @@ class LearnPracticeQuestionsScreen required LearnQuestion question, }) => [ - _buildStartLearnPracticeScreen(index: index, question: question), + if(index ==1) _buildStartLearnPracticeScreen(index: index, question: question), _buildInteractLearnPracticeScreen(index: index, question: question) ]; diff --git a/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart index 1e27b26..1901146 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart @@ -86,7 +86,7 @@ class LearnPracticeResultScreen SmallAppBar( title: 'Result', showBackButton: true, - onTap: () async => + onPop: () async => await _showSheet(context: context, viewModel: viewModel), ); diff --git a/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart b/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart index 50ccce6..2665e7c 100644 --- a/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart +++ b/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart @@ -97,7 +97,7 @@ class StartLearnPracticeScreen extends ViewModelWidget { required LearnPracticeViewModel viewModel}) => SmallAppBar( showBackButton: true, - onTap: () async => + onPop: () async => await _showSheet(context: context, viewModel: viewModel), title: 'Practice Speaking ($index/${viewModel.questions.length})'); diff --git a/lib/ui/views/login/login_viewmodel.dart b/lib/ui/views/login/login_viewmodel.dart index 1fb0beb..f408ae9 100644 --- a/lib/ui/views/login/login_viewmodel.dart +++ b/lib/ui/views/login/login_viewmodel.dart @@ -155,7 +155,6 @@ class LoginViewModel extends ReactiveViewModel // Navigation - Future navigateToRegister() async => await _navigationService.navigateToRegisterView(); @@ -165,9 +164,6 @@ class LoginViewModel extends ReactiveViewModel Future replaceWithStartUp() async => await _navigationService.clearStackAndShow(Routes.startupView); - - - // Remote api calls // Login with email diff --git a/lib/ui/views/onboarding/onboarding_view.dart b/lib/ui/views/onboarding/onboarding_view.dart index a799e32..cd30cf0 100644 --- a/lib/ui/views/onboarding/onboarding_view.dart +++ b/lib/ui/views/onboarding/onboarding_view.dart @@ -36,6 +36,7 @@ class OnboardingView extends StackedView void _initClearData() { topicController.clear(); + regionController.clear(); fullNameController.clear(); challengeController.clear(); occupationController.clear(); diff --git a/lib/ui/views/onboarding/onboarding_viewmodel.dart b/lib/ui/views/onboarding/onboarding_viewmodel.dart index d5b8b45..6b753d5 100644 --- a/lib/ui/views/onboarding/onboarding_viewmodel.dart +++ b/lib/ui/views/onboarding/onboarding_viewmodel.dart @@ -191,7 +191,7 @@ class OnboardingViewModel extends ReactiveViewModel final List _topics = [ 'Food & Cooking', - ' Hobbies, Sports, Music', + 'Hobbies, Sports, Music', 'Tech, News, Business', 'Travel, Places, Culture', 'Other' @@ -547,6 +547,7 @@ class OnboardingViewModel extends ReactiveViewModel // Reset country region form screen void resetCountryRegionFormScreen() { + _focusRegion = false; _selectedCountry = 'Ethiopia'; rebuildUi(); } @@ -613,6 +614,4 @@ class OnboardingViewModel extends ReactiveViewModel Future navigateToAssessment() async => await _navigationService.navigateToAssessmentView(data: _userData); - - } diff --git a/lib/ui/views/privacy_policy/privacy_policy_view.dart b/lib/ui/views/privacy_policy/privacy_policy_view.dart index 521191f..6ae303b 100644 --- a/lib/ui/views/privacy_policy/privacy_policy_view.dart +++ b/lib/ui/views/privacy_policy/privacy_policy_view.dart @@ -55,7 +55,7 @@ class PrivacyPolicyView extends StackedView { Widget _buildAppbar(PrivacyPolicyViewModel viewModel) => SmallAppBar( showBackButton: true, - onTap: viewModel.pop, + onPop: viewModel.pop, title: 'Privacy Policy', ); diff --git a/lib/ui/views/profile/profile_view.dart b/lib/ui/views/profile/profile_view.dart index 54bb6b3..1e423a5 100644 --- a/lib/ui/views/profile/profile_view.dart +++ b/lib/ui/views/profile/profile_view.dart @@ -3,6 +3,7 @@ import 'package:stacked/stacked.dart'; import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/enmus.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; +import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart'; import 'package:yimaru_app/ui/widgets/profile_card.dart'; import 'package:yimaru_app/ui/widgets/profile_image.dart'; import 'package:yimaru_app/ui/widgets/view_profile_button.dart'; @@ -155,7 +156,7 @@ class ProfileView extends StackedView { children: _buildSettingsChildren(viewModel)); List _buildSettingsChildren(ProfileViewModel viewModel) => [ - _buildDownloadsCard(viewModel), + // _buildDownloadsCard(viewModel), _buildProgressCard(viewModel), _buildAccountCard(viewModel), _buildSupportCard(viewModel) @@ -191,7 +192,7 @@ class ProfileView extends StackedView { Widget _buildLogOutButton(ProfileViewModel viewModel) => CustomElevatedButton( height: 55, - text: 'Log Out', + text: 'Logout', borderRadius: 12, foregroundColor: kcRed, backgroundColor: kcRed.withOpacity(0.25), diff --git a/lib/ui/views/profile/profile_viewmodel.dart b/lib/ui/views/profile/profile_viewmodel.dart index 4c6d119..083e9e4 100644 --- a/lib/ui/views/profile/profile_viewmodel.dart +++ b/lib/ui/views/profile/profile_viewmodel.dart @@ -61,13 +61,6 @@ class ProfileViewModel extends ReactiveViewModel { } } - // Logout - Future _logout() async { - await _googleAuthService.logout(); - await _authenticationService.logout(); - await _navigationService.replaceWithLoginView(); - } - // Dialog Future showAbortDialog() async { DialogResponse? response = await _dialogService.showDialog( @@ -82,13 +75,6 @@ class ProfileViewModel extends ReactiveViewModel { return response?.confirmed; } - Future logout() async { - bool? response = await showAbortDialog(); - if (response != null && response) { - await _logout(); - } - } - // Navigation void pop() => _navigationService.back(); @@ -107,6 +93,9 @@ class ProfileViewModel extends ReactiveViewModel { Future navigateToSupport() async => await _navigationService.navigateToSupportView(); + Future navigateToLogin() async => + await _navigationService.clearStackAndShow(Routes.loginView); + // Remote api call // Update profile @@ -115,10 +104,21 @@ class ProfileViewModel extends ReactiveViewModel { Future _updateProfilePicture(String image) async { if (await _statusChecker.checkConnection()) { - Map data = { - 'profile_picture_url': image, - }; + Map data = {'profile_picture_url': image}; await _apiService.updateProfileImage(data: data, userId: _user?.userId); } } + + Future logout() async { + bool? response = await showAbortDialog(); + if (response != null && response) { + await _logout(); + } + } + + Future _logout() async { + await _googleAuthService.logout(); + await _authenticationService.logout(); + await navigateToLogin(); + } } diff --git a/lib/ui/views/profile_detail/profile_detail_view.dart b/lib/ui/views/profile_detail/profile_detail_view.dart index 3fb13e7..c600054 100644 --- a/lib/ui/views/profile_detail/profile_detail_view.dart +++ b/lib/ui/views/profile_detail/profile_detail_view.dart @@ -34,13 +34,13 @@ class ProfileDetailView extends StackedView Future _update(ProfileDetailViewModel viewModel) async { Map data = { - 'region':regionController.text, + 'region': regionController.text, 'gender': viewModel.selectedGender, 'last_name': lastNameController.text, 'country': viewModel.selectedCountry, 'first_name': firstNameController.text, 'occupation': occupationController.text, - 'birth_day': DateFormat('d MMM, yyyy').format(DateTime.now()), + 'birth_day': DateFormat('yyyy-MM-dd').format(DateTime.now()), }; viewModel.addUserData(data); @@ -75,7 +75,6 @@ class ProfileDetailView extends StackedView viewModel.clearUserData(); viewModel.setGender(viewModel.user?.gender ?? ''); viewModel.setSelectedCountry(viewModel.user?.country ?? 'Ethiopia'); - } @override @@ -147,7 +146,7 @@ class ProfileDetailView extends StackedView ]; Widget _buildAppbar(ProfileDetailViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, title: 'Edit Profile', ); @@ -192,9 +191,8 @@ class ProfileDetailView extends StackedView verticalSpaceSmall, _buildCountryDropdown(viewModel), verticalSpaceMedium, - _buildRegionFormFieldWrapper(viewModel), + _buildRegionFormFieldWrapper(viewModel), verticalSpaceMedium, - _buildOccupationDropdownWrapper(viewModel), verticalSpaceLarge, _buildLowerColumn(viewModel) @@ -533,9 +531,10 @@ class ProfileDetailView extends StackedView ); List _buildRegionFormFieldChildren( - ProfileDetailViewModel viewModel) => + ProfileDetailViewModel viewModel) => [ - _buildRegionFormFieldLabel(),verticalSpaceSmall, + _buildRegionFormFieldLabel(), + verticalSpaceSmall, _buildRegionFormField(viewModel), if (viewModel.hasRegionValidationMessage && viewModel.focusRegion) verticalSpaceTiny, diff --git a/lib/ui/views/profile_detail/profile_detail_viewmodel.dart b/lib/ui/views/profile_detail/profile_detail_viewmodel.dart index a1eae51..67e9ffc 100644 --- a/lib/ui/views/profile_detail/profile_detail_viewmodel.dart +++ b/lib/ui/views/profile_detail/profile_detail_viewmodel.dart @@ -47,7 +47,6 @@ class ProfileDetailViewModel extends ReactiveViewModel String? get selectedGender => _selectedGender; - // First name bool _focusPhoneNumber = false; @@ -63,7 +62,6 @@ class ProfileDetailViewModel extends ReactiveViewModel String get selectedCountry => _selectedCountry; - // Occupation bool _focusOccupation = false; @@ -97,7 +95,6 @@ class ProfileDetailViewModel extends ReactiveViewModel rebuildUi(); } - // Phone number void setPhoneNumberFocus() { _focusPhoneNumber = true; @@ -113,167 +110,165 @@ class ProfileDetailViewModel extends ReactiveViewModel // Country // Country List getCountries() => [ - "Afghanistan", - "Albania", - "Algeria", - "Andorra", - "Angola", - "Argentina", - "Armenia", - "Australia", - "Austria", - "Azerbaijan", - "Bahrain", - "Bangladesh", - "Belarus", - "Belgium", - "Belize", - "Benin", - "Bhutan", - "Bolivia", - "Bosnia and Herzegovina", - "Botswana", - "Brazil", - "Brunei", - "Bulgaria", - "Burkina Faso", - "Burundi", - "Cambodia", - "Cameroon", - "Canada", - "Chad", - "Chile", - "China", - "Colombia", - "Comoros", - "Congo", - "Costa Rica", - "Croatia", - "Cuba", - "Cyprus", - "Czech Republic", - "Denmark", - "Djibouti", - "Dominican Republic", - "Ecuador", - "Egypt", - "El Salvador", - "Eritrea", - "Estonia", - "Eswatini", - "Ethiopia", - "Finland", - "France", - "Gabon", - "Gambia", - "Georgia", - "Germany", - "Ghana", - "Greece", - "Guatemala", - "Guinea", - "Haiti", - "Honduras", - "Hungary", - "Iceland", - "India", - "Indonesia", - "Iran", - "Iraq", - "Ireland", - "Israel", - "Italy", - "Jamaica", - "Japan", - "Jordan", - "Kazakhstan", - "Kenya", - "Kuwait", - "Kyrgyzstan", - "Laos", - "Latvia", - "Lebanon", - "Liberia", - "Libya", - "Lithuania", - "Luxembourg", - "Madagascar", - "Malawi", - "Malaysia", - "Maldives", - "Mali", - "Malta", - "Mexico", - "Moldova", - "Monaco", - "Mongolia", - "Morocco", - "Mozambique", - "Myanmar", - "Namibia", - "Nepal", - "Netherlands", - "New Zealand", - "Nicaragua", - "Niger", - "Nigeria", - "North Korea", - "Norway", - "Oman", - "Pakistan", - "Panama", - "Paraguay", - "Peru", - "Philippines", - "Poland", - "Portugal", - "Qatar", - "Romania", - "Russia", - "Rwanda", - "Saudi Arabia", - "Senegal", - "Serbia", - "Singapore", - "Slovakia", - "Slovenia", - "Somalia", - "South Africa", - "South Korea", - "Spain", - "Sri Lanka", - "Sudan", - "Sweden", - "Switzerland", - "Syria", - "Taiwan", - "Tajikistan", - "Tanzania", - "Thailand", - "Tunisia", - "Turkey", - "Uganda", - "Ukraine", - "United Arab Emirates", - "United Kingdom", - "United States", - "Uruguay", - "Uzbekistan", - "Venezuela", - "Vietnam", - "Yemen", - "Zambia", - "Zimbabwe" - ]; + "Afghanistan", + "Albania", + "Algeria", + "Andorra", + "Angola", + "Argentina", + "Armenia", + "Australia", + "Austria", + "Azerbaijan", + "Bahrain", + "Bangladesh", + "Belarus", + "Belgium", + "Belize", + "Benin", + "Bhutan", + "Bolivia", + "Bosnia and Herzegovina", + "Botswana", + "Brazil", + "Brunei", + "Bulgaria", + "Burkina Faso", + "Burundi", + "Cambodia", + "Cameroon", + "Canada", + "Chad", + "Chile", + "China", + "Colombia", + "Comoros", + "Congo", + "Costa Rica", + "Croatia", + "Cuba", + "Cyprus", + "Czech Republic", + "Denmark", + "Djibouti", + "Dominican Republic", + "Ecuador", + "Egypt", + "El Salvador", + "Eritrea", + "Estonia", + "Eswatini", + "Ethiopia", + "Finland", + "France", + "Gabon", + "Gambia", + "Georgia", + "Germany", + "Ghana", + "Greece", + "Guatemala", + "Guinea", + "Haiti", + "Honduras", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Iran", + "Iraq", + "Ireland", + "Israel", + "Italy", + "Jamaica", + "Japan", + "Jordan", + "Kazakhstan", + "Kenya", + "Kuwait", + "Kyrgyzstan", + "Laos", + "Latvia", + "Lebanon", + "Liberia", + "Libya", + "Lithuania", + "Luxembourg", + "Madagascar", + "Malawi", + "Malaysia", + "Maldives", + "Mali", + "Malta", + "Mexico", + "Moldova", + "Monaco", + "Mongolia", + "Morocco", + "Mozambique", + "Myanmar", + "Namibia", + "Nepal", + "Netherlands", + "New Zealand", + "Nicaragua", + "Niger", + "Nigeria", + "North Korea", + "Norway", + "Oman", + "Pakistan", + "Panama", + "Paraguay", + "Peru", + "Philippines", + "Poland", + "Portugal", + "Qatar", + "Romania", + "Russia", + "Rwanda", + "Saudi Arabia", + "Senegal", + "Serbia", + "Singapore", + "Slovakia", + "Slovenia", + "Somalia", + "South Africa", + "South Korea", + "Spain", + "Sri Lanka", + "Sudan", + "Sweden", + "Switzerland", + "Syria", + "Taiwan", + "Tajikistan", + "Tanzania", + "Thailand", + "Tunisia", + "Turkey", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "United States", + "Uruguay", + "Uzbekistan", + "Venezuela", + "Vietnam", + "Yemen", + "Zambia", + "Zimbabwe" + ]; void setSelectedCountry(String value) { _selectedCountry = value; - rebuildUi(); } - // Occupation void setOccupationFocus() { _focusOccupation = true; diff --git a/lib/ui/views/progress/progress_view.dart b/lib/ui/views/progress/progress_view.dart index f973f9c..df7578e 100644 --- a/lib/ui/views/progress/progress_view.dart +++ b/lib/ui/views/progress/progress_view.dart @@ -56,7 +56,7 @@ class ProgressView extends StackedView { Widget _buildAppbar(ProgressViewModel viewModel) => SmallAppBar( title: 'My Progress', showBackButton: true, - onTap: viewModel.pop, + onPop: viewModel.pop, ); Widget _buildContentScrollViewWrapper(ProgressViewModel viewModel) => diff --git a/lib/ui/views/register/register_viewmodel.dart b/lib/ui/views/register/register_viewmodel.dart index 67bca97..07a50d0 100644 --- a/lib/ui/views/register/register_viewmodel.dart +++ b/lib/ui/views/register/register_viewmodel.dart @@ -11,7 +11,6 @@ import 'package:yimaru_app/ui/common/ui_helpers.dart'; import '../../../app/app.locator.dart'; import '../../../models/user.dart'; import '../../../services/google_auth_service.dart'; -import '../../../services/smart_auth_service.dart'; import '../../../services/status_checker_service.dart'; class RegisterViewModel extends ReactiveViewModel diff --git a/lib/ui/views/startup/startup_view.dart b/lib/ui/views/startup/startup_view.dart index 9c28170..317e943 100644 --- a/lib/ui/views/startup/startup_view.dart +++ b/lib/ui/views/startup/startup_view.dart @@ -51,8 +51,8 @@ class StartupView extends StackedView { Widget _buildColumn() => Column( mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _buildUpperColumnChildren(), ); diff --git a/lib/ui/views/startup/startup_viewmodel.dart b/lib/ui/views/startup/startup_viewmodel.dart index 49e27bd..52c37f1 100644 --- a/lib/ui/views/startup/startup_viewmodel.dart +++ b/lib/ui/views/startup/startup_viewmodel.dart @@ -1,7 +1,6 @@ import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; import 'package:yimaru_app/services/authentication_service.dart'; -import 'package:yimaru_app/services/in_app_update_service.dart'; import '../../../app/app.locator.dart'; import '../../../app/app.router.dart'; @@ -10,14 +9,12 @@ import '../../../services/api_service.dart'; import '../../../services/image_downloader_service.dart'; import '../../../services/status_checker_service.dart'; import '../../common/enmus.dart'; -import '../../common/ui_helpers.dart'; class StartupViewModel extends ReactiveViewModel { // Dependency injection final _apiService = locator(); final _statusChecker = locator(); final _navigationService = locator(); - final _inAppUpdateService = locator(); final _authenticationService = locator(); final _imageDownloaderService = locator(); @@ -30,15 +27,12 @@ class StartupViewModel extends ReactiveViewModel { User? get user => _user; - - // Place anything here that needs to happen before we get into the application + // Main startup and navigation logic Future runStartupLogic() async { final loggedIn = await _authenticationService.userLoggedIn(); final firstTimeInstall = await _authenticationService.isFirstTimeInstall(); - await _inAppUpdate(); - if (firstTimeInstall) { await _navigationService.replaceWithWelcomeView(); } else { @@ -52,63 +46,45 @@ class StartupViewModel extends ReactiveViewModel { } } - - // Navigation - Future replaceWithFailure() async => await _navigationService - .replaceWithFailureView(label: 'Check you internet connection'); + Future replaceWithFailure() async => await _navigationService.replaceWithFailureView( + label: 'Check you internet connection', + onTap: () async => await _getProfileStatus()); Future replaceWithOnboarding() async => await _navigationService.replaceWithOnboardingView(); - Future replaceWithHome() async => await _navigationService.replaceWithHomeView(); // Remote api calls - // In-app update - Future _inAppUpdate() async { - if (await _statusChecker.checkConnection()) { - await _inAppUpdateService.checkForUpdate(); - } - } - // Get profile status Future _getProfileStatus() async { + final bool? profileCompleted = _user?.profileCompleted; Map response = {}; - if (_user?.profileCompleted == null) { + if (profileCompleted == null) { if (await _statusChecker.checkConnection()) { - print('PATH: 1'); response = await _apiService.getProfileStatus(_user); } else { - print('PATH: 2'); - - await Future.delayed(kDuration); await replaceWithFailure(); } - } else if (!(_user?.profileCompleted ?? false)) { - print('PATH: 3'); - + } else if (!profileCompleted) { response = {'data': false, 'status': ResponseStatus.success}; } else { - print('PATH: 4'); - response = {'data': true, 'status': ResponseStatus.success}; } if (response['status'] == ResponseStatus.success && !response['data']) { - print('PATH: 5'); - await replaceWithOnboarding(); } else if (response['status'] == ResponseStatus.success && response['data']) { - print('PATH: 6'); - await saveProfileStatus(response['data']); await _getProfileData(); await replaceWithHome(); + } else { + await replaceWithFailure(); } } @@ -118,26 +94,24 @@ class StartupViewModel extends ReactiveViewModel { // Get profile data Future _getProfileData() async { - if (!(_user?.userInfoLoaded ?? false)) { + bool? infoLoaded = _user?.userInfoLoaded ?? false; + bool? profileCompleted = _user?.profileCompleted ?? false; + + if (!infoLoaded) { Map response = {}; - if (_user?.profileCompleted != null && - (_user?.profileCompleted ?? false)) { - if (await _statusChecker.checkConnection()) { - response = await _apiService.getProfileData(_user?.userId); + if (profileCompleted) { + response = await _apiService.getProfileData(_user?.userId); - if (response['status'] == ResponseStatus.success) { - User user = response['data'] as User; + if (response['status'] == ResponseStatus.success) { + User user = response['data'] as User; - await _authenticationService.saveUserData(user); + await _authenticationService.saveUserData(user); - String image = - await _imageDownloaderService.downloader(user.profilePicture); + String image = + await _imageDownloaderService.downloader(user.profilePicture); - await _authenticationService.saveProfilePicture(image); - } - } else { - await replaceWithFailure(); + await _authenticationService.saveProfilePicture(image); } } } diff --git a/lib/ui/views/support/support_view.dart b/lib/ui/views/support/support_view.dart index f1f865e..d7709a8 100644 --- a/lib/ui/views/support/support_view.dart +++ b/lib/ui/views/support/support_view.dart @@ -53,7 +53,7 @@ class SupportView extends StackedView { Widget _buildAppbar(SupportViewModel viewModel) => SmallAppBar( title: 'Need Help?', showBackButton: true, - onTap: viewModel.pop, + onPop: viewModel.pop, ); Widget _buildContentWrapper(SupportViewModel viewModel) => diff --git a/lib/ui/views/telegram_support/telegram_support_view.dart b/lib/ui/views/telegram_support/telegram_support_view.dart index 364fd6e..ffe63b7 100644 --- a/lib/ui/views/telegram_support/telegram_support_view.dart +++ b/lib/ui/views/telegram_support/telegram_support_view.dart @@ -49,7 +49,7 @@ class TelegramSupportView extends StackedView { ); Widget _buildAppbar(TelegramSupportViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, title: 'Telegram Support', ); diff --git a/lib/ui/views/terms_and_conditions/terms_and_conditions_view.dart b/lib/ui/views/terms_and_conditions/terms_and_conditions_view.dart index 35afddc..a739a0c 100644 --- a/lib/ui/views/terms_and_conditions/terms_and_conditions_view.dart +++ b/lib/ui/views/terms_and_conditions/terms_and_conditions_view.dart @@ -57,7 +57,7 @@ class TermsAndConditionsView extends StackedView { ); Widget _buildAppbar(TermsAndConditionsViewModel viewModel) => SmallAppBar( - onTap: viewModel.pop, + onPop: viewModel.pop, showBackButton: true, title: 'Terms and Conditions', ); diff --git a/lib/ui/views/assessment/screens/assessment_loading_screen.dart b/lib/ui/widgets/assessment_loading_screen.dart similarity index 91% rename from lib/ui/views/assessment/screens/assessment_loading_screen.dart rename to lib/ui/widgets/assessment_loading_screen.dart index 1c1503a..7015704 100644 --- a/lib/ui/views/assessment/screens/assessment_loading_screen.dart +++ b/lib/ui/widgets/assessment_loading_screen.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart'; +import 'package:yimaru_app/ui/widgets/refresh_button.dart'; -import '../../../common/app_colors.dart'; -import '../../../widgets/large_app_bar.dart'; -import '../../../widgets/refresh_button.dart'; +import '../common/app_colors.dart'; +import 'large_app_bar.dart'; class AssessmentLoadingScreen extends StatelessWidget { final bool isEmpty; diff --git a/lib/ui/widgets/learn_lesson_tile.dart b/lib/ui/widgets/learn_lesson_tile.dart index ace314c..b9d46f9 100644 --- a/lib/ui/widgets/learn_lesson_tile.dart +++ b/lib/ui/widgets/learn_lesson_tile.dart @@ -55,7 +55,7 @@ class LearnLessonTile extends ViewModelWidget { expandedCrossAxisAlignment: CrossAxisAlignment.start, collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1), childrenPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15), - // enabled: status != ProgressStatuses.pending, + // enabled: (lesson.access?.isAccessible ?? false), // backgroundColor: ProgressStatuses.pending == status // ? kcPrimaryColor.withOpacity(0.1) // : kcGreen.withOpacity(0.1), diff --git a/lib/ui/widgets/learn_module_tile.dart b/lib/ui/widgets/learn_module_tile.dart index 1aa28c8..ee08d6c 100644 --- a/lib/ui/widgets/learn_module_tile.dart +++ b/lib/ui/widgets/learn_module_tile.dart @@ -56,9 +56,7 @@ class LearnModuleTile extends ViewModelWidget { {required BuildContext context, required LearnModuleViewModel viewModel}) => ExpansionTile( - enabled: true, textColor: kcDarkGrey, - showTrailingIcon: true, initiallyExpanded: true, subtitle: _buildContent(), title: _buildTitleWrapper(), @@ -69,13 +67,12 @@ class LearnModuleTile extends ViewModelWidget { shape: Border.all(color: kcTransparent), expandedAlignment: Alignment.centerLeft, collapsedBackgroundColor: kcBackgroundColor, + enabled: (module.access?.isAccessible ?? false), controlAffinity: ListTileControlAffinity.trailing, expandedCrossAxisAlignment: CrossAxisAlignment.start, tilePadding: const EdgeInsets.symmetric(horizontal: 15), childrenPadding: const EdgeInsets.fromLTRB(70, 0, 15, 15), - // enabled:(module.access?.isAccessible ?? false) , - // showTrailingIcon: status != ProgressStatuses.pending ? true : false, - //initiallyExpanded: status == ProgressStatuses.started ? true : false, + showTrailingIcon: (module.access?.isAccessible ?? false) ? true : false, children: _buildExpansionTileChildren(context: context, viewModel: viewModel), ); @@ -209,9 +206,9 @@ class LearnModuleTile extends ViewModelWidget { onTap: viewModel.pop, ); - // Widget _buildContainerShaderState() => status == ProgressStatuses.pending - // ? _buildContainerShaderWrapper() - // : Container(); + Widget _buildContainerShaderState() => !(module.access?.isAccessible ?? false) + ? _buildContainerShaderWrapper() + : Container(); Widget _buildContainerShaderWrapper() => Positioned.fill( child: _buildContainerShader(), diff --git a/lib/ui/widgets/learn_practice_tip_section.dart b/lib/ui/widgets/learn_practice_tip_section.dart index e76fb7e..7e8a5e6 100644 --- a/lib/ui/widgets/learn_practice_tip_section.dart +++ b/lib/ui/widgets/learn_practice_tip_section.dart @@ -4,7 +4,6 @@ import 'package:yimaru_app/ui/common/app_colors.dart'; import 'package:yimaru_app/ui/common/ui_helpers.dart'; import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart'; - class LearnPracticeTipSection extends ViewModelWidget { const LearnPracticeTipSection({super.key}); diff --git a/lib/ui/widgets/no_data_indicator.dart b/lib/ui/widgets/no_data_indicator.dart new file mode 100644 index 0000000..8db2b6d --- /dev/null +++ b/lib/ui/widgets/no_data_indicator.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:yimaru_app/ui/common/app_colors.dart'; + +import '../common/ui_helpers.dart'; + +class NoDataIndicator extends StatelessWidget { + final String title; + final GestureTapCallback? onTap; + + const NoDataIndicator({super.key, this.onTap, required this.title}); + + @override + Widget build(BuildContext context) => _buildColumn(); + + Widget _buildColumn() => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: _buildColumnChildren(), + ); + + List _buildColumnChildren() => [ + _buildIconWrapper(), + verticalSpaceMedium, + _buildTitle(), + + ]; + + Widget _buildTitle() => Text( + title, + style: style16P600, + ); + + Widget _buildIconWrapper() => GestureDetector( + onTap: onTap, + child: _buildIcon(), + ); + + Widget _buildIcon() => const Icon( + Icons.replay, + size: 75, + color: kcPrimaryColor, + ); + + +} diff --git a/lib/ui/widgets/small_app_bar.dart b/lib/ui/widgets/small_app_bar.dart index cc5b160..7abce20 100644 --- a/lib/ui/widgets/small_app_bar.dart +++ b/lib/ui/widgets/small_app_bar.dart @@ -5,10 +5,10 @@ import 'package:yimaru_app/ui/widgets/custom_back_button.dart'; class SmallAppBar extends StatelessWidget { final String? title; final bool showBackButton; - final GestureTapCallback? onTap; + final GestureTapCallback? onPop; const SmallAppBar( - {super.key, this.onTap, this.title, required this.showBackButton}); + {super.key, this.onPop, this.title, required this.showBackButton}); @override Widget build(BuildContext context) => _buildAppBar(); @@ -28,7 +28,7 @@ class SmallAppBar extends StatelessWidget { child: _buildBackButton(), ); - Widget _buildBackButton() => CustomBackButton(onTap: onTap); + Widget _buildBackButton() => CustomBackButton(onTap: onPop); Widget _buildTitleWrapper() => Align( alignment: Alignment.center, diff --git a/pubspec.yaml b/pubspec.yaml index 8d453df..1303e20 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: yimaru_app -version: 0.1.8+10 +version: 0.1.9+11 publish_to: 'none' description: A new Flutter project. diff --git a/test/helpers/test_helpers.mocks.dart b/test/helpers/test_helpers.mocks.dart index 9202a62..bac2620 100644 --- a/test/helpers/test_helpers.mocks.dart +++ b/test/helpers/test_helpers.mocks.dart @@ -8,48 +8,49 @@ import 'dart:ui' as _i10; import 'package:audioplayers/audioplayers.dart' as _i4; import 'package:dio/dio.dart' as _i2; -import 'package:firebase_messaging/firebase_messaging.dart' as _i39; +import 'package:firebase_messaging/firebase_messaging.dart' as _i40; import 'package:flutter/material.dart' as _i8; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i7; -import 'package:permission_handler/permission_handler.dart' as _i34; +import 'package:permission_handler/permission_handler.dart' as _i35; import 'package:stacked_services/stacked_services.dart' as _i6; import 'package:waveform_recorder/waveform_recorder.dart' as _i5; -import 'package:yimaru_app/models/category.dart' as _i21; -import 'package:yimaru_app/models/course.dart' as _i26; -import 'package:yimaru_app/models/course_detail.dart' as _i42; -import 'package:yimaru_app/models/course_lesson.dart' as _i24; -import 'package:yimaru_app/models/course_progress.dart' as _i23; -import 'package:yimaru_app/models/learn_course.dart' as _i16; -import 'package:yimaru_app/models/learn_lesson.dart' as _i19; -import 'package:yimaru_app/models/learn_module.dart' as _i18; -import 'package:yimaru_app/models/learn_practice.dart' as _i17; -import 'package:yimaru_app/models/learn_program.dart' as _i15; -import 'package:yimaru_app/models/learn_question.dart' as _i20; -import 'package:yimaru_app/models/lesson.dart' as _i30; -import 'package:yimaru_app/models/level.dart' as _i27; -import 'package:yimaru_app/models/module.dart' as _i28; -import 'package:yimaru_app/models/practice.dart' as _i25; -import 'package:yimaru_app/models/question.dart' as _i14; -import 'package:yimaru_app/models/subcategory.dart' as _i22; -import 'package:yimaru_app/models/submodule.dart' as _i29; +import 'package:yimaru_app/models/assessment.dart' as _i14; +import 'package:yimaru_app/models/assessment_question.dart' as _i15; +import 'package:yimaru_app/models/category.dart' as _i22; +import 'package:yimaru_app/models/course.dart' as _i27; +import 'package:yimaru_app/models/course_detail.dart' as _i43; +import 'package:yimaru_app/models/course_lesson.dart' as _i25; +import 'package:yimaru_app/models/course_progress.dart' as _i24; +import 'package:yimaru_app/models/learn_course.dart' as _i17; +import 'package:yimaru_app/models/learn_lesson.dart' as _i20; +import 'package:yimaru_app/models/learn_module.dart' as _i19; +import 'package:yimaru_app/models/learn_practice.dart' as _i18; +import 'package:yimaru_app/models/learn_program.dart' as _i16; +import 'package:yimaru_app/models/learn_question.dart' as _i21; +import 'package:yimaru_app/models/lesson.dart' as _i31; +import 'package:yimaru_app/models/level.dart' as _i28; +import 'package:yimaru_app/models/module.dart' as _i29; +import 'package:yimaru_app/models/practice.dart' as _i26; +import 'package:yimaru_app/models/subcategory.dart' as _i23; +import 'package:yimaru_app/models/submodule.dart' as _i30; import 'package:yimaru_app/models/user.dart' as _i12; import 'package:yimaru_app/services/api_service.dart' as _i13; -import 'package:yimaru_app/services/audio_player_service.dart' as _i43; +import 'package:yimaru_app/services/audio_player_service.dart' as _i44; import 'package:yimaru_app/services/authentication_service.dart' as _i11; -import 'package:yimaru_app/services/course_service.dart' as _i41; -import 'package:yimaru_app/services/dio_service.dart' as _i31; -import 'package:yimaru_app/services/google_auth_service.dart' as _i36; -import 'package:yimaru_app/services/image_downloader_service.dart' as _i37; -import 'package:yimaru_app/services/image_picker_service.dart' as _i35; -import 'package:yimaru_app/services/in_app_update_service.dart' as _i46; -import 'package:yimaru_app/services/notification_service.dart' as _i38; -import 'package:yimaru_app/services/permission_handler_service.dart' as _i33; +import 'package:yimaru_app/services/course_service.dart' as _i42; +import 'package:yimaru_app/services/dio_service.dart' as _i32; +import 'package:yimaru_app/services/google_auth_service.dart' as _i37; +import 'package:yimaru_app/services/image_downloader_service.dart' as _i38; +import 'package:yimaru_app/services/image_picker_service.dart' as _i36; +import 'package:yimaru_app/services/in_app_update_service.dart' as _i47; +import 'package:yimaru_app/services/notification_service.dart' as _i39; +import 'package:yimaru_app/services/permission_handler_service.dart' as _i34; import 'package:yimaru_app/services/secure_storage_service.dart' as _i3; -import 'package:yimaru_app/services/smart_auth_service.dart' as _i40; -import 'package:yimaru_app/services/status_checker_service.dart' as _i32; -import 'package:yimaru_app/services/voice_recorder_service.dart' as _i44; -import 'package:yimaru_app/ui/common/enmus.dart' as _i45; +import 'package:yimaru_app/services/smart_auth_service.dart' as _i41; +import 'package:yimaru_app/services/status_checker_service.dart' as _i33; +import 'package:yimaru_app/services/voice_recorder_service.dart' as _i45; +import 'package:yimaru_app/ui/common/enmus.dart' as _i46; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -1125,168 +1126,183 @@ class MockApiService extends _i1.Mock implements _i13.ApiService { ) as _i9.Future>); @override - _i9.Future> getAssessments() => (super.noSuchMethod( + _i9.Future> getAssessments() => (super.noSuchMethod( Invocation.method( #getAssessments, [], ), - returnValue: _i9.Future>.value(<_i14.Question>[]), + returnValue: + _i9.Future>.value(<_i14.Assessment>[]), returnValueForMissingStub: - _i9.Future>.value(<_i14.Question>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i14.Assessment>[]), + ) as _i9.Future>); @override - _i9.Future> getLearnPrograms() => (super.noSuchMethod( + _i9.Future> getAssessmentQuestions(int? id) => + (super.noSuchMethod( + Invocation.method( + #getAssessmentQuestions, + [id], + ), + returnValue: _i9.Future>.value( + <_i15.AssessmentQuestion>[]), + returnValueForMissingStub: + _i9.Future>.value( + <_i15.AssessmentQuestion>[]), + ) as _i9.Future>); + + @override + _i9.Future> getLearnPrograms() => (super.noSuchMethod( Invocation.method( #getLearnPrograms, [], ), returnValue: - _i9.Future>.value(<_i15.LearnProgram>[]), + _i9.Future>.value(<_i16.LearnProgram>[]), returnValueForMissingStub: - _i9.Future>.value(<_i15.LearnProgram>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i16.LearnProgram>[]), + ) as _i9.Future>); @override - _i9.Future> getLearnCourse(int? id) => + _i9.Future> getLearnCourse(int? id) => (super.noSuchMethod( Invocation.method( #getLearnCourse, [id], ), returnValue: - _i9.Future>.value(<_i16.LearnCourse>[]), + _i9.Future>.value(<_i17.LearnCourse>[]), returnValueForMissingStub: - _i9.Future>.value(<_i16.LearnCourse>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i17.LearnCourse>[]), + ) as _i9.Future>); @override - _i9.Future> getLearnCoursePractices(int? id) => + _i9.Future> getLearnCoursePractices(int? id) => (super.noSuchMethod( Invocation.method( #getLearnCoursePractices, [id], ), returnValue: - _i9.Future>.value(<_i17.LearnPractice>[]), + _i9.Future>.value(<_i18.LearnPractice>[]), returnValueForMissingStub: - _i9.Future>.value(<_i17.LearnPractice>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i18.LearnPractice>[]), + ) as _i9.Future>); @override - _i9.Future> getLearnModules(int? id) => + _i9.Future> getLearnModules(int? id) => (super.noSuchMethod( Invocation.method( #getLearnModules, [id], ), returnValue: - _i9.Future>.value(<_i18.LearnModule>[]), + _i9.Future>.value(<_i19.LearnModule>[]), returnValueForMissingStub: - _i9.Future>.value(<_i18.LearnModule>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i19.LearnModule>[]), + ) as _i9.Future>); @override - _i9.Future> getLearnModulePractices(int? id) => + _i9.Future> getLearnModulePractices(int? id) => (super.noSuchMethod( Invocation.method( #getLearnModulePractices, [id], ), returnValue: - _i9.Future>.value(<_i17.LearnPractice>[]), + _i9.Future>.value(<_i18.LearnPractice>[]), returnValueForMissingStub: - _i9.Future>.value(<_i17.LearnPractice>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i18.LearnPractice>[]), + ) as _i9.Future>); @override - _i9.Future> getLearnLessons(int? id) => + _i9.Future> getLearnLessons(int? id) => (super.noSuchMethod( Invocation.method( #getLearnLessons, [id], ), returnValue: - _i9.Future>.value(<_i19.LearnLesson>[]), + _i9.Future>.value(<_i20.LearnLesson>[]), returnValueForMissingStub: - _i9.Future>.value(<_i19.LearnLesson>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i20.LearnLesson>[]), + ) as _i9.Future>); @override - _i9.Future> getLearnLessonPractices(int? id) => + _i9.Future> getLearnLessonPractices(int? id) => (super.noSuchMethod( Invocation.method( #getLearnLessonPractices, [id], ), returnValue: - _i9.Future>.value(<_i17.LearnPractice>[]), + _i9.Future>.value(<_i18.LearnPractice>[]), returnValueForMissingStub: - _i9.Future>.value(<_i17.LearnPractice>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i18.LearnPractice>[]), + ) as _i9.Future>); @override - _i9.Future> getLearnQuestions(int? id) => + _i9.Future> getLearnQuestions(int? id) => (super.noSuchMethod( Invocation.method( #getLearnQuestions, [id], ), returnValue: - _i9.Future>.value(<_i20.LearnQuestion>[]), + _i9.Future>.value(<_i21.LearnQuestion>[]), returnValueForMissingStub: - _i9.Future>.value(<_i20.LearnQuestion>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i21.LearnQuestion>[]), + ) as _i9.Future>); @override - _i9.Future> getCategories() => (super.noSuchMethod( + _i9.Future> getCategories() => (super.noSuchMethod( Invocation.method( #getCategories, [], ), - returnValue: _i9.Future>.value(<_i21.Category>[]), + returnValue: _i9.Future>.value(<_i22.Category>[]), returnValueForMissingStub: - _i9.Future>.value(<_i21.Category>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i22.Category>[]), + ) as _i9.Future>); @override - _i9.Future> getSubcategories(int? id) => + _i9.Future> getSubcategories(int? id) => (super.noSuchMethod( Invocation.method( #getSubcategories, [id], ), returnValue: - _i9.Future>.value(<_i22.Subcategory>[]), + _i9.Future>.value(<_i23.Subcategory>[]), returnValueForMissingStub: - _i9.Future>.value(<_i22.Subcategory>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i23.Subcategory>[]), + ) as _i9.Future>); @override - _i9.Future> getCourseProgress(int? id) => + _i9.Future> getCourseProgress(int? id) => (super.noSuchMethod( Invocation.method( #getCourseProgress, [id], ), - returnValue: _i9.Future>.value( - <_i23.CourseProgress>[]), - returnValueForMissingStub: _i9.Future>.value( - <_i23.CourseProgress>[]), - ) as _i9.Future>); + returnValue: _i9.Future>.value( + <_i24.CourseProgress>[]), + returnValueForMissingStub: _i9.Future>.value( + <_i24.CourseProgress>[]), + ) as _i9.Future>); @override - _i9.Future> getCourseLessons(int? id) => + _i9.Future> getCourseLessons(int? id) => (super.noSuchMethod( Invocation.method( #getCourseLessons, [id], ), returnValue: - _i9.Future>.value(<_i24.CourseLesson>[]), + _i9.Future>.value(<_i25.CourseLesson>[]), returnValueForMissingStub: - _i9.Future>.value(<_i24.CourseLesson>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i25.CourseLesson>[]), + ) as _i9.Future>); @override _i9.Future> completeLesson(int? id) => @@ -1302,130 +1318,136 @@ class MockApiService extends _i1.Mock implements _i13.ApiService { ) as _i9.Future>); @override - _i9.Future> getCoursePractices(int? id) => + _i9.Future> getCoursePractices(int? id) => (super.noSuchMethod( Invocation.method( #getCoursePractices, [id], ), - returnValue: _i9.Future>.value(<_i25.Practice>[]), + returnValue: _i9.Future>.value(<_i26.Practice>[]), returnValueForMissingStub: - _i9.Future>.value(<_i25.Practice>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i26.Practice>[]), + ) as _i9.Future>); @override - _i9.Future> getCoursePracticeQuestions(int? id) => + _i9.Future> getCoursePracticeQuestions( + int? id) => (super.noSuchMethod( Invocation.method( #getCoursePracticeQuestions, [id], ), - returnValue: _i9.Future>.value(<_i14.Question>[]), + returnValue: _i9.Future>.value( + <_i15.AssessmentQuestion>[]), returnValueForMissingStub: - _i9.Future>.value(<_i14.Question>[]), - ) as _i9.Future>); + _i9.Future>.value( + <_i15.AssessmentQuestion>[]), + ) as _i9.Future>); @override - _i9.Future<_i14.Question?> getCoursePracticeQuestion(int? id) => + _i9.Future<_i15.AssessmentQuestion?> getCoursePracticeQuestion(int? id) => (super.noSuchMethod( Invocation.method( #getCoursePracticeQuestion, [id], ), - returnValue: _i9.Future<_i14.Question?>.value(), - returnValueForMissingStub: _i9.Future<_i14.Question?>.value(), - ) as _i9.Future<_i14.Question?>); + returnValue: _i9.Future<_i15.AssessmentQuestion?>.value(), + returnValueForMissingStub: _i9.Future<_i15.AssessmentQuestion?>.value(), + ) as _i9.Future<_i15.AssessmentQuestion?>); @override - _i9.Future> getLearnSubcategories() => + _i9.Future> getLearnSubcategories() => (super.noSuchMethod( Invocation.method( #getLearnSubcategories, [], ), returnValue: - _i9.Future>.value(<_i22.Subcategory>[]), + _i9.Future>.value(<_i23.Subcategory>[]), returnValueForMissingStub: - _i9.Future>.value(<_i22.Subcategory>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i23.Subcategory>[]), + ) as _i9.Future>); @override - _i9.Future> getCourses(int? id) => (super.noSuchMethod( + _i9.Future> getCourses(int? id) => (super.noSuchMethod( Invocation.method( #getCourses, [id], ), - returnValue: _i9.Future>.value(<_i26.Course>[]), + returnValue: _i9.Future>.value(<_i27.Course>[]), returnValueForMissingStub: - _i9.Future>.value(<_i26.Course>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i27.Course>[]), + ) as _i9.Future>); @override - _i9.Future> getLevels(int? id) => (super.noSuchMethod( + _i9.Future> getLevels(int? id) => (super.noSuchMethod( Invocation.method( #getLevels, [id], ), - returnValue: _i9.Future>.value(<_i27.Level>[]), + returnValue: _i9.Future>.value(<_i28.Level>[]), returnValueForMissingStub: - _i9.Future>.value(<_i27.Level>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i28.Level>[]), + ) as _i9.Future>); @override - _i9.Future> getModules(int? id) => (super.noSuchMethod( + _i9.Future> getModules(int? id) => (super.noSuchMethod( Invocation.method( #getModules, [id], ), - returnValue: _i9.Future>.value(<_i28.Module>[]), + returnValue: _i9.Future>.value(<_i29.Module>[]), returnValueForMissingStub: - _i9.Future>.value(<_i28.Module>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i29.Module>[]), + ) as _i9.Future>); @override - _i9.Future> getSubmodules(int? id) => + _i9.Future> getSubmodules(int? id) => (super.noSuchMethod( Invocation.method( #getSubmodules, [id], ), - returnValue: _i9.Future>.value(<_i29.Submodule>[]), + returnValue: _i9.Future>.value(<_i30.Submodule>[]), returnValueForMissingStub: - _i9.Future>.value(<_i29.Submodule>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i30.Submodule>[]), + ) as _i9.Future>); @override - _i9.Future> getLessons(int? id) => (super.noSuchMethod( + _i9.Future> getLessons(int? id) => (super.noSuchMethod( Invocation.method( #getLessons, [id], ), - returnValue: _i9.Future>.value(<_i30.Lesson>[]), + returnValue: _i9.Future>.value(<_i31.Lesson>[]), returnValueForMissingStub: - _i9.Future>.value(<_i30.Lesson>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i31.Lesson>[]), + ) as _i9.Future>); @override - _i9.Future> getPractices(int? id) => (super.noSuchMethod( + _i9.Future> getPractices(int? id) => (super.noSuchMethod( Invocation.method( #getPractices, [id], ), - returnValue: _i9.Future>.value(<_i25.Practice>[]), + returnValue: _i9.Future>.value(<_i26.Practice>[]), returnValueForMissingStub: - _i9.Future>.value(<_i25.Practice>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i26.Practice>[]), + ) as _i9.Future>); @override - _i9.Future> getQuestions(int? id) => (super.noSuchMethod( + _i9.Future> getQuestions(int? id) => + (super.noSuchMethod( Invocation.method( #getQuestions, [id], ), - returnValue: _i9.Future>.value(<_i14.Question>[]), + returnValue: _i9.Future>.value( + <_i15.AssessmentQuestion>[]), returnValueForMissingStub: - _i9.Future>.value(<_i14.Question>[]), - ) as _i9.Future>); + _i9.Future>.value( + <_i15.AssessmentQuestion>[]), + ) as _i9.Future>); } /// A class which mocks [SecureStorageService]. @@ -1528,7 +1550,7 @@ class MockSecureStorageService extends _i1.Mock /// A class which mocks [DioService]. /// /// See the documentation for Mockito's code generation for more information. -class MockDioService extends _i1.Mock implements _i31.DioService { +class MockDioService extends _i1.Mock implements _i32.DioService { @override _i2.Dio get dio => (super.noSuchMethod( Invocation.getter(#dio), @@ -1547,7 +1569,7 @@ class MockDioService extends _i1.Mock implements _i31.DioService { /// /// See the documentation for Mockito's code generation for more information. class MockStatusCheckerService extends _i1.Mock - implements _i32.StatusCheckerService { + implements _i33.StatusCheckerService { @override _i3.SecureStorageService get storage => (super.noSuchMethod( Invocation.getter(#storage), @@ -1583,40 +1605,40 @@ class MockStatusCheckerService extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockPermissionHandlerService extends _i1.Mock - implements _i33.PermissionHandlerService { + implements _i34.PermissionHandlerService { @override - _i9.Future<_i34.PermissionStatus> requestPermission( - _i34.Permission? requestedPermission) => + _i9.Future<_i35.PermissionStatus> requestPermission( + _i35.Permission? requestedPermission) => (super.noSuchMethod( Invocation.method( #requestPermission, [requestedPermission], ), - returnValue: _i9.Future<_i34.PermissionStatus>.value( - _i34.PermissionStatus.denied), - returnValueForMissingStub: _i9.Future<_i34.PermissionStatus>.value( - _i34.PermissionStatus.denied), - ) as _i9.Future<_i34.PermissionStatus>); + returnValue: _i9.Future<_i35.PermissionStatus>.value( + _i35.PermissionStatus.denied), + returnValueForMissingStub: _i9.Future<_i35.PermissionStatus>.value( + _i35.PermissionStatus.denied), + ) as _i9.Future<_i35.PermissionStatus>); @override - _i9.Future<_i34.PermissionStatus> request(_i34.Permission? permission) => + _i9.Future<_i35.PermissionStatus> request(_i35.Permission? permission) => (super.noSuchMethod( Invocation.method( #request, [permission], ), - returnValue: _i9.Future<_i34.PermissionStatus>.value( - _i34.PermissionStatus.denied), - returnValueForMissingStub: _i9.Future<_i34.PermissionStatus>.value( - _i34.PermissionStatus.denied), - ) as _i9.Future<_i34.PermissionStatus>); + returnValue: _i9.Future<_i35.PermissionStatus>.value( + _i35.PermissionStatus.denied), + returnValueForMissingStub: _i9.Future<_i35.PermissionStatus>.value( + _i35.PermissionStatus.denied), + ) as _i9.Future<_i35.PermissionStatus>); } /// A class which mocks [ImagePickerService]. /// /// See the documentation for Mockito's code generation for more information. class MockImagePickerService extends _i1.Mock - implements _i35.ImagePickerService { + implements _i36.ImagePickerService { @override _i9.Future gallery() => (super.noSuchMethod( Invocation.method( @@ -1641,7 +1663,7 @@ class MockImagePickerService extends _i1.Mock /// A class which mocks [GoogleAuthService]. /// /// See the documentation for Mockito's code generation for more information. -class MockGoogleAuthService extends _i1.Mock implements _i36.GoogleAuthService { +class MockGoogleAuthService extends _i1.Mock implements _i37.GoogleAuthService { @override int get listenersCount => (super.noSuchMethod( Invocation.getter(#listenersCount), @@ -1711,7 +1733,7 @@ class MockGoogleAuthService extends _i1.Mock implements _i36.GoogleAuthService { /// /// See the documentation for Mockito's code generation for more information. class MockImageDownloaderService extends _i1.Mock - implements _i37.ImageDownloaderService { + implements _i38.ImageDownloaderService { @override _i9.Future downloader(String? networkImage) => (super.noSuchMethod( Invocation.method( @@ -1740,7 +1762,7 @@ class MockImageDownloaderService extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockNotificationService extends _i1.Mock - implements _i38.NotificationService { + implements _i39.NotificationService { @override _i9.Future initialize() => (super.noSuchMethod( Invocation.method( @@ -1762,7 +1784,7 @@ class MockNotificationService extends _i1.Mock ) as _i9.Future); @override - _i9.Future showNotification(_i39.RemoteMessage? message) => + _i9.Future showNotification(_i40.RemoteMessage? message) => (super.noSuchMethod( Invocation.method( #showNotification, @@ -1796,7 +1818,7 @@ class MockNotificationService extends _i1.Mock /// A class which mocks [SmartAuthService]. /// /// See the documentation for Mockito's code generation for more information. -class MockSmartAuthService extends _i1.Mock implements _i40.SmartAuthService { +class MockSmartAuthService extends _i1.Mock implements _i41.SmartAuthService { @override bool get listenForMultipleSms => (super.noSuchMethod( Invocation.getter(#listenForMultipleSms), @@ -1828,26 +1850,26 @@ class MockSmartAuthService extends _i1.Mock implements _i40.SmartAuthService { /// A class which mocks [CourseService]. /// /// See the documentation for Mockito's code generation for more information. -class MockCourseService extends _i1.Mock implements _i41.CourseService { +class MockCourseService extends _i1.Mock implements _i42.CourseService { @override - _i9.Future> getCoursesDetail(int? id) => + _i9.Future> getCoursesDetail(int? id) => (super.noSuchMethod( Invocation.method( #getCoursesDetail, [id], ), returnValue: - _i9.Future>.value(<_i42.CourseDetail>[]), + _i9.Future>.value(<_i43.CourseDetail>[]), returnValueForMissingStub: - _i9.Future>.value(<_i42.CourseDetail>[]), - ) as _i9.Future>); + _i9.Future>.value(<_i43.CourseDetail>[]), + ) as _i9.Future>); } /// A class which mocks [AudioPlayerService]. /// /// See the documentation for Mockito's code generation for more information. class MockAudioPlayerService extends _i1.Mock - implements _i43.AudioPlayerService { + implements _i44.AudioPlayerService { @override _i4.AudioPlayer get player => (super.noSuchMethod( Invocation.getter(#player), @@ -1971,13 +1993,13 @@ class MockAudioPlayerService extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockVoiceRecorderService extends _i1.Mock - implements _i44.VoiceRecorderService { + implements _i45.VoiceRecorderService { @override - _i45.VoiceRecordingState get recordingState => (super.noSuchMethod( + _i46.VoiceRecordingState get recordingState => (super.noSuchMethod( Invocation.getter(#recordingState), - returnValue: _i45.VoiceRecordingState.pending, - returnValueForMissingStub: _i45.VoiceRecordingState.pending, - ) as _i45.VoiceRecordingState); + returnValue: _i46.VoiceRecordingState.pending, + returnValueForMissingStub: _i46.VoiceRecordingState.pending, + ) as _i46.VoiceRecordingState); @override _i5.WaveformRecorderController get waveController => (super.noSuchMethod( @@ -2078,7 +2100,7 @@ class MockVoiceRecorderService extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockInAppUpdateService extends _i1.Mock - implements _i46.InAppUpdateService { + implements _i47.InAppUpdateService { @override _i9.Future getBatteryLevel() => (super.noSuchMethod( Invocation.method( diff --git a/test/viewmodels/assessment_question_viewmodel_test.dart b/test/viewmodels/assessment_question_viewmodel_test.dart new file mode 100644 index 0000000..c682b30 --- /dev/null +++ b/test/viewmodels/assessment_question_viewmodel_test.dart @@ -0,0 +1,11 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:yimaru_app/app/app.locator.dart'; + +import '../helpers/test_helpers.dart'; + +void main() { + group('AssessmentQuestionViewModel Tests -', () { + setUp(() => registerServices()); + tearDown(() => locator.reset()); + }); +}