Compare commits
4 Commits
45602d0da7
...
2024dd3b6d
| Author | SHA1 | Date | |
|---|---|---|---|
| 2024dd3b6d | |||
| 54356812d6 | |||
| 7a54d0c4c8 | |||
| 59ffca2155 |
|
|
@ -21,7 +21,7 @@
|
|||
"create_password": "Create password",
|
||||
"confirm_password": "Confirm password",
|
||||
"eight_character_minimum": "8 characters minimum",
|
||||
"password_math": "password match",
|
||||
"password_match": "password match",
|
||||
"sign_up_agreement": "By clicking ‘Sign Up’, you agree to our ‘Terms of Service’ and ‘Privacy Policy’",
|
||||
"terms_of_services": "Terms of Service",
|
||||
"and": "and",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'access.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class Access {
|
||||
final String? reason;
|
||||
|
|
@ -21,15 +20,43 @@ class Access {
|
|||
@JsonKey(name: 'progress_percent')
|
||||
final int? progressPercent;
|
||||
|
||||
const Access(
|
||||
{this.reason,
|
||||
this.totalCount,
|
||||
this.isCompleted,
|
||||
this.isAccessible,
|
||||
this.completedCount,
|
||||
this.progressPercent});
|
||||
@JsonKey(name: 'progress_percent_precise')
|
||||
final int? progressPercentPrecise;
|
||||
|
||||
factory Access.fromJson(Map<String, dynamic> json) => _$AccessFromJson(json);
|
||||
const Access({
|
||||
this.reason,
|
||||
this.totalCount,
|
||||
this.isCompleted,
|
||||
this.isAccessible,
|
||||
this.completedCount,
|
||||
this.progressPercent,
|
||||
this.progressPercentPrecise,
|
||||
});
|
||||
|
||||
Access copyWith({
|
||||
String? reason,
|
||||
int? totalCount,
|
||||
bool? isCompleted,
|
||||
bool? isAccessible,
|
||||
int? completedCount,
|
||||
int? progressPercent,
|
||||
int? progressPercentPrecise,
|
||||
}) {
|
||||
return Access(
|
||||
reason: reason ?? this.reason,
|
||||
totalCount: totalCount ?? this.totalCount,
|
||||
isCompleted: isCompleted ?? this.isCompleted,
|
||||
isAccessible: isAccessible ?? this.isAccessible,
|
||||
progressPercentPrecise:
|
||||
progressPercentPrecise ?? this.progressPercentPrecise,
|
||||
completedCount: completedCount ?? this.completedCount,
|
||||
progressPercent: progressPercent ?? this.progressPercent,
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
factory Access.fromJson(Map<String, dynamic> json) =>
|
||||
_$AccessFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$AccessToJson(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,8 @@ Access _$AccessFromJson(Map<String, dynamic> json) => Access(
|
|||
isAccessible: json['is_accessible'] as bool?,
|
||||
completedCount: (json['completed_count'] as num?)?.toInt(),
|
||||
progressPercent: (json['progress_percent'] as num?)?.toInt(),
|
||||
progressPercentPrecise:
|
||||
(json['progress_percent_precise'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AccessToJson(Access instance) => <String, dynamic>{
|
||||
|
|
@ -22,4 +24,5 @@ Map<String, dynamic> _$AccessToJson(Access instance) => <String, dynamic>{
|
|||
'is_accessible': instance.isAccessible,
|
||||
'completed_count': instance.completedCount,
|
||||
'progress_percent': instance.progressPercent,
|
||||
'progress_percent_precise': instance.progressPercentPrecise,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,42 +1,30 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:yimaru_app/models/module_progress.dart';
|
||||
|
||||
import 'access.dart';
|
||||
|
||||
part 'course_progress.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class CourseProgress {
|
||||
final String? level;
|
||||
final int? id;
|
||||
|
||||
final String? title;
|
||||
final String? name;
|
||||
|
||||
final String? description;
|
||||
final Access? access;
|
||||
|
||||
@JsonKey(name: 'is_locked')
|
||||
final bool? isLocked;
|
||||
final List<ModuleProgress>? modules;
|
||||
|
||||
@JsonKey(name: 'sub_course_id')
|
||||
final int? courseId;
|
||||
|
||||
@JsonKey(name: 'display_order')
|
||||
final int? displayOrder;
|
||||
@JsonKey(name: 'program_id')
|
||||
final int? programId;
|
||||
|
||||
@JsonKey(name: 'progress_status')
|
||||
final String? progressStatus;
|
||||
|
||||
@JsonKey(name: 'progress_percentage')
|
||||
final double? progressPercentage;
|
||||
|
||||
const CourseProgress(
|
||||
{this.level,
|
||||
this.title,
|
||||
this.isLocked,
|
||||
this.courseId,
|
||||
this.description,
|
||||
this.displayOrder,
|
||||
this.progressStatus,
|
||||
this.progressPercentage});
|
||||
{this.id,this.name,this.access,this.modules,this.programId});
|
||||
|
||||
factory CourseProgress.fromJson(Map<String, dynamic> json) =>
|
||||
_$CourseProgressFromJson(json);
|
||||
factory CourseProgress.fromJson(Map<String, dynamic> json) => _$CourseProgressFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$CourseProgressToJson(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,24 +8,22 @@ part of 'course_progress.dart';
|
|||
|
||||
CourseProgress _$CourseProgressFromJson(Map<String, dynamic> json) =>
|
||||
CourseProgress(
|
||||
level: json['level'] as String?,
|
||||
title: json['title'] as String?,
|
||||
isLocked: json['is_locked'] as bool?,
|
||||
courseId: (json['sub_course_id'] as num?)?.toInt(),
|
||||
description: json['description'] as String?,
|
||||
displayOrder: (json['display_order'] as num?)?.toInt(),
|
||||
progressStatus: json['progress_status'] as String?,
|
||||
progressPercentage: (json['progress_percentage'] as num?)?.toDouble(),
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
name: json['name'] as String?,
|
||||
access: json['access'] == null
|
||||
? null
|
||||
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||
modules: (json['modules'] as List<dynamic>?)
|
||||
?.map((e) => ModuleProgress.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
programId: (json['program_id'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CourseProgressToJson(CourseProgress instance) =>
|
||||
<String, dynamic>{
|
||||
'level': instance.level,
|
||||
'title': instance.title,
|
||||
'description': instance.description,
|
||||
'is_locked': instance.isLocked,
|
||||
'sub_course_id': instance.courseId,
|
||||
'display_order': instance.displayOrder,
|
||||
'progress_status': instance.progressStatus,
|
||||
'progress_percentage': instance.progressPercentage,
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'access': instance.access,
|
||||
'modules': instance.modules,
|
||||
'program_id': instance.programId,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,16 +19,36 @@ class LearnCourse {
|
|||
@JsonKey(name: 'program_id')
|
||||
final int? programId;
|
||||
|
||||
const LearnCourse(
|
||||
{this.id,
|
||||
this.name,
|
||||
this.access,
|
||||
this.programId,
|
||||
this.sortOrder,
|
||||
this.description});
|
||||
const LearnCourse({
|
||||
this.id,
|
||||
this.name,
|
||||
this.access,
|
||||
this.programId,
|
||||
this.sortOrder,
|
||||
this.description,
|
||||
});
|
||||
|
||||
LearnCourse copyWith({
|
||||
int? id,
|
||||
String? name,
|
||||
Access? access,
|
||||
int? sortOrder,
|
||||
int? programId,
|
||||
String? description,
|
||||
|
||||
}) {
|
||||
return LearnCourse(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
access: access ?? this.access,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
programId: programId ?? this.programId,
|
||||
description: description ?? this.description,
|
||||
);
|
||||
}
|
||||
|
||||
factory LearnCourse.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnCourseFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnCourseToJson(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ import 'package:json_annotation/json_annotation.dart';
|
|||
import 'access.dart';
|
||||
|
||||
part 'learn_lesson.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LearnLesson {
|
||||
final int? id;
|
||||
|
|
@ -36,8 +35,31 @@ class LearnLesson {
|
|||
this.description,
|
||||
});
|
||||
|
||||
LearnLesson copyWith({
|
||||
int? id,
|
||||
String? title,
|
||||
int? moduleId,
|
||||
int? sortOrder,
|
||||
Access? access,
|
||||
String? videoUrl,
|
||||
String? thumbnail,
|
||||
String? description,
|
||||
|
||||
}) {
|
||||
return LearnLesson(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
access: access ?? this.access,
|
||||
videoUrl: videoUrl ?? this.videoUrl,
|
||||
moduleId: moduleId ?? this.moduleId,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
thumbnail: thumbnail ?? this.thumbnail,
|
||||
description: description ?? this.description,
|
||||
);
|
||||
}
|
||||
|
||||
factory LearnLesson.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnLessonFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnLessonToJson(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ import 'package:json_annotation/json_annotation.dart';
|
|||
import 'package:yimaru_app/models/access.dart';
|
||||
|
||||
part 'learn_module.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LearnModule {
|
||||
final int? id;
|
||||
|
|
@ -24,18 +23,43 @@ class LearnModule {
|
|||
@JsonKey(name: 'sort_order')
|
||||
final int? sortOrder;
|
||||
|
||||
const LearnModule(
|
||||
{this.id,
|
||||
this.icon,
|
||||
this.name,
|
||||
this.access,
|
||||
this.courseId,
|
||||
this.sortOrder,
|
||||
this.programId,
|
||||
this.description});
|
||||
const LearnModule({
|
||||
this.id,
|
||||
this.icon,
|
||||
this.name,
|
||||
this.access,
|
||||
this.courseId,
|
||||
this.sortOrder,
|
||||
this.programId,
|
||||
this.description,
|
||||
});
|
||||
|
||||
LearnModule copyWith({
|
||||
int? id,
|
||||
String? icon,
|
||||
String? name,
|
||||
int? courseId,
|
||||
Access? access,
|
||||
int? programId,
|
||||
int? sortOrder,
|
||||
String? description,
|
||||
|
||||
}) {
|
||||
return LearnModule(
|
||||
id: id ?? this.id,
|
||||
icon: icon ?? this.icon,
|
||||
name: name ?? this.name,
|
||||
access: access ?? this.access,
|
||||
courseId: courseId ?? this.courseId,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
programId: programId ?? this.programId,
|
||||
description: description ?? this.description,
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
factory LearnModule.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnModuleFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnModuleToJson(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ import 'package:json_annotation/json_annotation.dart';
|
|||
import 'package:yimaru_app/models/access.dart';
|
||||
|
||||
part 'learn_program.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LearnProgram {
|
||||
final int? id;
|
||||
|
|
@ -16,11 +15,32 @@ class LearnProgram {
|
|||
@JsonKey(name: 'sort_order')
|
||||
final int? sortOrder;
|
||||
|
||||
const LearnProgram(
|
||||
{this.id, this.name, this.access, this.sortOrder, this.description});
|
||||
const LearnProgram({
|
||||
this.id,
|
||||
this.name,
|
||||
this.access,
|
||||
this.sortOrder,
|
||||
this.description,
|
||||
});
|
||||
|
||||
LearnProgram copyWith({
|
||||
int? id,
|
||||
String? name,
|
||||
Access? access,
|
||||
int? sortOrder,
|
||||
String? description,
|
||||
}) {
|
||||
return LearnProgram(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
access: access ?? this.access,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
description: description ?? this.description,
|
||||
);
|
||||
}
|
||||
|
||||
factory LearnProgram.fromJson(Map<String, dynamic> json) =>
|
||||
_$LearnProgramFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LearnProgramToJson(this);
|
||||
}
|
||||
}
|
||||
25
lib/models/lesson_progress.dart
Normal file
25
lib/models/lesson_progress.dart
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import 'access.dart';
|
||||
|
||||
part 'lesson_progress.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class LessonProgress {
|
||||
final int? id;
|
||||
|
||||
final String? title;
|
||||
|
||||
final Access? access;
|
||||
|
||||
@JsonKey(name: 'module_id')
|
||||
final int? moduleId;
|
||||
|
||||
|
||||
const LessonProgress(
|
||||
{this.id,this.title,this.access,this.moduleId});
|
||||
|
||||
factory LessonProgress.fromJson(Map<String, dynamic> json) => _$LessonProgressFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LessonProgressToJson(this);
|
||||
}
|
||||
25
lib/models/lesson_progress.g.dart
Normal file
25
lib/models/lesson_progress.g.dart
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'lesson_progress.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
LessonProgress _$LessonProgressFromJson(Map<String, dynamic> json) =>
|
||||
LessonProgress(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
title: json['title'] as String?,
|
||||
access: json['access'] == null
|
||||
? null
|
||||
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||
moduleId: (json['module_id'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LessonProgressToJson(LessonProgress instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'title': instance.title,
|
||||
'access': instance.access,
|
||||
'module_id': instance.moduleId,
|
||||
};
|
||||
36
lib/models/module_progress.dart
Normal file
36
lib/models/module_progress.dart
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:yimaru_app/models/lesson_progress.dart';
|
||||
|
||||
import 'access.dart';
|
||||
|
||||
part 'module_progress.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class ModuleProgress {
|
||||
final int? id;
|
||||
|
||||
final String? name;
|
||||
|
||||
final Access? access;
|
||||
|
||||
final List<LessonProgress>? lessons;
|
||||
|
||||
@JsonKey(name: 'course_id')
|
||||
final int? courseId;
|
||||
|
||||
@JsonKey(name: 'program_id')
|
||||
final int? programId;
|
||||
|
||||
const ModuleProgress(
|
||||
{this.id,
|
||||
this.name,
|
||||
this.access,
|
||||
this.lessons,
|
||||
this.courseId,
|
||||
this.programId});
|
||||
|
||||
factory ModuleProgress.fromJson(Map<String, dynamic> json) =>
|
||||
_$ModuleProgressFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$ModuleProgressToJson(this);
|
||||
}
|
||||
31
lib/models/module_progress.g.dart
Normal file
31
lib/models/module_progress.g.dart
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'module_progress.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ModuleProgress _$ModuleProgressFromJson(Map<String, dynamic> json) =>
|
||||
ModuleProgress(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
name: json['name'] as String?,
|
||||
access: json['access'] == null
|
||||
? null
|
||||
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||
lessons: (json['lessons'] as List<dynamic>?)
|
||||
?.map((e) => LessonProgress.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
courseId: (json['course_id'] as num?)?.toInt(),
|
||||
programId: (json['program_id'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ModuleProgressToJson(ModuleProgress instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'access': instance.access,
|
||||
'lessons': instance.lessons,
|
||||
'course_id': instance.courseId,
|
||||
'program_id': instance.programId,
|
||||
};
|
||||
24
lib/models/progress_summary.dart
Normal file
24
lib/models/progress_summary.dart
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:yimaru_app/models/course_progress.dart';
|
||||
|
||||
import 'access.dart';
|
||||
|
||||
part 'progress_summary.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class ProgressSummary {
|
||||
final int? id;
|
||||
|
||||
final String? name;
|
||||
|
||||
final Access? access;
|
||||
|
||||
final List<CourseProgress>? courses;
|
||||
|
||||
const ProgressSummary(
|
||||
{this.id,this.name,this.access,this.courses});
|
||||
|
||||
factory ProgressSummary.fromJson(Map<String, dynamic> json) => _$ProgressSummaryFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$ProgressSummaryToJson(this);
|
||||
}
|
||||
27
lib/models/progress_summary.g.dart
Normal file
27
lib/models/progress_summary.g.dart
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'progress_summary.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ProgressSummary _$ProgressSummaryFromJson(Map<String, dynamic> json) =>
|
||||
ProgressSummary(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
name: json['name'] as String?,
|
||||
access: json['access'] == null
|
||||
? null
|
||||
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||
courses: (json['courses'] as List<dynamic>?)
|
||||
?.map((e) => CourseProgress.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ProgressSummaryToJson(ProgressSummary instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'access': instance.access,
|
||||
'courses': instance.courses,
|
||||
};
|
||||
18
lib/models/refresh_object.dart
Normal file
18
lib/models/refresh_object.dart
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'refresh_object.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class RefreshObject {
|
||||
final String? url;
|
||||
|
||||
@JsonKey(name: 'object_key')
|
||||
final String? objectKey;
|
||||
|
||||
const RefreshObject({this.url, this.objectKey});
|
||||
|
||||
factory RefreshObject.fromJson(Map<String, dynamic> json) =>
|
||||
_$RefreshObjectFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$RefreshObjectToJson(this);
|
||||
}
|
||||
19
lib/models/refresh_object.g.dart
Normal file
19
lib/models/refresh_object.g.dart
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'refresh_object.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
RefreshObject _$RefreshObjectFromJson(Map<String, dynamic> json) =>
|
||||
RefreshObject(
|
||||
url: json['url'] as String?,
|
||||
objectKey: json['object_key'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$RefreshObjectToJson(RefreshObject instance) =>
|
||||
<String, dynamic>{
|
||||
'url': instance.url,
|
||||
'object_key': instance.objectKey,
|
||||
};
|
||||
|
|
@ -6,6 +6,7 @@ import 'package:yimaru_app/models/learn_program.dart';
|
|||
import 'package:yimaru_app/models/assessment_question.dart';
|
||||
import 'package:yimaru_app/models/course_catalog.dart';
|
||||
import 'package:yimaru_app/models/course_lesson.dart';
|
||||
import 'package:yimaru_app/models/refresh_object.dart';
|
||||
import 'package:yimaru_app/models/user.dart';
|
||||
import 'package:yimaru_app/services/dio_service.dart';
|
||||
import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||
|
|
@ -20,6 +21,7 @@ import '../models/learn_question.dart';
|
|||
import '../models/learn_subscription.dart';
|
||||
import '../models/assessment.dart';
|
||||
import '../models/learn_subscription_request.dart';
|
||||
import '../models/progress_summary.dart';
|
||||
import '../ui/common/enmus.dart';
|
||||
|
||||
class ApiService {
|
||||
|
|
@ -621,6 +623,34 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// Complete profile
|
||||
Future<Map<String, dynamic>> refreshObject(Map<String, dynamic> data) async {
|
||||
try {
|
||||
Response response = await _service.dio.post(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFilesUrl/$kRefreshUrl',
|
||||
data: data,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'status': ResponseStatus.success,
|
||||
'message': 'Operation successful',
|
||||
'data': RefreshObject.fromJson(response.data['data'])
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': 'Unknown Error Occurred'
|
||||
};
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': e.response?.data.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Learn learn programs
|
||||
Future<List<LearnProgram>> getLearnPrograms() async {
|
||||
try {
|
||||
|
|
@ -841,6 +871,30 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// Get progress summary
|
||||
Future<List<ProgressSummary>> getProgressSummary() async {
|
||||
try {
|
||||
List<ProgressSummary> summaries = [];
|
||||
|
||||
final Response response = await _service.dio
|
||||
.get('$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kLmsUrl/$kProgressSummary');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['programs'] as List;
|
||||
summaries = decodedData.map(
|
||||
(e) {
|
||||
return ProgressSummary.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return summaries;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Complete lesson
|
||||
Future<Map<String, dynamic>> completeLearnPractice(int id) async {
|
||||
try {
|
||||
|
|
@ -898,6 +952,32 @@ class ApiService {
|
|||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kPaymentsUrl/$kSubscribeUrl',
|
||||
data: data);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'status': ResponseStatus.success,
|
||||
'message': 'Subscription successful!',
|
||||
'data': LearnSubscriptionRequest.fromJson(response.data['data']),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': 'Unknown Error Occurred'
|
||||
};
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': e.response?.data.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Verify subscription
|
||||
Future<Map<String, dynamic>> verifySubscription(int id) async {
|
||||
try {
|
||||
Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kPaymentsUrl/$kVerifySubscriptionUrl/$id');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'message': 'Lesson completed',
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
import 'package:http/http.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/refresh_object.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
import '../models/access.dart';
|
||||
import '../models/learn_course.dart';
|
||||
import '../models/learn_lesson.dart';
|
||||
import '../models/learn_module.dart';
|
||||
import '../models/learn_program.dart';
|
||||
import '../models/progress_summary.dart';
|
||||
import 'api_service.dart';
|
||||
|
||||
class LearnService with ListenableServiceMixin {
|
||||
|
|
@ -36,6 +41,24 @@ class LearnService with ListenableServiceMixin {
|
|||
|
||||
List<LearnLesson> get lessons => _lessons;
|
||||
|
||||
// Learn progress
|
||||
List<ProgressSummary> _summaries = [];
|
||||
|
||||
List<ProgressSummary> get summaries => _summaries;
|
||||
|
||||
// Learn programs
|
||||
Future<String?> refreshObject(String url) async {
|
||||
Map<String, dynamic> data = {'reference': url};
|
||||
Map<String, dynamic> response = await _apiService.refreshObject(data);
|
||||
|
||||
if (response['status'] == ResponseStatus.success) {
|
||||
RefreshObject object = response['data'] as RefreshObject;
|
||||
|
||||
return object.url ?? '';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Learn programs
|
||||
Future<void> getLearnPrograms() async {
|
||||
_programs = await _apiService.getLearnPrograms();
|
||||
|
|
@ -63,4 +86,77 @@ class LearnService with ListenableServiceMixin {
|
|||
_lessons.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Learn progress
|
||||
Future<void> getLearnProgressSummary() async {
|
||||
final summaries = await _apiService.getProgressSummary();
|
||||
print('MY SUMMARIES: ${summaries.length}');
|
||||
|
||||
/// PROGRAM ACCESS MAP
|
||||
final Map<int, Access?> programAccessMap = {};
|
||||
|
||||
/// COURSE ACCESS MAP
|
||||
final Map<int, Access?> courseAccessMap = {};
|
||||
|
||||
/// MODULE ACCESS MAP
|
||||
final Map<int, Access?> moduleAccessMap = {};
|
||||
|
||||
/// LESSON ACCESS MAP
|
||||
final Map<int, Access?> lessonAccessMap = {};
|
||||
|
||||
// Build maps
|
||||
for (final summary in summaries) {
|
||||
if (summary.id != null) {
|
||||
programAccessMap[summary.id!] = summary.access;
|
||||
}
|
||||
|
||||
for (final course in summary.courses ?? []) {
|
||||
if (course.id != null) {
|
||||
courseAccessMap[course.id!] = course.access;
|
||||
}
|
||||
|
||||
for (final module in course.modules ?? []) {
|
||||
if (module.id != null) {
|
||||
moduleAccessMap[module.id!] = module.access;
|
||||
}
|
||||
|
||||
for (final lesson in module.lessons ?? []) {
|
||||
if (lesson.id != null) {
|
||||
lessonAccessMap[lesson.id!] = lesson.access;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// UPDATE PROGRAMS
|
||||
_programs = _programs.map((program) {
|
||||
return program.copyWith(
|
||||
access: programAccessMap[program.id] ?? program.access,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
/// UPDATE COURSES
|
||||
_courses = _courses.map((course) {
|
||||
return course.copyWith(
|
||||
access: courseAccessMap[course.id] ?? course.access,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
/// UPDATE MODULES
|
||||
_modules = _modules.map((module) {
|
||||
return module.copyWith(
|
||||
access: moduleAccessMap[module.id] ?? module.access,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
/// UPDATE LESSONS
|
||||
_lessons = _lessons.map((lesson) {
|
||||
return lesson.copyWith(
|
||||
access: lessonAccessMap[lesson.id] ?? lesson.access,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class NotificationService {
|
|||
}
|
||||
|
||||
Future<void> updateFCMToken() async {
|
||||
print('DEVICE TOKEN: ${await _messaging.getToken()}');
|
||||
// print('DEVICE TOKEN: ${await _messaging.getToken()}');
|
||||
_messaging.onTokenRefresh.listen((newToken) {
|
||||
// updateTokenOnServer(newToken);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Endpoints
|
||||
String kBaseUrl = 'https://api.yimaruacademy.com';
|
||||
|
||||
String kLmsUrl = 'lms';
|
||||
|
||||
String kAppUrl = 'app';
|
||||
|
||||
String kApiUrl = 'api';
|
||||
|
|
@ -9,6 +11,8 @@ String kUnitsUrl = 'units';
|
|||
|
||||
String kCheckUrl = 'check';
|
||||
|
||||
String kFilesUrl = 'files';
|
||||
|
||||
String kApiVersionUrl = 'v1';
|
||||
|
||||
String kLevelsUrl = 'levels';
|
||||
|
|
@ -31,13 +35,15 @@ String kCompleteUrl = 'complete';
|
|||
|
||||
String kPaymentsUrl = 'payments';
|
||||
|
||||
String kExamPrepUrl = 'exam-prep';
|
||||
|
||||
String kSubscribeUrl = 'subscribe';
|
||||
|
||||
String kPracticesUrl = 'practices';
|
||||
|
||||
String kQuestionsUrl = 'questions';
|
||||
|
||||
String kExamPrepUrl = 'exam-prep';
|
||||
String kRefreshUrl = 'refresh-url';
|
||||
|
||||
String kCoursePractice = 'by-owner';
|
||||
|
||||
|
|
@ -57,12 +63,16 @@ String kFieldOptions = 'field-options';
|
|||
|
||||
String kResetPassword = 'resetPassword';
|
||||
|
||||
String kVerifySubscriptionUrl = 'verify';
|
||||
|
||||
String kQuestionSetsUrl = 'question-sets';
|
||||
|
||||
String kRequestResetCode = 'sendResetCode';
|
||||
|
||||
String kSubcategoriesUrl = 'sub-categories';
|
||||
|
||||
String kProgressSummary = 'progress-summary';
|
||||
|
||||
String kPublishedVideos = 'videos/published';
|
||||
|
||||
String kCoursePracticeQuestions = 'questions';
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ static const Map<String,dynamic> _en = {
|
|||
"create_password": "Create password",
|
||||
"confirm_password": "Confirm password",
|
||||
"eight_character_minimum": "8 characters minimum",
|
||||
"password_math": "password match",
|
||||
"password_match": "password match",
|
||||
"sign_up_agreement": "By clicking ‘Sign Up’, you agree to our ‘Terms of Service’ and ‘Privacy Policy’",
|
||||
"terms_of_services": "Terms of Service",
|
||||
"and": "and",
|
||||
|
|
|
|||
|
|
@ -15,17 +15,10 @@ class ArifPayView extends StackedView<ArifPayViewModel> {
|
|||
|
||||
void _pop(ArifPayViewModel viewModel) => viewModel.pop;
|
||||
|
||||
Future<void> _error() async {
|
||||
// await Navigator.pushNamed(context, AppRoutes.subscriptionErrorPage);
|
||||
// Navigation.pop();
|
||||
}
|
||||
void _error(ArifPayViewModel viewModel) => viewModel.pop();
|
||||
|
||||
void _success() {
|
||||
// Navigation.navigateTo(
|
||||
// AppRoutes.subscriptionSuccessPage,
|
||||
// arguments: widget.body,
|
||||
// );
|
||||
}
|
||||
Future<void> _success(ArifPayViewModel viewModel) async =>
|
||||
await viewModel.replaceWithHome();
|
||||
|
||||
@override
|
||||
void onViewModelReady(ArifPayViewModel viewModel) async {
|
||||
|
|
@ -58,21 +51,12 @@ class ArifPayView extends StackedView<ArifPayViewModel> {
|
|||
Widget _buildBody(ArifPayViewModel viewModel) => InAppWebView(
|
||||
initialUrlRequest:
|
||||
URLRequest(url: WebUri(viewModel.request?.paymentUrl ?? '')),
|
||||
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
||||
if (url
|
||||
.toString()
|
||||
.contains("https://checkout.arifpay.net/canceled")) {
|
||||
showErrorToast('Operation was cancelled');
|
||||
// _pop();
|
||||
} else if (url.toString().contains(kSuccessUrl)) {
|
||||
_success();
|
||||
onUpdateVisitedHistory: (controller, url, androidIsReload) async {
|
||||
if (url.toString().contains(kSuccessUrl)) {
|
||||
showSuccessToast('Subscription successful, activation in progress!');
|
||||
_success(viewModel);
|
||||
} else if (url.toString().contains(kErrorUrl)) {
|
||||
showErrorToast('Operation was cancelled');
|
||||
// _pop();
|
||||
} else if (url.toString().contains("http://x.com/elonmusk/status/")) {
|
||||
_error();
|
||||
} else if (url.toString().contains(kErrorUrl)) {
|
||||
_error();
|
||||
_error(viewModel);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,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 '../../../models/learn_subscription_request.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
|
|
@ -24,6 +25,9 @@ class ArifPayViewModel extends BaseViewModel {
|
|||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> replaceWithHome() async =>
|
||||
await _navigationService.clearStackAndShow(Routes.homeView);
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn subscription
|
||||
|
|
@ -36,7 +40,8 @@ class ArifPayViewModel extends BaseViewModel {
|
|||
Map<String, dynamic> data = {
|
||||
'plan_id': 1,
|
||||
'phone': '251$phone',
|
||||
'email': 'test@gmail.com'
|
||||
'provider': 'ARIFPAY',
|
||||
'email': 'test@gmail.com',
|
||||
};
|
||||
|
||||
Map<String, dynamic> response =
|
||||
|
|
@ -48,24 +53,4 @@ class ArifPayViewModel extends BaseViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Future<void> verifyLearnSubscription(String id) async => await runBusyFuture(_verifyLearnSubscription(phone),
|
||||
// busyObject: StateObjects.learnSubscription);
|
||||
//
|
||||
// Future<void> _verifyLearnSubscription(String id) async {
|
||||
// if (await _statusChecker.checkConnection()) {
|
||||
// Map<String,dynamic> data = {
|
||||
// 'plan_id':1,
|
||||
// 'phone': '251$phone',
|
||||
// 'email':'test@gmail.com'
|
||||
// };
|
||||
//
|
||||
// Map<String, dynamic> response =
|
||||
// await _apiService.createSubscriptionRequest(data);
|
||||
//
|
||||
// if (response['status'] == ResponseStatus.success) {
|
||||
// _request = response['data'];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import '../../../app/app.locator.dart';
|
|||
import '../../../models/learn_module.dart';
|
||||
import '../../../services/learn_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/helper_functions.dart';
|
||||
|
||||
class LearnLessonViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
|
|
@ -23,6 +24,11 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
|||
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
||||
|
||||
// Learn lessons
|
||||
|
||||
final Map<int, String> _refreshedThumbnails = {};
|
||||
|
||||
Map<int, String> get refreshedThumbnails => _refreshedThumbnails;
|
||||
|
||||
List<LearnLesson> get _lessons => _learnService.lessons;
|
||||
|
||||
List<LearnLesson> get lessons => _lessons;
|
||||
|
|
@ -55,6 +61,27 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
|||
Future<void> _getLessons(int id) async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _learnService.getLearnLessons(id);
|
||||
// await refreshLessonImages(_lessons);
|
||||
}
|
||||
}
|
||||
|
||||
//Refresh image
|
||||
Future<void> refreshLessonImages(List<LearnLesson> lessons) async {
|
||||
for (final lesson in lessons) {
|
||||
final thumbnail = lesson.thumbnail;
|
||||
|
||||
if (lesson.id == null || thumbnail == null || thumbnail.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String? refreshedUrl = await _learnService.refreshObject(thumbnail);
|
||||
|
||||
if (refreshedUrl != null) {
|
||||
_refreshedThumbnails[lesson.id!] = refreshedUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getLessonImage(LearnLesson lesson) =>
|
||||
getReadableUrl(_refreshedThumbnails[lesson.id] ?? '') ?? '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ import 'package:stacked/stacked.dart';
|
|||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
import 'package:yimaru_app/models/learn_module.dart';
|
||||
import 'package:yimaru_app/models/refresh_object.dart';
|
||||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../services/learn_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/helper_functions.dart';
|
||||
|
||||
class LearnModuleViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
|
|
@ -22,6 +24,10 @@ class LearnModuleViewModel extends ReactiveViewModel {
|
|||
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
||||
|
||||
// Learn module
|
||||
final Map<int, String> _refreshedIcons = {};
|
||||
|
||||
Map<int, String> get refreshedIcons => _refreshedIcons;
|
||||
|
||||
List<LearnModule> get _modules => _learnService.modules;
|
||||
|
||||
List<LearnModule> get modules => _modules;
|
||||
|
|
@ -52,6 +58,28 @@ class LearnModuleViewModel extends ReactiveViewModel {
|
|||
Future<void> _getLearnModules(int id) async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _learnService.getLearnModules(id);
|
||||
await refreshModuleImages(_modules);
|
||||
}
|
||||
}
|
||||
|
||||
//Refresh image
|
||||
Future<void> refreshModuleImages(List<LearnModule> modules) async {
|
||||
for (final module in modules) {
|
||||
final icon = module.icon;
|
||||
|
||||
if (module.id == null || icon == null || icon.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String? refreshedUrl = await _learnService.refreshObject(icon);
|
||||
|
||||
if (refreshedUrl != null) {
|
||||
_refreshedIcons[module.id!] = refreshedUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getModuleImage(LearnModule module) =>
|
||||
getReadableUrl(_refreshedIcons[module.id] ?? '') ?? '';
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import '../../../services/audio_player_service.dart';
|
|||
import '../../../services/learn_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/helper_functions.dart';
|
||||
|
||||
class LearnPracticeViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
|
|
@ -94,10 +95,16 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
Voice? get playing => _playing;
|
||||
|
||||
// Learn practices
|
||||
|
||||
|
||||
List<LearnPractice> _practices = [];
|
||||
|
||||
List<LearnPractice> get practices => _practices;
|
||||
|
||||
final Map<int, String> _refreshedImages= {};
|
||||
|
||||
Map<int, String> get refreshedImages => _refreshedImages;
|
||||
|
||||
// Practice questions
|
||||
List<LearnQuestion> _questions = [];
|
||||
|
||||
|
|
@ -244,6 +251,7 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
);
|
||||
await playVoicePrompt(_questions[index]);
|
||||
} else {
|
||||
await completeLearnPractices();
|
||||
goTo(3);
|
||||
}
|
||||
}
|
||||
|
|
@ -260,6 +268,10 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
|
||||
// Remote api call
|
||||
|
||||
// Refresh url
|
||||
Future<String?> refreshUrl(String url) async =>
|
||||
await _learnService.refreshObject(url);
|
||||
|
||||
// Learn practice
|
||||
Future<void> getLearnPractices(
|
||||
{required int id, required LearnPractices practice}) async =>
|
||||
|
|
@ -271,14 +283,14 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
if (await _statusChecker.checkConnection()) {
|
||||
if (practice == LearnPractices.course) {
|
||||
_practices = await _apiService.getLearnCoursePractices(id);
|
||||
|
||||
// await refreshPracticeImages(_practices);
|
||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||
} else if (practice == LearnPractices.module) {
|
||||
_practices = await _apiService.getLearnModulePractices(id);
|
||||
// await refreshPracticeImages(_practices);
|
||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||
} else {
|
||||
_practices = await _apiService.getLearnLessonPractices(id);
|
||||
|
||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -296,6 +308,28 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
Future<void> _completeLearnPractices() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _apiService.completeLearnPractice(_practices.first.id ?? 0);
|
||||
await _learnService.getLearnProgressSummary();
|
||||
}
|
||||
}
|
||||
|
||||
//Refresh image
|
||||
Future<void> refreshPracticeImages(List<LearnPractice> practices) async {
|
||||
for (final practice in practices) {
|
||||
final image = practice.storyImage;
|
||||
|
||||
if (practice.id == null || image == null || image.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String? refreshedUrl = await _learnService.refreshObject(image);
|
||||
|
||||
if (refreshedUrl != null) {
|
||||
_refreshedImages[practice.id!] = refreshedUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getPracticeImage(LearnPractice practice) =>
|
||||
getReadableUrl(_refreshedImages[practice.id] ?? '') ?? '';
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ class LearnPracticeAppreciationScreen
|
|||
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
const LearnPracticeAppreciationScreen({super.key});
|
||||
|
||||
Future<void> _reset(LearnPracticeViewModel viewModel) async =>
|
||||
await viewModel.reset();
|
||||
|
||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||
await viewModel.stopRecording();
|
||||
|
|
|
|||
|
|
@ -163,10 +163,13 @@ class LearnPracticeDescriptionScreen
|
|||
Widget _buildImage(LearnPracticeViewModel viewModel) => CachedNetworkImage(
|
||||
fit: BoxFit.cover,
|
||||
width: double.maxFinite,
|
||||
|
||||
imageUrl:
|
||||
getReadableUrl(viewModel.practices.first.storyImage ?? '') ?? '',
|
||||
getReadableUrl( viewModel.practices.first.storyImage ?? '') ?? '',
|
||||
);
|
||||
|
||||
|
||||
|
||||
Widget _buildContinueButtonWrapper(LearnPracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
|
|
|
|||
|
|
@ -82,7 +82,6 @@ class LearnSubscriptionViewModel extends FormViewModel {
|
|||
Future<void> _getLearnSubscriptions() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_subscriptions = await _apiService.getLearnSubscriptions();
|
||||
_subscriptions = _subscriptions + _subscriptions + _subscriptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:yimaru_app/ui/widgets/phone_number_prefix.dart';
|
|||
|
||||
import '../../../common/app_colors.dart';
|
||||
import '../../../common/ui_helpers.dart';
|
||||
import '../../../widgets/learn_subscription_card.dart';
|
||||
import '../../../widgets/small_app_bar.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../learn_subscription_view.form.dart';
|
||||
|
|
@ -73,11 +74,15 @@ class LearnSubscriptionFormScreen
|
|||
);
|
||||
|
||||
List<Widget> _buildSheetChildren(LearnSubscriptionViewModel viewModel) => [
|
||||
verticalSpaceMedium,
|
||||
verticalSpaceSmall,
|
||||
_buildTitleWrapper(),
|
||||
verticalSpaceTiny,
|
||||
verticalSpaceMedium,
|
||||
_buildFirstCard(),
|
||||
verticalSpaceMedium,
|
||||
_buildSecondCard(),
|
||||
verticalSpaceLarge,
|
||||
_buildSubtitle(),
|
||||
verticalSpaceMassive,
|
||||
verticalSpaceMedium,
|
||||
_buildPhoneNumberWrapper(viewModel),
|
||||
if (viewModel.hasPhoneNumberValidationMessage &&
|
||||
viewModel.focusPhoneNumber)
|
||||
|
|
@ -87,10 +92,22 @@ class LearnSubscriptionFormScreen
|
|||
_buildPhoneNumberValidatorWrapper(viewModel),
|
||||
verticalSpaceLarge,
|
||||
_buildContinueButton(viewModel),
|
||||
verticalSpaceMassive,
|
||||
verticalSpaceMedium,
|
||||
_buildSecurePaymentWrapper()
|
||||
];
|
||||
|
||||
Widget _buildFirstCard() => const LearnSubscriptionCard(
|
||||
icon: Icons.school,
|
||||
title: '180+ New Lessons',
|
||||
subtitle: 'Access fresh, advanced content',
|
||||
);
|
||||
|
||||
Widget _buildSecondCard() => const LearnSubscriptionCard(
|
||||
icon: Icons.developer_board,
|
||||
title: 'Mastery Through Practice',
|
||||
subtitle: 'Practice All Lessons, Modules & Levels',
|
||||
);
|
||||
|
||||
Widget _buildTitleWrapper() => Align(
|
||||
alignment: Alignment.center,
|
||||
child: _buildTitle(),
|
||||
|
|
|
|||
|
|
@ -1,156 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:yimaru_app/models/course_detail.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class CourseTile extends StatelessWidget {
|
||||
final CourseDetail courseDetail;
|
||||
final GestureTapCallback? onCourseTap;
|
||||
final GestureTapCallback? onPracticeTap;
|
||||
|
||||
const CourseTile({
|
||||
super.key,
|
||||
this.onCourseTap,
|
||||
this.onPracticeTap,
|
||||
required this.courseDetail,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildExpansionTileCard();
|
||||
|
||||
Widget _buildExpansionTileCard() => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(
|
||||
color: kcPrimaryColor.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
child: _buildTileStack(),
|
||||
);
|
||||
|
||||
Widget _buildTileStack() => Stack(
|
||||
children: [_buildExpansionTile(), _buildTileShaderState()],
|
||||
);
|
||||
|
||||
Widget _buildExpansionTile() => ExpansionTile(
|
||||
title: _buildTitle(),
|
||||
textColor: kcDarkGrey,
|
||||
showTrailingIcon: false,
|
||||
initiallyExpanded: false,
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
childrenPadding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
enabled: courseDetail.courseProgress?.progressStatus == 'NOT_STARTED'
|
||||
? courseDetail.courseProgress?.displayOrder == 1
|
||||
? true
|
||||
: false
|
||||
: true,
|
||||
children: _buildExpansionTileChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileChildren() => [
|
||||
_buildProgressRow(),
|
||||
verticalSpaceSmall,
|
||||
_buildActionButtonWrapper(),
|
||||
verticalSpaceSmall
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
(courseDetail.course?.title == null ||
|
||||
(courseDetail.course?.title?.isEmpty ?? false))
|
||||
? 'Course ${courseDetail.course?.id}'
|
||||
: courseDetail.course?.title ?? '',
|
||||
style: style16P600,
|
||||
);
|
||||
|
||||
Widget _buildProgressRow() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildProgressChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildProgressChildren() =>
|
||||
[_buildProgressStatusWrapper(), horizontalSpaceSmall, _buildProgress()];
|
||||
|
||||
Widget _buildProgressStatusWrapper() => Expanded(
|
||||
child: _buildProgressStatus(),
|
||||
);
|
||||
|
||||
Widget _buildProgressStatus() => CustomLinearProgressIndicator(
|
||||
activeColor: kcPrimaryColor,
|
||||
backgroundColor: kcVeryLightGrey,
|
||||
progress: courseDetail.courseProgress?.progressPercentage ?? 0 / 100,
|
||||
);
|
||||
|
||||
Widget _buildProgress() => Text(
|
||||
'${courseDetail.courseProgress?.progressPercentage?.toInt() ?? 0}%',
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
Widget _buildActionButtonWrapper() => SizedBox(
|
||||
height: 40,
|
||||
width: 300,
|
||||
child: _buildActionButtons(),
|
||||
);
|
||||
|
||||
Widget _buildActionButtons() => Row(
|
||||
children: [
|
||||
_buildStartButtonWrapper(),
|
||||
horizontalSpaceSmall,
|
||||
_buildPracticeButtonWrapper()
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildStartButtonWrapper() => Expanded(
|
||||
child: _buildStartButton(),
|
||||
);
|
||||
|
||||
Widget _buildStartButton() => CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 8,
|
||||
onTap: onCourseTap,
|
||||
text: 'Start Course',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
||||
Widget _buildPracticeButtonWrapper() => Expanded(
|
||||
child: _buildPracticeButton(),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButton() => CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 8,
|
||||
text: 'Practice',
|
||||
onTap: onPracticeTap,
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
||||
Widget _buildTileShaderState() =>
|
||||
courseDetail.courseProgress?.progressStatus == 'NOT_STARTED'
|
||||
? courseDetail.courseProgress?.displayOrder == 1
|
||||
? Container()
|
||||
: _buildTileShaderWrapper()
|
||||
: Container();
|
||||
|
||||
Widget _buildTileShaderWrapper() => Positioned.fill(
|
||||
child: _buildTileShader(),
|
||||
);
|
||||
|
||||
Widget _buildTileShader() => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: kcWhite.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -59,9 +59,9 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
|||
trailing: _buildIconState(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
leading: _buildLeadingWrapper(),
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
leading: _buildLeadingWrapper(viewModel),
|
||||
enabled: (lesson.access?.isAccessible ?? false),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -78,10 +78,8 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
|||
children: _buildExpansionTileChildren(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLeadingWrapper() => MiniThumbnail(
|
||||
thumbnail:
|
||||
getReadableUrl(lesson.thumbnail ?? 'assets/images/image_1.png') ??
|
||||
'assets/images/image_1.png');
|
||||
Widget _buildLeadingWrapper(LearnLessonViewModel viewModel) => MiniThumbnail(
|
||||
thumbnail: getReadableUrl(lesson.thumbnail ?? '') ?? '');
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
lesson.title ?? '',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
|
@ -9,6 +10,7 @@ import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
|||
import 'package:yimaru_app/ui/widgets/finish_practice_sheet.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/helper_functions.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
|
|
@ -63,10 +65,10 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
subtitle: _buildContent(),
|
||||
trailing: _buildLockIcon(),
|
||||
title: _buildTitleWrapper(),
|
||||
leading: _buildIconWrapper(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
backgroundColor: kcBackgroundColor,
|
||||
leading: _buildIconWrapper(viewModel),
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
collapsedBackgroundColor: kcBackgroundColor,
|
||||
|
|
@ -87,16 +89,25 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
color: kcLightGrey,
|
||||
);
|
||||
|
||||
Widget _buildIconWrapper() => CircleAvatar(
|
||||
Widget _buildIconWrapper(LearnModuleViewModel viewModel) => CircleAvatar(
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
child: _buildIcon(),
|
||||
child: _buildIconClipper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildIcon() => const Icon(
|
||||
Icons.lightbulb_outline,
|
||||
color: kcPrimaryColor,
|
||||
Widget _buildIconClipper(LearnModuleViewModel viewModel)=> ClipRRect(
|
||||
child: _buildIcon(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildIcon(LearnModuleViewModel viewModel) =>
|
||||
CachedNetworkImage(
|
||||
width: 25,
|
||||
height: 25,
|
||||
cacheKey: viewModel.getModuleImage(module),
|
||||
imageUrl: viewModel.getModuleImage(module),
|
||||
);
|
||||
|
||||
|
||||
|
||||
Widget _buildTitleWrapper() => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: _buildTitle(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
name: yimaru_app
|
||||
version: 0.1.23+25
|
||||
version: 0.1.24+26
|
||||
publish_to: 'none'
|
||||
description: A new Flutter project.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user