-feat(progress): Integrat my progress section with api.
-fix(localization): Localization state issue fixed. -fix(profile_image): Fix profile image downloading issue.
This commit is contained in:
parent
fbedb63e3a
commit
7f6a5f1d34
|
|
@ -187,6 +187,14 @@
|
|||
"finish_all_practice_module": "የሞጁሉን ልምምድ ለመውሰድ የትምህርት ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_course": "የኮርሱን ልምምድ ለመውሰድ የሞጁል ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_module": "ይህን ልምምድ ለመውሰድ የቀድሞውን የሞጁል ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_course": "ይህን ለመውሰድ የቀድሞውን የኮርስ ልምምድ ያጠናቅቁ"
|
||||
"finish_all_practice_previouse_course": "ይህን ለመውሰድ የቀድሞውን የኮርስ ልምምድ ያጠናቅቁ",
|
||||
"track_journey": "የትምህርት ጉዞዎን ይከታተሉ እና በጊዜ ሂደት ያሳዩትን እድገት ይመልከቱ።",
|
||||
"learn_english": "እንግሊዝኛ ይማሩ",
|
||||
"keep_momentum":"በጣም ጥሩ ስራ! በዚሁ ብርታት ይቀጥሉ።",
|
||||
"completed_practices": "የተጠናቀቁ ልምምዶች",
|
||||
"total_practices": "ጠቅላላ ልምምዶች",
|
||||
"progress_percentage": "የእድገት መቶኛ"
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
{"loading": "Loading",
|
||||
{
|
||||
"loading": "Loading",
|
||||
"welcome_back": "Welcome back",
|
||||
"checking_user_info": "Checking user info",
|
||||
"dont_have_account": "Don't have an account?",
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
"app_settings": "App settings",
|
||||
"legal_and_information": "Legal & Information",
|
||||
"change_language": "Change language",
|
||||
"terms_and_conditions":"Terms & Conditions",
|
||||
"terms_and_conditions": "Terms & Conditions",
|
||||
"delete_account": "Delete account",
|
||||
"language_preference": "Language preference",
|
||||
"choose_your_language": "Choose your language",
|
||||
|
|
@ -117,7 +117,7 @@
|
|||
"call_support": "Call support",
|
||||
"talk_with_support": "Talk with our support team directly",
|
||||
"telegram_support": "Telegram support",
|
||||
"chat_via_telegram" :"Chat instantly via Telegram",
|
||||
"chat_via_telegram": "Chat instantly via Telegram",
|
||||
"call_our_support": "Call our support team between 9 AM - 6 PM",
|
||||
"tap_to_call": "Tap to call",
|
||||
"join_telegram": "Join Yimaru Academy on Telegram",
|
||||
|
|
@ -187,6 +187,11 @@
|
|||
"finish_all_practice_module": "Finish the lesson practices to take the Module Practice",
|
||||
"finish_all_practice_course": "Finish the Module practices to take the Course practice",
|
||||
"finish_all_practice_previouse_module": "Finish the previous Module practice to take this practice",
|
||||
"finish_all_practice_previouse_course": "Finish the previous course practice to take this"
|
||||
|
||||
}
|
||||
"finish_all_practice_previouse_course": "Finish the previous course practice to take this",
|
||||
"track_journey": "Track your learning journey and see your growth over time.",
|
||||
"learn_english": "Learn English",
|
||||
"keep_momentum":"Great job! Keep the momentum.",
|
||||
"completed_practices": "Completed Practices",
|
||||
"total_practices": "Total Practices",
|
||||
"progress_percentage": "Progress Percentage"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,18 @@ import 'package:stacked/stacked.dart';
|
|||
import 'package:yimaru_app/app/app.locator.dart';
|
||||
import 'package:yimaru_app/models/user.dart';
|
||||
import 'package:yimaru_app/services/secure_storage_service.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import 'google_auth_service.dart';
|
||||
import 'localization_service.dart';
|
||||
|
||||
class AuthenticationService with ListenableServiceMixin {
|
||||
// Dependency injection
|
||||
|
||||
final _secureService = locator<SecureStorageService>();
|
||||
|
||||
final _googleAuthService = locator<GoogleAuthService>();
|
||||
|
||||
final _localizationService = locator<LocalizationService>();
|
||||
|
||||
// User data
|
||||
|
|
@ -18,9 +23,15 @@ class AuthenticationService with ListenableServiceMixin {
|
|||
|
||||
// Initialization
|
||||
AuthenticationService() {
|
||||
listenToReactiveValues([_user, _localizationService]);
|
||||
listenToReactiveValues([_user,_state, _localizationService]);
|
||||
}
|
||||
|
||||
// Logout state
|
||||
StateObjects _state = StateObjects.none;
|
||||
|
||||
StateObjects get state => _state;
|
||||
|
||||
|
||||
// Check user logged in
|
||||
Future<bool> userLoggedIn() async {
|
||||
if (await _secureService.getString('userId') != null) {
|
||||
|
|
@ -179,13 +190,20 @@ class AuthenticationService with ListenableServiceMixin {
|
|||
|
||||
// Logout
|
||||
Future<void> logout() async {
|
||||
_state = StateObjects.logout;
|
||||
notifyListeners();
|
||||
|
||||
bool firstTimeInstall = await isFirstTimeInstall();
|
||||
String language = await _localizationService.selectedLanguage['code'];
|
||||
|
||||
_user = null;
|
||||
await _secureService.clear();
|
||||
await _googleAuthService.logout();
|
||||
await setFirstTimeInstall(firstTimeInstall);
|
||||
await _secureService.setString('language', language);
|
||||
|
||||
|
||||
_state = StateObjects.none;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ class GoogleAuthService with ListenableServiceMixin {
|
|||
Future<void> logout() async {
|
||||
await _signIn.signOut();
|
||||
_googleUser = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Google authentication
|
||||
|
|
@ -30,10 +29,7 @@ class GoogleAuthService with ListenableServiceMixin {
|
|||
|
||||
_googleUser ??=
|
||||
await _signIn.authenticate(scopeHint: ['email', 'profile']);
|
||||
print('GOOGLE AUTH');
|
||||
print(_googleUser?.email);
|
||||
print(_googleUser?.displayName);
|
||||
print(_googleUser?.authentication.idToken);
|
||||
|
||||
});
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -5,39 +5,53 @@ import 'package:path/path.dart';
|
|||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
import '../ui/common/app_constants.dart';
|
||||
import '../models/refresh_object.dart';
|
||||
import '../ui/common/enmus.dart';
|
||||
import 'api_service.dart';
|
||||
import 'dio_service.dart';
|
||||
|
||||
class ImageDownloaderService {
|
||||
// Dependency injection
|
||||
final _service = locator<DioService>();
|
||||
|
||||
// Image downloader
|
||||
Future<String> downloader(String? networkImage) async {
|
||||
late File image;
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
late String profileImage;
|
||||
final Dio _dio = Dio();
|
||||
|
||||
// Image downloader
|
||||
Future<String?> downloader(String? networkImage) async {
|
||||
try {
|
||||
File? image;
|
||||
|
||||
String? profileImage = networkImage;
|
||||
|
||||
final Directory appDir = await getApplicationDocumentsDirectory();
|
||||
|
||||
if (networkImage != null) {
|
||||
profileImage = networkImage.contains('https://lh3.googleusercontent.com')
|
||||
? networkImage
|
||||
: '$kBaseUrl$networkImage';
|
||||
Map<String, dynamic> data = {'reference': profileImage};
|
||||
Map<String, dynamic> response = await _apiService.refreshObject(data);
|
||||
|
||||
if (response['status'] == ResponseStatus.success) {
|
||||
RefreshObject object = response['data'] as RefreshObject;
|
||||
|
||||
profileImage = object.url;
|
||||
}
|
||||
|
||||
final Response profileImageResponse = await _service.dio.get(
|
||||
if (profileImage != null) {
|
||||
final Response profileImageResponse = await _dio.get<List<int>>(
|
||||
profileImage,
|
||||
options: Options(
|
||||
followRedirects: false,
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
);
|
||||
final imageName = basename(networkImage ?? '');
|
||||
final localImagePath = join(appDir.path, imageName);
|
||||
final localImagePath = join(appDir.path, 'profile.jpg');
|
||||
image = File(localImagePath);
|
||||
image.writeAsBytes(profileImageResponse.data);
|
||||
|
||||
return image.path;
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/course_progress.dart';
|
||||
import 'package:yimaru_app/models/module_progress.dart';
|
||||
import 'package:yimaru_app/models/refresh_object.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
|
|
@ -17,9 +19,25 @@ class LearnService with ListenableServiceMixin {
|
|||
|
||||
// Initialization
|
||||
learnService() {
|
||||
listenToReactiveValues([_programs, _courses, _lessons, _modules]);
|
||||
listenToReactiveValues(
|
||||
[_programs, _courses, _lessons, _modules, _totalCount]);
|
||||
}
|
||||
|
||||
// Completed count
|
||||
int _completedCount = 0;
|
||||
|
||||
int get completedCount => _completedCount;
|
||||
|
||||
// Total count
|
||||
int _totalCount = 0;
|
||||
|
||||
int get totalCount => _totalCount;
|
||||
|
||||
// Total progress
|
||||
int _totalProgress = 0;
|
||||
|
||||
int get totalProgress => _totalProgress;
|
||||
|
||||
// Learn program
|
||||
List<LearnProgram> _programs = [];
|
||||
|
||||
|
|
@ -40,10 +58,7 @@ class LearnService with ListenableServiceMixin {
|
|||
|
||||
List<LearnLesson> get lessons => _lessons;
|
||||
|
||||
// Learn progress
|
||||
final List<ProgressSummary> _summaries = [];
|
||||
|
||||
List<ProgressSummary> get summaries => _summaries;
|
||||
|
||||
// Learn programs
|
||||
Future<String?> refreshObject(String url) async {
|
||||
|
|
@ -87,7 +102,7 @@ class LearnService with ListenableServiceMixin {
|
|||
}
|
||||
|
||||
// Learn progress
|
||||
Future<void> getLearnProgressSummary() async {
|
||||
Future<void> getLearnProgress() async {
|
||||
final summaries = await _apiService.getProgressSummary();
|
||||
|
||||
/// PROGRAM ACCESS MAP
|
||||
|
|
@ -148,10 +163,6 @@ class LearnService with ListenableServiceMixin {
|
|||
);
|
||||
}).toList();
|
||||
|
||||
print(
|
||||
'MY SUMMARIES - COMPLETED COUNT: ${_modules.first.access?.completedCount}');
|
||||
print('PROGRESS PERCENT: ${_modules.first.access?.progressPercent}');
|
||||
|
||||
/// UPDATE LESSONS
|
||||
_lessons = _lessons.map((lesson) {
|
||||
return lesson.copyWith(
|
||||
|
|
@ -177,4 +188,30 @@ class LearnService with ListenableServiceMixin {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getProgressSummary() async {
|
||||
final summaries = await _apiService.getProgressSummary();
|
||||
|
||||
for (final ProgressSummary summary in summaries) {
|
||||
_totalCount = _totalCount + (summary.access?.totalCount ?? 0);
|
||||
_completedCount = _completedCount + (summary.access?.completedCount ?? 0);
|
||||
|
||||
for (final CourseProgress course in summary.courses ?? []) {
|
||||
_totalCount = _totalCount + (course.access?.totalCount ?? 0);
|
||||
_completedCount =
|
||||
_completedCount + (course.access?.completedCount ?? 0);
|
||||
|
||||
for (final ModuleProgress module in course.modules ?? []) {
|
||||
_totalCount = _totalCount + (module.access?.totalCount ?? 0);
|
||||
_completedCount =
|
||||
_completedCount + (module.access?.completedCount ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_totalProgress = summaries.fold(
|
||||
0, (sum, progress) => sum + (progress.access?.progressPercent ?? 0));
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ String kTelegramSupport = '@yimaruacademy2026';
|
|||
|
||||
String kTelegramSupportLink = 'https://t.me/yimaruacademy2026';
|
||||
|
||||
String kErrorUrl = 'https://yimaru.net/api/v1/payments/chapa/error';
|
||||
String kErrorUrl = 'https://api.yimaruacademy.com/payment/error';
|
||||
|
||||
String kSuccessUrl =
|
||||
'https://api.yimaruacademy.com/api/v1/payments/chapa/success';
|
||||
'https://api.yimaruacademy.com/payment/success';
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ enum PracticeReason { course, module, lesson, previousModule, previousCourse }
|
|||
// State object
|
||||
enum StateObjects {
|
||||
none,
|
||||
logout,
|
||||
courses,
|
||||
register,
|
||||
verifyOtp,
|
||||
|
|
@ -55,6 +56,7 @@ enum StateObjects {
|
|||
loginWithGoogle,
|
||||
loadLessonVideo,
|
||||
loadCourseVideo,
|
||||
progressSummary,
|
||||
requestResetCode,
|
||||
profileCompletion,
|
||||
learnSubscription,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import 'dart:ui';
|
|||
|
||||
import 'package:easy_localization/easy_localization.dart' show AssetLoader;
|
||||
|
||||
class CodegenLoader extends AssetLoader {
|
||||
class CodegenLoader extends AssetLoader{
|
||||
const CodegenLoader();
|
||||
|
||||
@override
|
||||
|
|
@ -14,7 +14,7 @@ class CodegenLoader extends AssetLoader {
|
|||
return Future.value(mapLocales[locale.toString()]);
|
||||
}
|
||||
|
||||
static const Map<String, dynamic> _am = {
|
||||
static const Map<String,dynamic> _am = {
|
||||
"loading": "በመጫን ላይ",
|
||||
"welcome_back": "እንኳን በደህና ተመለሱ",
|
||||
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
||||
|
|
@ -38,8 +38,7 @@ class CodegenLoader extends AssetLoader {
|
|||
"confirm_password": "የይለፍ ቃል ያረጋግጡ",
|
||||
"eight_character_minimum": "ቢያንስ 8 ፊደላት",
|
||||
"password_match": "የይለፍ ቃሉ ተመሳስሏል",
|
||||
"sign_up_agreement":
|
||||
"‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።",
|
||||
"sign_up_agreement": "‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።",
|
||||
"terms_of_services": "የአገልግሎት ውሎች",
|
||||
"and": "እና",
|
||||
"privacy_policy": "የግላዊነት ፖሊሲ",
|
||||
|
|
@ -138,8 +137,7 @@ class CodegenLoader extends AssetLoader {
|
|||
"call_our_support": "ከ3 ጠዋት እስከ 12 ማታ ድረስ የድጋፍ ቡድናችንን ይደውሉ",
|
||||
"tap_to_call": "ለመደወል ይንኩ",
|
||||
"join_telegram": "በቴሌግራም የይማሩ አካዳሚን ይቀላቀሉ",
|
||||
"connect_with_support_team":
|
||||
"ለፈጣን እርዳታ እና የማህበረሰብ ዝማኔዎች፣ በቴሌግራም ከድጋፍ ቡድናችን ጋር ወዲያውኑ ይገናኙ።",
|
||||
"connect_with_support_team": "ለፈጣን እርዳታ እና የማህበረሰብ ዝማኔዎች፣ በቴሌግራም ከድጋፍ ቡድናችን ጋር ወዲያውኑ ይገናኙ።",
|
||||
"open_in_telegram": "በቴሌግራም ይክፈቱ",
|
||||
"search_for": "ፈልጉት",
|
||||
"current_level": "የአሁኑ ደረጃ",
|
||||
|
|
@ -189,8 +187,7 @@ class CodegenLoader extends AssetLoader {
|
|||
"evey_one_has_strugle": "ሁሉም ሰው ችግሮች አሉት፣ የአንተን እንጀምር እንፍታ",
|
||||
"write_your_challenge": "ችግርህን ጻፍ…",
|
||||
"topic_interest": "በጣም የሚስቡህ ርዕሶች የትኞቹ ናቸው?",
|
||||
"favourite_topic":
|
||||
"የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
||||
"favourite_topic": "የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
||||
"your_interest": "ፍላጎትህን ጻፍ…",
|
||||
"want_quick_assessment": "የእንግሊዝኛ ደረጃህን ለማወቅ ፈጣን ግምገማ ትፈልጋለህ?",
|
||||
"answer_quick_questions": "የእንግሊዝኛ ችሎታህን ለመረዳት ጥቂት ፈጣን ጥያቄዎችን መልስ።",
|
||||
|
|
@ -205,11 +202,16 @@ class CodegenLoader extends AssetLoader {
|
|||
"finish_all_practice_lesson": "ይህን ልምምድ ለመውሰድ የቀድሞውን የትምህርት ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_module": "የሞጁሉን ልምምድ ለመውሰድ የትምህርት ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_course": "የኮርሱን ልምምድ ለመውሰድ የሞጁል ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_module":
|
||||
"ይህን ልምምድ ለመውሰድ የቀድሞውን የሞጁል ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_course": "ይህን ለመውሰድ የቀድሞውን የኮርስ ልምምድ ያጠናቅቁ"
|
||||
};
|
||||
static const Map<String, dynamic> _en = {
|
||||
"finish_all_practice_previouse_module": "ይህን ልምምድ ለመውሰድ የቀድሞውን የሞጁል ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_course": "ይህን ለመውሰድ የቀድሞውን የኮርስ ልምምድ ያጠናቅቁ",
|
||||
"track_journey": "የትምህርት ጉዞዎን ይከታተሉ እና በጊዜ ሂደት ያሳዩትን እድገት ይመልከቱ።",
|
||||
"learn_english": "እንግሊዝኛ ይማሩ",
|
||||
"keep_momentum": "በጣም ጥሩ ስራ! በዚሁ ብርታት ይቀጥሉ።",
|
||||
"completed_practices": "የተጠናቀቁ ልምምዶች",
|
||||
"total_practices": "ጠቅላላ ልምምዶች",
|
||||
"progress_percentage": "የእድገት መቶኛ"
|
||||
};
|
||||
static const Map<String,dynamic> _en = {
|
||||
"loading": "Loading",
|
||||
"welcome_back": "Welcome back",
|
||||
"checking_user_info": "Checking user info",
|
||||
|
|
@ -227,15 +229,13 @@ class CodegenLoader extends AssetLoader {
|
|||
"login": "Login",
|
||||
"register_with_google": "Register with Google",
|
||||
"register_with_phone": "Register with phone number",
|
||||
"enter_phone_number":
|
||||
"Enter your phone number. We will send you a confirmation code there.",
|
||||
"enter_phone_number": "Enter your phone number. We will send you a confirmation code there.",
|
||||
"login_with_email": "Login with email",
|
||||
"create_password": "Create password",
|
||||
"confirm_password": "Confirm password",
|
||||
"eight_character_minimum": "8 characters minimum",
|
||||
"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",
|
||||
"and": "and",
|
||||
"privacy_policy": "Privacy Policy",
|
||||
|
|
@ -246,8 +246,7 @@ class CodegenLoader extends AssetLoader {
|
|||
"code_sent_to_email": "Code sent to your email",
|
||||
"resend_code_in": "Resend code in",
|
||||
"reset_password": "Reset Password",
|
||||
"enter_email_reset_code":
|
||||
"Enter your email. We will send you a reset code.",
|
||||
"enter_email_reset_code": "Enter your email. We will send you a reset code.",
|
||||
"please_wait": "Please wait",
|
||||
"reset_code_sent": "Reset code sent successfully",
|
||||
"reset_code": "Reset code",
|
||||
|
|
@ -288,8 +287,7 @@ class CodegenLoader extends AssetLoader {
|
|||
"cancel": "Cancel",
|
||||
"you_are_speaking": "You're speaking",
|
||||
"practice_completed": "Practice completed!",
|
||||
"great_improvement":
|
||||
"You sound more confident this time, great improvement",
|
||||
"great_improvement": "You sound more confident this time, great improvement",
|
||||
"practice_again": "Practice again",
|
||||
"conversation_review": "Conversation review",
|
||||
"result": "Result",
|
||||
|
|
@ -336,8 +334,7 @@ class CodegenLoader extends AssetLoader {
|
|||
"call_our_support": "Call our support team between 9 AM - 6 PM",
|
||||
"tap_to_call": "Tap to call",
|
||||
"join_telegram": "Join Yimaru Academy on Telegram",
|
||||
"connect_with_support_team":
|
||||
"Connect with our support team instantly on Telegram for quick assistance and community updates",
|
||||
"connect_with_support_team": "Connect with our support team instantly on Telegram for quick assistance and community updates",
|
||||
"open_in_telegram": "Open in Telegram",
|
||||
"search_for": "Search for",
|
||||
"current_level": "Current Level",
|
||||
|
|
@ -345,22 +342,18 @@ class CodegenLoader extends AssetLoader {
|
|||
"no_practice_available": "No practice available!",
|
||||
"begin_module_practice": "Begin Module Practice",
|
||||
"lets_practice_lesson": "Let’s Practice",
|
||||
"lets_quickly_review":
|
||||
"Let’s quickly review what you’ve learned in this module!",
|
||||
"lets_quickly_review": "Let’s quickly review what you’ve learned in this module!",
|
||||
"lets_practice_module": "Let's practice what you just learnt!",
|
||||
"ask_you_few_actions":
|
||||
"I’ll ask you a few questions, and you can respond naturally.",
|
||||
"ask_you_few_actions": "I’ll ask you a few questions, and you can respond naturally.",
|
||||
"begin_level_practice": "Begin Level Practice",
|
||||
"lets_practice_course": "Let’s Practice Course",
|
||||
"lets_quick_review":
|
||||
"Let’s quickly review what you’ve learned in this level!",
|
||||
"lets_quick_review": "Let’s quickly review what you’ve learned in this level!",
|
||||
"speaking": "is speaking...",
|
||||
"you_have_finished_practice": "You have finished your practice",
|
||||
"view_results": "View My Results",
|
||||
"sample_answer": "Sample Answer",
|
||||
"your_answer": "Your Answer",
|
||||
"sound_confident":
|
||||
"You sound more confident this time - great improvement!",
|
||||
"sound_confident": "You sound more confident this time - great improvement!",
|
||||
"you_have_completed": "Yay, you’ve completed",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
|
|
@ -371,20 +364,15 @@ class CodegenLoader extends AssetLoader {
|
|||
"phone_must_start_with": "Phone number must start with 251",
|
||||
"phone_must_be": "Phone number must be 12 digits",
|
||||
"what_should_we_call_you": "What should we call you?",
|
||||
"name_for_personalization":
|
||||
"We’ll use your name to personalize your learning journey.",
|
||||
"name_for_personalization": "We’ll use your name to personalize your learning journey.",
|
||||
"choose_your_gender": "Choose your gender?",
|
||||
"gender_for_personalization":
|
||||
"We’ll personalize your learning experience based on your gender.",
|
||||
"gender_for_personalization": "We’ll personalize your learning experience based on your gender.",
|
||||
"age_range": "Which age range are you in?",
|
||||
"age_for_personalization":
|
||||
"We’ll personalize your learning experience based on your age.",
|
||||
"age_for_personalization": "We’ll personalize your learning experience based on your age.",
|
||||
"educational_background": "What’s your current educational level?",
|
||||
"education_for_personalization":
|
||||
"This helps us tailor your lessons to your experience.",
|
||||
"education_for_personalization": "This helps us tailor your lessons to your experience.",
|
||||
"your_occupation": "What’s your occupation?",
|
||||
"occupation_for_personalization":
|
||||
"We’ll personalize your learning experience based on your occupation.",
|
||||
"occupation_for_personalization": "We’ll personalize your learning experience based on your occupation.",
|
||||
"location": "Where are you from?",
|
||||
"select_country_region": "Select your country and region from the dropdown",
|
||||
"select_country": "Select country",
|
||||
|
|
@ -396,13 +384,10 @@ class CodegenLoader extends AssetLoader {
|
|||
"evey_one_has_strugle": "Everyone has struggles, let’s start fixing yours",
|
||||
"write_your_challenge": "Write your challenge…",
|
||||
"topic_interest": "Which topics interest you most?",
|
||||
"favourite_topic":
|
||||
"Your favorite topics help us create fun, relatable lessons.",
|
||||
"favourite_topic": "Your favorite topics help us create fun, relatable lessons.",
|
||||
"your_interest": "Write your interest…",
|
||||
"want_quick_assessment":
|
||||
"Want a quick assessment to know your English level?",
|
||||
"answer_quick_questions":
|
||||
"Answer a few quick questions to help us understand your English proficiency.",
|
||||
"want_quick_assessment": "Want a quick assessment to know your English level?",
|
||||
"answer_quick_questions": "Answer a few quick questions to help us understand your English proficiency.",
|
||||
"skip": "Skip",
|
||||
"finish_level": "Finish Level",
|
||||
"likely_speaker": "You’re likely speaker of",
|
||||
|
|
@ -411,19 +396,17 @@ class CodegenLoader extends AssetLoader {
|
|||
"welcome_abroad": "Welcome aboard",
|
||||
"ready_to_explore": "You’re ready to explore your personalized lessons.",
|
||||
"finish": "Finish",
|
||||
"finish_all_practice_lesson":
|
||||
"Finish the previous lesson practice to take this practice",
|
||||
"finish_all_practice_module":
|
||||
"Finish the lesson practices to take the Module Practice",
|
||||
"finish_all_practice_course":
|
||||
"Finish the Module practices to take the Course practice",
|
||||
"finish_all_practice_previouse_module":
|
||||
"Finish the previous Module practice to take this practice",
|
||||
"finish_all_practice_previouse_course":
|
||||
"Finish the previous course practice to take this"
|
||||
};
|
||||
static const Map<String, Map<String, dynamic>> mapLocales = {
|
||||
"am": _am,
|
||||
"en": _en
|
||||
};
|
||||
"finish_all_practice_lesson": "Finish the previous lesson practice to take this practice",
|
||||
"finish_all_practice_module": "Finish the lesson practices to take the Module Practice",
|
||||
"finish_all_practice_course": "Finish the Module practices to take the Course practice",
|
||||
"finish_all_practice_previouse_module": "Finish the previous Module practice to take this practice",
|
||||
"finish_all_practice_previouse_course": "Finish the previous course practice to take this",
|
||||
"track_journey": "Track your learning journey and see your growth over time.",
|
||||
"learn_english": "Learn English",
|
||||
"keep_momentum": "Great job! Keep the momentum.",
|
||||
"completed_practices": "Completed Practices",
|
||||
"total_practices": "Total Practices",
|
||||
"progress_percentage": "Progress Percentage"
|
||||
};
|
||||
static const Map<String, Map<String,dynamic>> mapLocales = {"am": _am, "en": _en};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,8 +161,7 @@ abstract class LocaleKeys {
|
|||
static const educational_background = 'educational_background';
|
||||
static const education_for_personalization = 'education_for_personalization';
|
||||
static const your_occupation = 'your_occupation';
|
||||
static const occupation_for_personalization =
|
||||
'occupation_for_personalization';
|
||||
static const occupation_for_personalization = 'occupation_for_personalization';
|
||||
static const location = 'location';
|
||||
static const select_country_region = 'select_country_region';
|
||||
static const select_country = 'select_country';
|
||||
|
|
@ -189,8 +188,13 @@ abstract class LocaleKeys {
|
|||
static const finish_all_practice_lesson = 'finish_all_practice_lesson';
|
||||
static const finish_all_practice_module = 'finish_all_practice_module';
|
||||
static const finish_all_practice_course = 'finish_all_practice_course';
|
||||
static const finish_all_practice_previouse_module =
|
||||
'finish_all_practice_previouse_module';
|
||||
static const finish_all_practice_previouse_course =
|
||||
'finish_all_practice_previouse_course';
|
||||
static const finish_all_practice_previouse_module = 'finish_all_practice_previouse_module';
|
||||
static const finish_all_practice_previouse_course = 'finish_all_practice_previouse_course';
|
||||
static const track_journey = 'track_journey';
|
||||
static const learn_english = 'learn_english';
|
||||
static const keep_momentum = 'keep_momentum';
|
||||
static const completed_practices = 'completed_practices';
|
||||
static const total_practices = 'total_practices';
|
||||
static const progress_percentage = 'progress_percentage';
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,18 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
||||
import 'package:yimaru_app/ui/views/profile/profile_view.dart';
|
||||
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
||||
|
||||
import '../../widgets/coming_soon.dart';
|
||||
import 'home_viewmodel.dart';
|
||||
|
||||
class HomeView extends StackedView<HomeViewModel> {
|
||||
const HomeView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onViewModelReady(HomeViewModel viewModel) async {
|
||||
await viewModel.inAppUpdate();
|
||||
|
|
@ -23,7 +26,13 @@ class HomeView extends StackedView<HomeViewModel> {
|
|||
@override
|
||||
Widget builder(
|
||||
BuildContext context, HomeViewModel viewModel, Widget? child) =>
|
||||
_buildScaffold(viewModel);
|
||||
KeyedSubtree(
|
||||
key: ValueKey(context.locale.languageCode),
|
||||
child: _buildScaffoldStack(viewModel));
|
||||
|
||||
Widget _buildScaffoldStack(HomeViewModel viewModel) => Stack(
|
||||
children: [_buildScaffold(viewModel), _buildLogoutState(viewModel)],
|
||||
);
|
||||
|
||||
Widget _buildScaffold(HomeViewModel viewModel) => Scaffold(
|
||||
body: getViewForIndex(viewModel.currentPage),
|
||||
|
|
@ -77,4 +86,9 @@ class HomeView extends StackedView<HomeViewModel> {
|
|||
return const ProfileView();
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildLogoutState(HomeViewModel viewModel) =>
|
||||
viewModel.state == StateObjects.logout
|
||||
? const PageLoadingIndicator()
|
||||
: Container();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:yimaru_app/services/status_checker_service.dart';
|
|||
import 'package:yimaru_app/ui/common/app_strings.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../../../services/authentication_service.dart';
|
||||
import '../../../services/in_app_update_service.dart';
|
||||
|
|
@ -12,8 +13,13 @@ import '../../../services/in_app_update_service.dart';
|
|||
class HomeViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
final _bottomSheetService = locator<BottomSheetService>();
|
||||
|
||||
final _inAppUpdateService = locator<InAppUpdateService>();
|
||||
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
@override
|
||||
|
|
@ -25,6 +31,11 @@ class HomeViewModel extends ReactiveViewModel {
|
|||
|
||||
User? get user => _user;
|
||||
|
||||
// Logout state
|
||||
StateObjects get _state => _authenticationService.state;
|
||||
|
||||
StateObjects get state => _state;
|
||||
|
||||
// Bottom navigation
|
||||
int _currentPage = 0;
|
||||
|
||||
|
|
@ -44,6 +55,8 @@ class HomeViewModel extends ReactiveViewModel {
|
|||
rebuildUi();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Remote api calls
|
||||
|
||||
// In-app update
|
||||
|
|
|
|||
|
|
@ -29,9 +29,12 @@ class LanguageViewModel extends ReactiveViewModel {
|
|||
|
||||
Future<void> setSelectedLanguage(
|
||||
{required BuildContext context,
|
||||
required Map<String, dynamic> title}) async =>
|
||||
required Map<String, dynamic> title}) async {
|
||||
await _localizationService.setSelectedLanguage(
|
||||
context: context, title: title);
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
|
|
|||
|
|
@ -26,12 +26,12 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
required LearnLesson lesson,
|
||||
required BuildContext context,
|
||||
required LearnLessonViewModel viewModel}) async {
|
||||
if (lesson.access?.isAccessible ?? false) {
|
||||
/* if (lesson.access?.isAccessible ?? false) {
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
} else {
|
||||
await _showSheet(context: context, viewModel: viewModel);
|
||||
}
|
||||
/* if (index > 1) {
|
||||
}*/
|
||||
if (index > 1) {
|
||||
if (viewModel.user?.subscriptionStatus?.toLowerCase() == 'subscribed') {
|
||||
if (lesson.access?.isAccessible ?? false) {
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
|
|
@ -47,7 +47,7 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
} else {
|
||||
await _showSheet(context: context, viewModel: viewModel);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showSheet(
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
Future<void> _onPractice(
|
||||
{required LearnLesson lesson,
|
||||
required LearnLessonDetailViewModel viewModel}) async {
|
||||
await viewModel.pause();
|
||||
/* await viewModel.pause();
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
|
||||
/* if (index > 1) {
|
||||
*/
|
||||
if (index > 1) {
|
||||
if (viewModel.user?.subscriptionStatus?.toLowerCase() == 'subscribed') {
|
||||
await viewModel.pause();
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
|
|
@ -45,7 +45,7 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
} else {
|
||||
await viewModel.pause();
|
||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
|||
Future<void> _completeLearnPractices() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _apiService.completeLearnPractice(_practices.first.id ?? 0);
|
||||
await _learnService.getLearnProgressSummary();
|
||||
await _learnService.getLearnProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -203,10 +203,10 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
|
||||
void setSelectedCountry(FieldOption? value) {
|
||||
_selectedCountry = value;
|
||||
if (value?.label?.toLowerCase().trim() == 'ethiopia') {
|
||||
if (value?.code?.toLowerCase().trim() == 'et') {
|
||||
_dropdownRegion = true;
|
||||
_selectedRegion = _regions
|
||||
.firstWhere((e) => e.label?.toLowerCase().trim() == 'addis ababa');
|
||||
.firstWhere((e) => e.code?.toLowerCase().trim().contains('addis_ababa') ?? false);
|
||||
} else {
|
||||
_dropdownRegion = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ class ProfileView extends StackedView<ProfileViewModel> {
|
|||
|
||||
List<Widget> _buildSettingsChildren(ProfileViewModel viewModel) => [
|
||||
// _buildDownloadsCard(viewModel),
|
||||
// _buildProgressCard(viewModel),
|
||||
_buildProgressCard(viewModel),
|
||||
_buildAccountCard(viewModel),
|
||||
_buildSupportCard(viewModel)
|
||||
];
|
||||
|
|
|
|||
|
|
@ -68,10 +68,10 @@ class ProfileViewModel extends ReactiveViewModel {
|
|||
DialogResponse? response = await _dialogService.showDialog(
|
||||
barrierDismissible: true,
|
||||
cancelTitleColor: kcDarkGrey,
|
||||
buttonTitleColor: kcPrimaryColor,
|
||||
title: LocaleKeys.logout.tr(),
|
||||
cancelTitle: LocaleKeys.no.tr(),
|
||||
buttonTitle: LocaleKeys.yes.tr(),
|
||||
buttonTitleColor: kcPrimaryColor,
|
||||
description: LocaleKeys.want_to_quit.tr(),
|
||||
);
|
||||
return response?.confirmed;
|
||||
|
|
@ -119,7 +119,6 @@ class ProfileViewModel extends ReactiveViewModel {
|
|||
}
|
||||
|
||||
Future<void> _logout() async {
|
||||
await _googleAuthService.logout();
|
||||
await _authenticationService.logout();
|
||||
await navigateToLogin();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
}
|
||||
|
||||
void _onModelReady(ProfileDetailViewModel viewModel) {
|
||||
print('MY RESPONSE: ${viewModel.user?.region}');
|
||||
phoneNumberController.text = '251900000000';
|
||||
emailController.text = viewModel.user?.email ?? '';
|
||||
lastNameController.text = viewModel.user?.lastName ?? '';
|
||||
|
|
@ -106,6 +107,11 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
viewModel.setSelectedCountry(viewModel.countries
|
||||
.where((e) => (e.code ?? '') == viewModel.user?.country)
|
||||
.first);
|
||||
if (viewModel.user?.country?.toLowerCase() == 'et') {
|
||||
viewModel.setEthiopianRegion(viewModel.user?.region ?? '');
|
||||
} else {
|
||||
regionController.text = viewModel.user?.region ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -665,6 +671,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
Icons.search,
|
||||
color: kcPrimaryColor,
|
||||
);
|
||||
|
||||
Widget _buildLowerColumn(ProfileDetailViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildLowerColumnChildren(viewModel),
|
||||
|
|
|
|||
|
|
@ -134,12 +134,20 @@ class ProfileDetailViewModel extends ReactiveViewModel
|
|||
|
||||
List<FieldOption> get countries => _countries;
|
||||
|
||||
void setEthiopianRegion(String region) {
|
||||
_dropdownRegion = true;
|
||||
_selectedRegion =
|
||||
_regions.firstWhere((r) => r.code?.toLowerCase() == region.toLowerCase());
|
||||
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
void setSelectedCountry(FieldOption? value) {
|
||||
_selectedCountry = value;
|
||||
if (value?.label?.toLowerCase().trim() == 'ethiopia') {
|
||||
if (value?.code?.toLowerCase().trim().contains('et') ?? false) {
|
||||
_dropdownRegion = true;
|
||||
_selectedRegion = _regions
|
||||
.firstWhere((e) => e.label?.toLowerCase().trim() == 'addis ababa');
|
||||
_selectedRegion = _regions.firstWhere(
|
||||
(e) => e.code?.toLowerCase().trim().contains('addis_ababa') ?? false);
|
||||
} else {
|
||||
_dropdownRegion = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
|
|
@ -11,6 +13,12 @@ import 'progress_viewmodel.dart';
|
|||
class ProgressView extends StackedView<ProgressViewModel> {
|
||||
const ProgressView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onViewModelReady(ProgressViewModel viewModel) async {
|
||||
await viewModel.getProgressSummary();
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
ProgressViewModel viewModelBuilder(BuildContext context) =>
|
||||
ProgressViewModel();
|
||||
|
|
@ -54,9 +62,9 @@ class ProgressView extends StackedView<ProgressViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildAppbar(ProgressViewModel viewModel) => SmallAppBar(
|
||||
title: 'My Progress',
|
||||
showBackButton: true,
|
||||
onPop: viewModel.pop,
|
||||
title: LocaleKeys.my_progress.tr(),
|
||||
);
|
||||
|
||||
Widget _buildContentScrollViewWrapper(ProgressViewModel viewModel) =>
|
||||
|
|
@ -81,12 +89,12 @@ class ProgressView extends StackedView<ProgressViewModel> {
|
|||
verticalSpaceMedium,
|
||||
_buildLearningProgressCard(),
|
||||
verticalSpaceMedium,
|
||||
_buildCourseProgressSection()
|
||||
// _buildCourseProgressSection()
|
||||
];
|
||||
|
||||
Widget _buildText() => const Text(
|
||||
'Track your learning journey and see your growth over time.',
|
||||
style: TextStyle(color: kcDarkGrey),
|
||||
Widget _buildText() => Text(
|
||||
LocaleKeys.track_journey.tr(),
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
Widget _buildLearningProgressCard() => const LearningProgressCard();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,40 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/services/learn_service.dart';
|
||||
import 'package:yimaru_app/services/status_checker_service.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/progress_summary.dart';
|
||||
|
||||
class ProgressViewModel extends BaseViewModel {
|
||||
class ProgressViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
final _learnService = locator<LearnService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
final _statusCheckerService = locator<StatusCheckerService>();
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
||||
|
||||
|
||||
// Total practice count
|
||||
int get _totalCount => _learnService.totalCount;
|
||||
|
||||
int get totalCount => _totalCount;
|
||||
|
||||
// Completed practice count
|
||||
int get _completedCount => _learnService.completedCount;
|
||||
|
||||
int get completedCount => _completedCount;
|
||||
|
||||
// Total progress
|
||||
int get _totalProgress => _learnService.totalProgress;
|
||||
|
||||
int get totalProgress => _totalProgress;
|
||||
|
||||
|
||||
// Courses
|
||||
final List<Map<String, dynamic>> _courses = [
|
||||
{
|
||||
|
|
@ -21,4 +49,17 @@ class ProgressViewModel extends BaseViewModel {
|
|||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
// Remote api
|
||||
|
||||
// Learning progress
|
||||
|
||||
Future<void> getProgressSummary() async => runBusyFuture(_getProgressSummary(),
|
||||
busyObject: StateObjects.progressSummary);
|
||||
|
||||
Future<void> _getProgressSummary() async {
|
||||
if (await _statusCheckerService.checkConnection()) {
|
||||
await _learnService.getProgressSummary();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,14 +122,16 @@ class StartupViewModel extends ReactiveViewModel {
|
|||
|
||||
await _authenticationService.saveUserData(user);
|
||||
|
||||
String image =
|
||||
String? image =
|
||||
await _imageDownloaderService.downloader(user.profilePicture);
|
||||
|
||||
if (image != null) {
|
||||
await _authenticationService.saveProfilePicture(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remote api call
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
|||
import 'package:yimaru_app/ui/views/learn_module/learn_module_viewmodel.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_container_shader.dart';
|
||||
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/ui_helpers.dart';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/views/progress/progress_viewmodel.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_column.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
|
|
@ -6,45 +11,46 @@ import '../common/ui_helpers.dart';
|
|||
import 'custom_elevated_button.dart';
|
||||
import 'custom_linear_progress_indicator.dart';
|
||||
|
||||
class LearningProgressCard extends StatelessWidget {
|
||||
class LearningProgressCard extends ViewModelWidget<ProgressViewModel> {
|
||||
final GestureTapCallback? onTap;
|
||||
|
||||
const LearningProgressCard({super.key, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainerWrapper();
|
||||
Widget build(BuildContext context, ProgressViewModel viewModel) =>
|
||||
_buildContainerWrapper(viewModel);
|
||||
|
||||
Widget _buildContainerWrapper() => GestureDetector(
|
||||
Widget _buildContainerWrapper(ProgressViewModel viewModel) => GestureDetector(
|
||||
onTap: onTap,
|
||||
child: _buildContainer(),
|
||||
child: _buildContainer(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
Widget _buildContainer(ProgressViewModel viewModel) => Container(
|
||||
height: 320,
|
||||
padding: const EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: kcPrimaryColor.withOpacity(0.1),
|
||||
),
|
||||
child: _buildColumn(),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn() => Column(
|
||||
Widget _buildColumn(ProgressViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildColumnChildren(),
|
||||
children: _buildColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildColumnChildren() => [
|
||||
List<Widget> _buildColumnChildren(ProgressViewModel viewModel) => [
|
||||
_buildIcon(),
|
||||
verticalSpaceSmall,
|
||||
_buildTitle(),
|
||||
verticalSpaceTiny,
|
||||
_buildSubtitle(),
|
||||
verticalSpaceSmall,
|
||||
_buildProgressIndicator(),
|
||||
_buildProgressIndicator(viewModel),
|
||||
verticalSpaceSmall,
|
||||
_buildLearningStatus(),
|
||||
_buildLearningStatus(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildActionButton(),
|
||||
];
|
||||
|
|
@ -56,47 +62,64 @@ class LearningProgressCard extends StatelessWidget {
|
|||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'Learn English',
|
||||
LocaleKeys.learn_english.tr(),
|
||||
style: style16DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => const Text(
|
||||
'Great job! Keep the momentum.',
|
||||
Widget _buildSubtitle() => Text(
|
||||
LocaleKeys.keep_momentum.tr(),
|
||||
maxLines: 2,
|
||||
style: TextStyle(color: kcMediumGrey),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
Widget _buildProgressIndicator() => const CustomLinearProgressIndicator(
|
||||
progress: 0,
|
||||
Widget _buildProgressIndicator(ProgressViewModel viewModel) =>
|
||||
CustomLinearProgressIndicator(
|
||||
activeColor: kcPrimaryColor,
|
||||
backgroundColor: kcVeryLightGrey,
|
||||
progress: viewModel.busy(StateObjects.progressSummary)
|
||||
? 0
|
||||
: viewModel.totalProgress / 100,
|
||||
);
|
||||
|
||||
Widget _buildLearningStatus() => Row(
|
||||
children: _buildLearningStatusChildren(),
|
||||
Widget _buildLearningStatus(ProgressViewModel viewModel) => Row(
|
||||
children: _buildLearningStatusChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildLearningStatusChildren() => [
|
||||
_buildWatchedVideos(),
|
||||
List<Widget> _buildLearningStatusChildren(ProgressViewModel viewModel) => [
|
||||
_buildCompletedPractices(viewModel),
|
||||
horizontalSpaceSmall,
|
||||
_buildCompletedPractices(),
|
||||
_buildTotalPractices(viewModel),
|
||||
horizontalSpaceSmall,
|
||||
_buildTakenQuizzes()
|
||||
_buildProgressPercentage(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildWatchedVideos() => const Expanded(
|
||||
child: CustomColumn(title: '0', subtitle: 'Videos Watched'));
|
||||
Widget _buildCompletedPractices() => const Expanded(
|
||||
child: CustomColumn(title: '0', subtitle: 'Practices Completed'));
|
||||
Widget _buildTakenQuizzes() => const Expanded(
|
||||
child: CustomColumn(title: '0', subtitle: 'Quizzes Taken'));
|
||||
Widget _buildCompletedPractices(ProgressViewModel viewModel) => Expanded(
|
||||
child: CustomColumn(
|
||||
title: viewModel.busy(StateObjects.progressSummary)
|
||||
? '-'
|
||||
: viewModel.completedCount.toString(),
|
||||
subtitle: LocaleKeys.completed_practices.tr()));
|
||||
|
||||
Widget _buildActionButton() => const CustomElevatedButton(
|
||||
Widget _buildTotalPractices(ProgressViewModel viewModel) => Expanded(
|
||||
child: CustomColumn(
|
||||
title: viewModel.busy(StateObjects.progressSummary)
|
||||
? '-'
|
||||
: viewModel.totalCount.toString(),
|
||||
subtitle: LocaleKeys.total_practices.tr()));
|
||||
|
||||
Widget _buildProgressPercentage(ProgressViewModel viewModel) => Expanded(
|
||||
child: CustomColumn(
|
||||
title: viewModel.busy(StateObjects.progressSummary)
|
||||
? '-'
|
||||
: '${viewModel.totalProgress.toString()}%',
|
||||
subtitle: LocaleKeys.progress_percentage.tr()));
|
||||
|
||||
Widget _buildActionButton() => CustomElevatedButton(
|
||||
height: 15,
|
||||
width: 200,
|
||||
borderRadius: 12,
|
||||
text: 'Continue Learning',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text: LocaleKeys.continue_learning.tr(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user