Merge branch 'release/0.1.24'
-fix: Apply fix for ArifPay
This commit is contained in:
commit
2024dd3b6d
|
|
@ -21,7 +21,7 @@
|
||||||
"create_password": "Create password",
|
"create_password": "Create password",
|
||||||
"confirm_password": "Confirm password",
|
"confirm_password": "Confirm password",
|
||||||
"eight_character_minimum": "8 characters minimum",
|
"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’",
|
"sign_up_agreement": "By clicking ‘Sign Up’, you agree to our ‘Terms of Service’ and ‘Privacy Policy’",
|
||||||
"terms_of_services": "Terms of Service",
|
"terms_of_services": "Terms of Service",
|
||||||
"and": "and",
|
"and": "and",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
part 'access.g.dart';
|
part 'access.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Access {
|
class Access {
|
||||||
final String? reason;
|
final String? reason;
|
||||||
|
|
@ -21,15 +20,43 @@ class Access {
|
||||||
@JsonKey(name: 'progress_percent')
|
@JsonKey(name: 'progress_percent')
|
||||||
final int? progressPercent;
|
final int? progressPercent;
|
||||||
|
|
||||||
const Access(
|
@JsonKey(name: 'progress_percent_precise')
|
||||||
{this.reason,
|
final int? progressPercentPrecise;
|
||||||
this.totalCount,
|
|
||||||
this.isCompleted,
|
|
||||||
this.isAccessible,
|
|
||||||
this.completedCount,
|
|
||||||
this.progressPercent});
|
|
||||||
|
|
||||||
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);
|
Map<String, dynamic> toJson() => _$AccessToJson(this);
|
||||||
}
|
}
|
||||||
|
|
@ -13,6 +13,8 @@ Access _$AccessFromJson(Map<String, dynamic> json) => Access(
|
||||||
isAccessible: json['is_accessible'] as bool?,
|
isAccessible: json['is_accessible'] as bool?,
|
||||||
completedCount: (json['completed_count'] as num?)?.toInt(),
|
completedCount: (json['completed_count'] as num?)?.toInt(),
|
||||||
progressPercent: (json['progress_percent'] 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>{
|
Map<String, dynamic> _$AccessToJson(Access instance) => <String, dynamic>{
|
||||||
|
|
@ -22,4 +24,5 @@ Map<String, dynamic> _$AccessToJson(Access instance) => <String, dynamic>{
|
||||||
'is_accessible': instance.isAccessible,
|
'is_accessible': instance.isAccessible,
|
||||||
'completed_count': instance.completedCount,
|
'completed_count': instance.completedCount,
|
||||||
'progress_percent': instance.progressPercent,
|
'progress_percent': instance.progressPercent,
|
||||||
|
'progress_percent_precise': instance.progressPercentPrecise,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,30 @@
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:yimaru_app/models/module_progress.dart';
|
||||||
|
|
||||||
|
import 'access.dart';
|
||||||
|
|
||||||
part 'course_progress.g.dart';
|
part 'course_progress.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class CourseProgress {
|
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 List<ModuleProgress>? modules;
|
||||||
final bool? isLocked;
|
|
||||||
|
|
||||||
@JsonKey(name: 'sub_course_id')
|
|
||||||
final int? courseId;
|
|
||||||
|
|
||||||
@JsonKey(name: 'display_order')
|
@JsonKey(name: 'program_id')
|
||||||
final int? displayOrder;
|
final int? programId;
|
||||||
|
|
||||||
@JsonKey(name: 'progress_status')
|
|
||||||
final String? progressStatus;
|
|
||||||
|
|
||||||
@JsonKey(name: 'progress_percentage')
|
|
||||||
final double? progressPercentage;
|
|
||||||
|
|
||||||
const CourseProgress(
|
const CourseProgress(
|
||||||
{this.level,
|
{this.id,this.name,this.access,this.modules,this.programId});
|
||||||
this.title,
|
|
||||||
this.isLocked,
|
|
||||||
this.courseId,
|
|
||||||
this.description,
|
|
||||||
this.displayOrder,
|
|
||||||
this.progressStatus,
|
|
||||||
this.progressPercentage});
|
|
||||||
|
|
||||||
factory CourseProgress.fromJson(Map<String, dynamic> json) =>
|
factory CourseProgress.fromJson(Map<String, dynamic> json) => _$CourseProgressFromJson(json);
|
||||||
_$CourseProgressFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$CourseProgressToJson(this);
|
Map<String, dynamic> toJson() => _$CourseProgressToJson(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,24 +8,22 @@ part of 'course_progress.dart';
|
||||||
|
|
||||||
CourseProgress _$CourseProgressFromJson(Map<String, dynamic> json) =>
|
CourseProgress _$CourseProgressFromJson(Map<String, dynamic> json) =>
|
||||||
CourseProgress(
|
CourseProgress(
|
||||||
level: json['level'] as String?,
|
id: (json['id'] as num?)?.toInt(),
|
||||||
title: json['title'] as String?,
|
name: json['name'] as String?,
|
||||||
isLocked: json['is_locked'] as bool?,
|
access: json['access'] == null
|
||||||
courseId: (json['sub_course_id'] as num?)?.toInt(),
|
? null
|
||||||
description: json['description'] as String?,
|
: Access.fromJson(json['access'] as Map<String, dynamic>),
|
||||||
displayOrder: (json['display_order'] as num?)?.toInt(),
|
modules: (json['modules'] as List<dynamic>?)
|
||||||
progressStatus: json['progress_status'] as String?,
|
?.map((e) => ModuleProgress.fromJson(e as Map<String, dynamic>))
|
||||||
progressPercentage: (json['progress_percentage'] as num?)?.toDouble(),
|
.toList(),
|
||||||
|
programId: (json['program_id'] as num?)?.toInt(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$CourseProgressToJson(CourseProgress instance) =>
|
Map<String, dynamic> _$CourseProgressToJson(CourseProgress instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'level': instance.level,
|
'id': instance.id,
|
||||||
'title': instance.title,
|
'name': instance.name,
|
||||||
'description': instance.description,
|
'access': instance.access,
|
||||||
'is_locked': instance.isLocked,
|
'modules': instance.modules,
|
||||||
'sub_course_id': instance.courseId,
|
'program_id': instance.programId,
|
||||||
'display_order': instance.displayOrder,
|
|
||||||
'progress_status': instance.progressStatus,
|
|
||||||
'progress_percentage': instance.progressPercentage,
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,36 @@ class LearnCourse {
|
||||||
@JsonKey(name: 'program_id')
|
@JsonKey(name: 'program_id')
|
||||||
final int? programId;
|
final int? programId;
|
||||||
|
|
||||||
const LearnCourse(
|
const LearnCourse({
|
||||||
{this.id,
|
this.id,
|
||||||
this.name,
|
this.name,
|
||||||
this.access,
|
this.access,
|
||||||
this.programId,
|
this.programId,
|
||||||
this.sortOrder,
|
this.sortOrder,
|
||||||
this.description});
|
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) =>
|
factory LearnCourse.fromJson(Map<String, dynamic> json) =>
|
||||||
_$LearnCourseFromJson(json);
|
_$LearnCourseFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$LearnCourseToJson(this);
|
Map<String, dynamic> toJson() => _$LearnCourseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,6 @@ import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'access.dart';
|
import 'access.dart';
|
||||||
|
|
||||||
part 'learn_lesson.g.dart';
|
part 'learn_lesson.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class LearnLesson {
|
class LearnLesson {
|
||||||
final int? id;
|
final int? id;
|
||||||
|
|
@ -36,8 +35,31 @@ class LearnLesson {
|
||||||
this.description,
|
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) =>
|
factory LearnLesson.fromJson(Map<String, dynamic> json) =>
|
||||||
_$LearnLessonFromJson(json);
|
_$LearnLessonFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$LearnLessonToJson(this);
|
Map<String, dynamic> toJson() => _$LearnLessonToJson(this);
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,6 @@ import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:yimaru_app/models/access.dart';
|
import 'package:yimaru_app/models/access.dart';
|
||||||
|
|
||||||
part 'learn_module.g.dart';
|
part 'learn_module.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class LearnModule {
|
class LearnModule {
|
||||||
final int? id;
|
final int? id;
|
||||||
|
|
@ -24,18 +23,43 @@ class LearnModule {
|
||||||
@JsonKey(name: 'sort_order')
|
@JsonKey(name: 'sort_order')
|
||||||
final int? sortOrder;
|
final int? sortOrder;
|
||||||
|
|
||||||
const LearnModule(
|
const LearnModule({
|
||||||
{this.id,
|
this.id,
|
||||||
this.icon,
|
this.icon,
|
||||||
this.name,
|
this.name,
|
||||||
this.access,
|
this.access,
|
||||||
this.courseId,
|
this.courseId,
|
||||||
this.sortOrder,
|
this.sortOrder,
|
||||||
this.programId,
|
this.programId,
|
||||||
this.description});
|
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) =>
|
factory LearnModule.fromJson(Map<String, dynamic> json) =>
|
||||||
_$LearnModuleFromJson(json);
|
_$LearnModuleFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$LearnModuleToJson(this);
|
Map<String, dynamic> toJson() => _$LearnModuleToJson(this);
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,6 @@ import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:yimaru_app/models/access.dart';
|
import 'package:yimaru_app/models/access.dart';
|
||||||
|
|
||||||
part 'learn_program.g.dart';
|
part 'learn_program.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class LearnProgram {
|
class LearnProgram {
|
||||||
final int? id;
|
final int? id;
|
||||||
|
|
@ -16,11 +15,32 @@ class LearnProgram {
|
||||||
@JsonKey(name: 'sort_order')
|
@JsonKey(name: 'sort_order')
|
||||||
final int? sortOrder;
|
final int? sortOrder;
|
||||||
|
|
||||||
const LearnProgram(
|
const LearnProgram({
|
||||||
{this.id, this.name, this.access, this.sortOrder, this.description});
|
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) =>
|
factory LearnProgram.fromJson(Map<String, dynamic> json) =>
|
||||||
_$LearnProgramFromJson(json);
|
_$LearnProgramFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$LearnProgramToJson(this);
|
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/assessment_question.dart';
|
||||||
import 'package:yimaru_app/models/course_catalog.dart';
|
import 'package:yimaru_app/models/course_catalog.dart';
|
||||||
import 'package:yimaru_app/models/course_lesson.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/models/user.dart';
|
||||||
import 'package:yimaru_app/services/dio_service.dart';
|
import 'package:yimaru_app/services/dio_service.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_constants.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/learn_subscription.dart';
|
||||||
import '../models/assessment.dart';
|
import '../models/assessment.dart';
|
||||||
import '../models/learn_subscription_request.dart';
|
import '../models/learn_subscription_request.dart';
|
||||||
|
import '../models/progress_summary.dart';
|
||||||
import '../ui/common/enmus.dart';
|
import '../ui/common/enmus.dart';
|
||||||
|
|
||||||
class ApiService {
|
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
|
// Learn learn programs
|
||||||
Future<List<LearnProgram>> getLearnPrograms() async {
|
Future<List<LearnProgram>> getLearnPrograms() async {
|
||||||
try {
|
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
|
// Complete lesson
|
||||||
Future<Map<String, dynamic>> completeLearnPractice(int id) async {
|
Future<Map<String, dynamic>> completeLearnPractice(int id) async {
|
||||||
try {
|
try {
|
||||||
|
|
@ -898,6 +952,32 @@ class ApiService {
|
||||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kPaymentsUrl/$kSubscribeUrl',
|
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kPaymentsUrl/$kSubscribeUrl',
|
||||||
data: data);
|
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) {
|
if (response.statusCode == 200) {
|
||||||
return {
|
return {
|
||||||
'message': 'Lesson completed',
|
'message': 'Lesson completed',
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
|
import 'package:http/http.dart';
|
||||||
import 'package:stacked/stacked.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 '../app/app.locator.dart';
|
||||||
|
import '../models/access.dart';
|
||||||
import '../models/learn_course.dart';
|
import '../models/learn_course.dart';
|
||||||
import '../models/learn_lesson.dart';
|
import '../models/learn_lesson.dart';
|
||||||
import '../models/learn_module.dart';
|
import '../models/learn_module.dart';
|
||||||
import '../models/learn_program.dart';
|
import '../models/learn_program.dart';
|
||||||
|
import '../models/progress_summary.dart';
|
||||||
import 'api_service.dart';
|
import 'api_service.dart';
|
||||||
|
|
||||||
class LearnService with ListenableServiceMixin {
|
class LearnService with ListenableServiceMixin {
|
||||||
|
|
@ -36,6 +41,24 @@ class LearnService with ListenableServiceMixin {
|
||||||
|
|
||||||
List<LearnLesson> get lessons => _lessons;
|
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
|
// Learn programs
|
||||||
Future<void> getLearnPrograms() async {
|
Future<void> getLearnPrograms() async {
|
||||||
_programs = await _apiService.getLearnPrograms();
|
_programs = await _apiService.getLearnPrograms();
|
||||||
|
|
@ -63,4 +86,77 @@ class LearnService with ListenableServiceMixin {
|
||||||
_lessons.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
_lessons.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
||||||
notifyListeners();
|
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 {
|
Future<void> updateFCMToken() async {
|
||||||
print('DEVICE TOKEN: ${await _messaging.getToken()}');
|
// print('DEVICE TOKEN: ${await _messaging.getToken()}');
|
||||||
_messaging.onTokenRefresh.listen((newToken) {
|
_messaging.onTokenRefresh.listen((newToken) {
|
||||||
// updateTokenOnServer(newToken);
|
// updateTokenOnServer(newToken);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Endpoints
|
// Endpoints
|
||||||
String kBaseUrl = 'https://api.yimaruacademy.com';
|
String kBaseUrl = 'https://api.yimaruacademy.com';
|
||||||
|
|
||||||
|
String kLmsUrl = 'lms';
|
||||||
|
|
||||||
String kAppUrl = 'app';
|
String kAppUrl = 'app';
|
||||||
|
|
||||||
String kApiUrl = 'api';
|
String kApiUrl = 'api';
|
||||||
|
|
@ -9,6 +11,8 @@ String kUnitsUrl = 'units';
|
||||||
|
|
||||||
String kCheckUrl = 'check';
|
String kCheckUrl = 'check';
|
||||||
|
|
||||||
|
String kFilesUrl = 'files';
|
||||||
|
|
||||||
String kApiVersionUrl = 'v1';
|
String kApiVersionUrl = 'v1';
|
||||||
|
|
||||||
String kLevelsUrl = 'levels';
|
String kLevelsUrl = 'levels';
|
||||||
|
|
@ -31,13 +35,15 @@ String kCompleteUrl = 'complete';
|
||||||
|
|
||||||
String kPaymentsUrl = 'payments';
|
String kPaymentsUrl = 'payments';
|
||||||
|
|
||||||
|
String kExamPrepUrl = 'exam-prep';
|
||||||
|
|
||||||
String kSubscribeUrl = 'subscribe';
|
String kSubscribeUrl = 'subscribe';
|
||||||
|
|
||||||
String kPracticesUrl = 'practices';
|
String kPracticesUrl = 'practices';
|
||||||
|
|
||||||
String kQuestionsUrl = 'questions';
|
String kQuestionsUrl = 'questions';
|
||||||
|
|
||||||
String kExamPrepUrl = 'exam-prep';
|
String kRefreshUrl = 'refresh-url';
|
||||||
|
|
||||||
String kCoursePractice = 'by-owner';
|
String kCoursePractice = 'by-owner';
|
||||||
|
|
||||||
|
|
@ -57,12 +63,16 @@ String kFieldOptions = 'field-options';
|
||||||
|
|
||||||
String kResetPassword = 'resetPassword';
|
String kResetPassword = 'resetPassword';
|
||||||
|
|
||||||
|
String kVerifySubscriptionUrl = 'verify';
|
||||||
|
|
||||||
String kQuestionSetsUrl = 'question-sets';
|
String kQuestionSetsUrl = 'question-sets';
|
||||||
|
|
||||||
String kRequestResetCode = 'sendResetCode';
|
String kRequestResetCode = 'sendResetCode';
|
||||||
|
|
||||||
String kSubcategoriesUrl = 'sub-categories';
|
String kSubcategoriesUrl = 'sub-categories';
|
||||||
|
|
||||||
|
String kProgressSummary = 'progress-summary';
|
||||||
|
|
||||||
String kPublishedVideos = 'videos/published';
|
String kPublishedVideos = 'videos/published';
|
||||||
|
|
||||||
String kCoursePracticeQuestions = 'questions';
|
String kCoursePracticeQuestions = 'questions';
|
||||||
|
|
|
||||||
|
|
@ -223,7 +223,7 @@ static const Map<String,dynamic> _en = {
|
||||||
"create_password": "Create password",
|
"create_password": "Create password",
|
||||||
"confirm_password": "Confirm password",
|
"confirm_password": "Confirm password",
|
||||||
"eight_character_minimum": "8 characters minimum",
|
"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’",
|
"sign_up_agreement": "By clicking ‘Sign Up’, you agree to our ‘Terms of Service’ and ‘Privacy Policy’",
|
||||||
"terms_of_services": "Terms of Service",
|
"terms_of_services": "Terms of Service",
|
||||||
"and": "and",
|
"and": "and",
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,10 @@ class ArifPayView extends StackedView<ArifPayViewModel> {
|
||||||
|
|
||||||
void _pop(ArifPayViewModel viewModel) => viewModel.pop;
|
void _pop(ArifPayViewModel viewModel) => viewModel.pop;
|
||||||
|
|
||||||
Future<void> _error() async {
|
void _error(ArifPayViewModel viewModel) => viewModel.pop();
|
||||||
// await Navigator.pushNamed(context, AppRoutes.subscriptionErrorPage);
|
|
||||||
// Navigation.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _success() {
|
Future<void> _success(ArifPayViewModel viewModel) async =>
|
||||||
// Navigation.navigateTo(
|
await viewModel.replaceWithHome();
|
||||||
// AppRoutes.subscriptionSuccessPage,
|
|
||||||
// arguments: widget.body,
|
|
||||||
// );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onViewModelReady(ArifPayViewModel viewModel) async {
|
void onViewModelReady(ArifPayViewModel viewModel) async {
|
||||||
|
|
@ -58,21 +51,12 @@ class ArifPayView extends StackedView<ArifPayViewModel> {
|
||||||
Widget _buildBody(ArifPayViewModel viewModel) => InAppWebView(
|
Widget _buildBody(ArifPayViewModel viewModel) => InAppWebView(
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: WebUri(viewModel.request?.paymentUrl ?? '')),
|
URLRequest(url: WebUri(viewModel.request?.paymentUrl ?? '')),
|
||||||
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
onUpdateVisitedHistory: (controller, url, androidIsReload) async {
|
||||||
if (url
|
if (url.toString().contains(kSuccessUrl)) {
|
||||||
.toString()
|
showSuccessToast('Subscription successful, activation in progress!');
|
||||||
.contains("https://checkout.arifpay.net/canceled")) {
|
_success(viewModel);
|
||||||
showErrorToast('Operation was cancelled');
|
|
||||||
// _pop();
|
|
||||||
} else if (url.toString().contains(kSuccessUrl)) {
|
|
||||||
_success();
|
|
||||||
} else if (url.toString().contains(kErrorUrl)) {
|
} else if (url.toString().contains(kErrorUrl)) {
|
||||||
showErrorToast('Operation was cancelled');
|
_error(viewModel);
|
||||||
// _pop();
|
|
||||||
} else if (url.toString().contains("http://x.com/elonmusk/status/")) {
|
|
||||||
_error();
|
|
||||||
} else if (url.toString().contains(kErrorUrl)) {
|
|
||||||
_error();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:stacked_services/stacked_services.dart';
|
||||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
|
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
|
import '../../../app/app.router.dart';
|
||||||
import '../../../models/learn_subscription_request.dart';
|
import '../../../models/learn_subscription_request.dart';
|
||||||
import '../../../services/api_service.dart';
|
import '../../../services/api_service.dart';
|
||||||
import '../../../services/status_checker_service.dart';
|
import '../../../services/status_checker_service.dart';
|
||||||
|
|
@ -24,6 +25,9 @@ class ArifPayViewModel extends BaseViewModel {
|
||||||
// Navigation
|
// Navigation
|
||||||
void pop() => _navigationService.back();
|
void pop() => _navigationService.back();
|
||||||
|
|
||||||
|
Future<void> replaceWithHome() async =>
|
||||||
|
await _navigationService.clearStackAndShow(Routes.homeView);
|
||||||
|
|
||||||
// Remote api call
|
// Remote api call
|
||||||
|
|
||||||
// Learn subscription
|
// Learn subscription
|
||||||
|
|
@ -36,7 +40,8 @@ class ArifPayViewModel extends BaseViewModel {
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
'plan_id': 1,
|
'plan_id': 1,
|
||||||
'phone': '251$phone',
|
'phone': '251$phone',
|
||||||
'email': 'test@gmail.com'
|
'provider': 'ARIFPAY',
|
||||||
|
'email': 'test@gmail.com',
|
||||||
};
|
};
|
||||||
|
|
||||||
Map<String, dynamic> response =
|
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 '../../../models/learn_module.dart';
|
||||||
import '../../../services/learn_service.dart';
|
import '../../../services/learn_service.dart';
|
||||||
import '../../../services/status_checker_service.dart';
|
import '../../../services/status_checker_service.dart';
|
||||||
|
import '../../common/helper_functions.dart';
|
||||||
|
|
||||||
class LearnLessonViewModel extends ReactiveViewModel {
|
class LearnLessonViewModel extends ReactiveViewModel {
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
|
|
@ -23,6 +24,11 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
||||||
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
||||||
|
|
||||||
// Learn lessons
|
// Learn lessons
|
||||||
|
|
||||||
|
final Map<int, String> _refreshedThumbnails = {};
|
||||||
|
|
||||||
|
Map<int, String> get refreshedThumbnails => _refreshedThumbnails;
|
||||||
|
|
||||||
List<LearnLesson> get _lessons => _learnService.lessons;
|
List<LearnLesson> get _lessons => _learnService.lessons;
|
||||||
|
|
||||||
List<LearnLesson> get lessons => _lessons;
|
List<LearnLesson> get lessons => _lessons;
|
||||||
|
|
@ -55,6 +61,27 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
||||||
Future<void> _getLessons(int id) async {
|
Future<void> _getLessons(int id) async {
|
||||||
if (await _statusChecker.checkConnection()) {
|
if (await _statusChecker.checkConnection()) {
|
||||||
await _learnService.getLearnLessons(id);
|
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:stacked_services/stacked_services.dart';
|
||||||
import 'package:yimaru_app/app/app.router.dart';
|
import 'package:yimaru_app/app/app.router.dart';
|
||||||
import 'package:yimaru_app/models/learn_module.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 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||||
|
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
import '../../../services/learn_service.dart';
|
import '../../../services/learn_service.dart';
|
||||||
import '../../../services/status_checker_service.dart';
|
import '../../../services/status_checker_service.dart';
|
||||||
import '../../common/enmus.dart';
|
import '../../common/enmus.dart';
|
||||||
|
import '../../common/helper_functions.dart';
|
||||||
|
|
||||||
class LearnModuleViewModel extends ReactiveViewModel {
|
class LearnModuleViewModel extends ReactiveViewModel {
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
|
|
@ -22,6 +24,10 @@ class LearnModuleViewModel extends ReactiveViewModel {
|
||||||
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
||||||
|
|
||||||
// Learn module
|
// Learn module
|
||||||
|
final Map<int, String> _refreshedIcons = {};
|
||||||
|
|
||||||
|
Map<int, String> get refreshedIcons => _refreshedIcons;
|
||||||
|
|
||||||
List<LearnModule> get _modules => _learnService.modules;
|
List<LearnModule> get _modules => _learnService.modules;
|
||||||
|
|
||||||
List<LearnModule> get modules => _modules;
|
List<LearnModule> get modules => _modules;
|
||||||
|
|
@ -52,6 +58,28 @@ class LearnModuleViewModel extends ReactiveViewModel {
|
||||||
Future<void> _getLearnModules(int id) async {
|
Future<void> _getLearnModules(int id) async {
|
||||||
if (await _statusChecker.checkConnection()) {
|
if (await _statusChecker.checkConnection()) {
|
||||||
await _learnService.getLearnModules(id);
|
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/learn_service.dart';
|
||||||
import '../../../services/status_checker_service.dart';
|
import '../../../services/status_checker_service.dart';
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
|
import '../../common/helper_functions.dart';
|
||||||
|
|
||||||
class LearnPracticeViewModel extends ReactiveViewModel {
|
class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
|
|
@ -94,10 +95,16 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
Voice? get playing => _playing;
|
Voice? get playing => _playing;
|
||||||
|
|
||||||
// Learn practices
|
// Learn practices
|
||||||
|
|
||||||
|
|
||||||
List<LearnPractice> _practices = [];
|
List<LearnPractice> _practices = [];
|
||||||
|
|
||||||
List<LearnPractice> get practices => _practices;
|
List<LearnPractice> get practices => _practices;
|
||||||
|
|
||||||
|
final Map<int, String> _refreshedImages= {};
|
||||||
|
|
||||||
|
Map<int, String> get refreshedImages => _refreshedImages;
|
||||||
|
|
||||||
// Practice questions
|
// Practice questions
|
||||||
List<LearnQuestion> _questions = [];
|
List<LearnQuestion> _questions = [];
|
||||||
|
|
||||||
|
|
@ -244,6 +251,7 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
);
|
);
|
||||||
await playVoicePrompt(_questions[index]);
|
await playVoicePrompt(_questions[index]);
|
||||||
} else {
|
} else {
|
||||||
|
await completeLearnPractices();
|
||||||
goTo(3);
|
goTo(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -260,6 +268,10 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
// Remote api call
|
// Remote api call
|
||||||
|
|
||||||
|
// Refresh url
|
||||||
|
Future<String?> refreshUrl(String url) async =>
|
||||||
|
await _learnService.refreshObject(url);
|
||||||
|
|
||||||
// Learn practice
|
// Learn practice
|
||||||
Future<void> getLearnPractices(
|
Future<void> getLearnPractices(
|
||||||
{required int id, required LearnPractices practice}) async =>
|
{required int id, required LearnPractices practice}) async =>
|
||||||
|
|
@ -271,14 +283,14 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
if (await _statusChecker.checkConnection()) {
|
if (await _statusChecker.checkConnection()) {
|
||||||
if (practice == LearnPractices.course) {
|
if (practice == LearnPractices.course) {
|
||||||
_practices = await _apiService.getLearnCoursePractices(id);
|
_practices = await _apiService.getLearnCoursePractices(id);
|
||||||
|
// await refreshPracticeImages(_practices);
|
||||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||||
} else if (practice == LearnPractices.module) {
|
} else if (practice == LearnPractices.module) {
|
||||||
_practices = await _apiService.getLearnModulePractices(id);
|
_practices = await _apiService.getLearnModulePractices(id);
|
||||||
|
// await refreshPracticeImages(_practices);
|
||||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||||
} else {
|
} else {
|
||||||
_practices = await _apiService.getLearnLessonPractices(id);
|
_practices = await _apiService.getLearnLessonPractices(id);
|
||||||
|
|
||||||
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -296,6 +308,28 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
Future<void> _completeLearnPractices() async {
|
Future<void> _completeLearnPractices() async {
|
||||||
if (await _statusChecker.checkConnection()) {
|
if (await _statusChecker.checkConnection()) {
|
||||||
await _apiService.completeLearnPractice(_practices.first.id ?? 0);
|
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> {
|
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
const LearnPracticeAppreciationScreen({super.key});
|
const LearnPracticeAppreciationScreen({super.key});
|
||||||
|
|
||||||
Future<void> _reset(LearnPracticeViewModel viewModel) async =>
|
|
||||||
await viewModel.reset();
|
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
await viewModel.stopRecording();
|
await viewModel.stopRecording();
|
||||||
|
|
|
||||||
|
|
@ -163,10 +163,13 @@ class LearnPracticeDescriptionScreen
|
||||||
Widget _buildImage(LearnPracticeViewModel viewModel) => CachedNetworkImage(
|
Widget _buildImage(LearnPracticeViewModel viewModel) => CachedNetworkImage(
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
|
|
||||||
imageUrl:
|
imageUrl:
|
||||||
getReadableUrl(viewModel.practices.first.storyImage ?? '') ?? '',
|
getReadableUrl( viewModel.practices.first.storyImage ?? '') ?? '',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(LearnPracticeViewModel viewModel) =>
|
Widget _buildContinueButtonWrapper(LearnPracticeViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,6 @@ class LearnSubscriptionViewModel extends FormViewModel {
|
||||||
Future<void> _getLearnSubscriptions() async {
|
Future<void> _getLearnSubscriptions() async {
|
||||||
if (await _statusChecker.checkConnection()) {
|
if (await _statusChecker.checkConnection()) {
|
||||||
_subscriptions = await _apiService.getLearnSubscriptions();
|
_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/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/learn_subscription_card.dart';
|
||||||
import '../../../widgets/small_app_bar.dart';
|
import '../../../widgets/small_app_bar.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../learn_subscription_view.form.dart';
|
import '../learn_subscription_view.form.dart';
|
||||||
|
|
@ -73,11 +74,15 @@ class LearnSubscriptionFormScreen
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildSheetChildren(LearnSubscriptionViewModel viewModel) => [
|
List<Widget> _buildSheetChildren(LearnSubscriptionViewModel viewModel) => [
|
||||||
verticalSpaceMedium,
|
verticalSpaceSmall,
|
||||||
_buildTitleWrapper(),
|
_buildTitleWrapper(),
|
||||||
verticalSpaceTiny,
|
verticalSpaceMedium,
|
||||||
|
_buildFirstCard(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildSecondCard(),
|
||||||
|
verticalSpaceLarge,
|
||||||
_buildSubtitle(),
|
_buildSubtitle(),
|
||||||
verticalSpaceMassive,
|
verticalSpaceMedium,
|
||||||
_buildPhoneNumberWrapper(viewModel),
|
_buildPhoneNumberWrapper(viewModel),
|
||||||
if (viewModel.hasPhoneNumberValidationMessage &&
|
if (viewModel.hasPhoneNumberValidationMessage &&
|
||||||
viewModel.focusPhoneNumber)
|
viewModel.focusPhoneNumber)
|
||||||
|
|
@ -87,10 +92,22 @@ class LearnSubscriptionFormScreen
|
||||||
_buildPhoneNumberValidatorWrapper(viewModel),
|
_buildPhoneNumberValidatorWrapper(viewModel),
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildContinueButton(viewModel),
|
_buildContinueButton(viewModel),
|
||||||
verticalSpaceMassive,
|
verticalSpaceMedium,
|
||||||
_buildSecurePaymentWrapper()
|
_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(
|
Widget _buildTitleWrapper() => Align(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: _buildTitle(),
|
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(),
|
trailing: _buildIconState(),
|
||||||
collapsedIconColor: kcDarkGrey,
|
collapsedIconColor: kcDarkGrey,
|
||||||
collapsedTextColor: kcDarkGrey,
|
collapsedTextColor: kcDarkGrey,
|
||||||
leading: _buildLeadingWrapper(),
|
|
||||||
shape: Border.all(color: kcTransparent),
|
shape: Border.all(color: kcTransparent),
|
||||||
expandedAlignment: Alignment.centerLeft,
|
expandedAlignment: Alignment.centerLeft,
|
||||||
|
leading: _buildLeadingWrapper(viewModel),
|
||||||
enabled: (lesson.access?.isAccessible ?? false),
|
enabled: (lesson.access?.isAccessible ?? false),
|
||||||
controlAffinity: ListTileControlAffinity.trailing,
|
controlAffinity: ListTileControlAffinity.trailing,
|
||||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
@ -78,10 +78,8 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
||||||
children: _buildExpansionTileChildren(viewModel),
|
children: _buildExpansionTileChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLeadingWrapper() => MiniThumbnail(
|
Widget _buildLeadingWrapper(LearnLessonViewModel viewModel) => MiniThumbnail(
|
||||||
thumbnail:
|
thumbnail: getReadableUrl(lesson.thumbnail ?? '') ?? '');
|
||||||
getReadableUrl(lesson.thumbnail ?? 'assets/images/image_1.png') ??
|
|
||||||
'assets/images/image_1.png');
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
lesson.title ?? '',
|
lesson.title ?? '',
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.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 'package:yimaru_app/ui/widgets/finish_practice_sheet.dart';
|
||||||
|
|
||||||
import '../common/app_colors.dart';
|
import '../common/app_colors.dart';
|
||||||
|
import '../common/helper_functions.dart';
|
||||||
import '../common/ui_helpers.dart';
|
import '../common/ui_helpers.dart';
|
||||||
import 'custom_elevated_button.dart';
|
import 'custom_elevated_button.dart';
|
||||||
|
|
||||||
|
|
@ -63,10 +65,10 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
||||||
subtitle: _buildContent(),
|
subtitle: _buildContent(),
|
||||||
trailing: _buildLockIcon(),
|
trailing: _buildLockIcon(),
|
||||||
title: _buildTitleWrapper(),
|
title: _buildTitleWrapper(),
|
||||||
leading: _buildIconWrapper(),
|
|
||||||
collapsedIconColor: kcDarkGrey,
|
collapsedIconColor: kcDarkGrey,
|
||||||
collapsedTextColor: kcDarkGrey,
|
collapsedTextColor: kcDarkGrey,
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
|
leading: _buildIconWrapper(viewModel),
|
||||||
shape: Border.all(color: kcTransparent),
|
shape: Border.all(color: kcTransparent),
|
||||||
expandedAlignment: Alignment.centerLeft,
|
expandedAlignment: Alignment.centerLeft,
|
||||||
collapsedBackgroundColor: kcBackgroundColor,
|
collapsedBackgroundColor: kcBackgroundColor,
|
||||||
|
|
@ -87,16 +89,25 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
||||||
color: kcLightGrey,
|
color: kcLightGrey,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIconWrapper() => CircleAvatar(
|
Widget _buildIconWrapper(LearnModuleViewModel viewModel) => CircleAvatar(
|
||||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||||
child: _buildIcon(),
|
child: _buildIconClipper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIcon() => const Icon(
|
Widget _buildIconClipper(LearnModuleViewModel viewModel)=> ClipRRect(
|
||||||
Icons.lightbulb_outline,
|
child: _buildIcon(viewModel),
|
||||||
color: kcPrimaryColor,
|
);
|
||||||
|
|
||||||
|
Widget _buildIcon(LearnModuleViewModel viewModel) =>
|
||||||
|
CachedNetworkImage(
|
||||||
|
width: 25,
|
||||||
|
height: 25,
|
||||||
|
cacheKey: viewModel.getModuleImage(module),
|
||||||
|
imageUrl: viewModel.getModuleImage(module),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildTitleWrapper() => Padding(
|
Widget _buildTitleWrapper() => Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
child: _buildTitle(),
|
child: _buildTitle(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name: yimaru_app
|
name: yimaru_app
|
||||||
version: 0.1.23+25
|
version: 0.1.24+26
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user